/* * R : A Computer Language for Statistical Data Analysis * file pager.c * Copyright (C) 1998--2002 Guido Masarotto and Brian Ripley * Copyright (C) 2004--8 The R Foundation * Copyright (C) 2004--2023 The R Core Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, a copy is available at * https://www.R-project.org/Licenses/ */ #ifdef HAVE_CONFIG_H #include #endif #include "win-nls.h" #ifdef Win32 #define USE_MDI 1 #endif #define WIN32_LEAN_AND_MEAN 1 #include #include "graphapp/ga.h" #ifdef USE_MDI #include "graphapp/stdimg.h" #endif #include "console.h" #include "consolestructs.h" #include "rui.h" #include /* for CharacterMode */ #define CE_UTF8 1 extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n); #define PAGERMAXKEPT 12 #define PAGERMAXTITLE 128 static int pagerActualKept = 0, pagerActualShown; static pager pagerInstance = NULL; static menubar pagerBar = NULL; static xbuf pagerXbuf[PAGERMAXKEPT]; static char pagerTitles[PAGERMAXKEPT][PAGERMAXTITLE+8]; static menuitem pagerMenus[PAGERMAXKEPT]; static int pagerRow[PAGERMAXKEPT]; static void pagerupdateview(void); void menueditoropen(control m); void menueditornew(control m); /* from console.c */ extern int pagerMultiple, haveusedapager; /* To be fixed: during creation, memory is allocated two times (faster for small files but a big waste otherwise) */ static xbuf file2xbuf(const char *name, int enc, int del) { HANDLE f; DWORD rr, vv; char *p; xlong dim, cnt; xint ms; xbuf xb; wchar_t *wp, *q; if (enc == CE_UTF8) { size_t len = Rf_utf8towcs(NULL, name, 0); if (len == (size_t)-1) { R_ShowMessage(G_("Error opening file")); return NULL; } wchar_t *wfn = (wchar_t *)malloc((len + 1)*sizeof(wchar_t)); if (!wfn) { R_ShowMessage(G_("Insufficient memory to display file in internal pager")); return NULL; } Rf_utf8towcs(wfn, name, len + 1); f = CreateFileW(wfn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); free(wfn); } else f = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (f == INVALID_HANDLE_VALUE) { R_ShowMessage(G_("Error opening file")); return NULL; } vv = GetFileSize(f, NULL); p = (char *) malloc((size_t) vv + 1); if (!p) { CloseHandle(f); R_ShowMessage(G_("Insufficient memory to display file in internal pager")); return NULL; } ReadFile(f, p, vv, &rr, NULL); CloseHandle(f); if (del) DeleteFile(name); p[rr] = '\0'; cnt = mbstowcs(NULL, p, 0); wp = (wchar_t *) malloc((cnt+1) * sizeof(wchar_t)); mbstowcs(wp, p, cnt+1); for (q = wp, ms = 1, dim = cnt; *q; q++) { if (*q == '\t') dim += TABSIZE; else if (*q == '\n') { dim++; ms++; } } free(p); if ((xb = newxbuf(dim + 1, ms + 1, 1))) for (q = wp, ms = 0; *q; q++) { if (*q == L'\r') continue; if (*q == L'\n') { ms++; xbufaddxc(xb, *q); /* next line interprets underlining in help files */ if (q[1] == L'_' && q[2] == L'\b') xb->user[ms] = -2; } else xbufaddxc(xb, *q); } free(wp); return xb; } static void delpager(control m) { int i; ConsoleData p = getdata(m); if (!pagerMultiple) { for (i = 0; i < pagerActualKept; i++) xbufdel(pagerXbuf[i]); pagerActualKept = 0; } else xbufdel(p->lbuf); freeConsoleData(getdata(m)); } void pagerbclose(control m) { show(RConsole); if (!pagerMultiple) { hide(pagerInstance); del(pagerInstance); pagerInstance = pagerBar = NULL; } else { hide(m); del(m); } } static void pagerclose(control m) { pagerbclose(getdata(m)); } static void pagerprint(control m) { consoleprint(getdata(m)); } static void pagersavefile(control m) { consolesavefile(getdata(m), 1); } static void pagercopy(control m) { control c = getdata(m); if (consolecancopy(c)) consolecopy(c); else R_ShowMessage(G_("No selection")); } static void pagerpaste(control m) { control c = getdata(m); if (CharacterMode != RGui) { R_ShowMessage(G_("No RGui console to paste to")); return; } if (!consolecancopy(c)) { R_ShowMessage(G_("No selection")); return; } else { consolecopy(c); } if (consolecanpaste(RConsole)) { consolepaste(RConsole); show(RConsole); } } static void pagerpastecmds(control m) { control c = getdata(m); if (CharacterMode != RGui) { R_ShowMessage(G_("No RGui console to paste to")); return; } if (!consolecancopy(c)) { R_ShowMessage(G_("No selection")); return; } else { consolecopy(c); } if (consolecanpaste(RConsole)) { consolepastecmds(RConsole); show(RConsole); } } static void pagerselectall(control m) { control c = getdata(m); consoleselectall(c); } static void pagerstayontop(control m) { control c = getdata(m); BringToTop(c, 2); } static void pagerconsole(control m) { show(RConsole); } static void pagerchangeview(control m) { ConsoleData p = getdata(pagerInstance); int i = getvalue(m); if (i >= pagerActualKept) return; uncheck(pagerMenus[pagerActualShown]); /* save position of middle line of pager display */ pagerRow[pagerActualShown] = FV + ROWS/2; pagerActualShown = i; check(pagerMenus[i]); pagerupdateview(); } static void pagerupdateview(void) { control c = pagerInstance; ConsoleData p = getdata(c); settext(pagerInstance, &pagerTitles[pagerActualShown][4]); p->lbuf = pagerXbuf[pagerActualShown]; setfirstvisible(c, pagerRow[pagerActualShown] - ROWS/2); setfirstcol(c, 0); show(c); } static int pageraddfile(const char *wtitle, const char *filename, int enc, int deleteonexit) { ConsoleData p = getdata(pagerInstance); int i; xbuf nxbuf = file2xbuf(filename, enc, deleteonexit); if (!nxbuf) { /* R_ShowMessage("File not found or memory insufficient"); */ return 0; } if (pagerActualKept == PAGERMAXKEPT) { pagerActualKept -= 1; xbufdel(pagerXbuf[pagerActualKept]); } if(pagerActualKept > 0) pagerRow[0] = FV; for (i = pagerActualKept; i > 0; i--) { pagerXbuf[i] = pagerXbuf[i - 1]; pagerRow[i] = pagerRow[i - 1]; strcpy(&pagerTitles[i][4], &pagerTitles[i - 1][4]); } pagerXbuf[0] = nxbuf; pagerRow[0] = 0; strcpy(&pagerTitles[0][4], wtitle); pagerActualKept += 1; for (i = 0; i < pagerActualKept; i++) { enable(pagerMenus[i]); settext(pagerMenus[i], pagerTitles[i]); } for (i = pagerActualKept; i < PAGERMAXKEPT; i++) disable(pagerMenus[i]); uncheck(pagerMenus[pagerActualShown]); pagerActualShown = 0; check(pagerMenus[pagerActualShown]); return 1; } static MenuItem PagerPopup[] = { /* Numbers used below */ {GN_("Copy"), pagercopy, 'C', 0}, /* 0 */ {GN_("Paste to console"), pagerpaste, 'V', 0}, /* 1 */ {GN_("Paste commands to console"), pagerpastecmds, 0, 0}, /* 2 */ {GN_("Select all"), pagerselectall, 'A', 0}, /* 3 */ {"-", 0, 0, 0}, {GN_("Stay on top"), pagerstayontop, 0, 0}, /* 5 */ {"-", 0, 0, 0}, {GN_("Close"), pagerclose, 0, 0}, /* 7 */ LASTMENUITEM }; static void pagermenuact(control m) { control c = getdata(m); ConsoleData p = getdata(c); if (consolecancopy(c)) { enable(p->mcopy); enable(p->mpopcopy); if (CharacterMode == RGui) { enable(p->mpaste); enable(p->mpastecmds); enable(p->mpoppaste); enable(p->mpoppastecmds); } } else { disable(p->mcopy); disable(p->mpopcopy); disable(p->mpaste); disable(p->mpastecmds); disable(p->mpoppaste); disable(p->mpoppastecmds); } if (ismdi()) disable(PagerPopup[5].m); else { enable(PagerPopup[5].m); if (isTopmost(c)) check(PagerPopup[5].m); else uncheck(PagerPopup[5].m); } } #define MCHECK(a) if (!(a)) {freeConsoleData(p);del(c);return NULL;} RECT *RgetMDIsize(void); /* in rui.c */ static pager pagercreate(void) { ConsoleData p; int w, h, i, x, y, w0, h0; pager c; menuitem m; p = newconsoledata((consolefn) ? consolefn : FixedFont, pagerrow, pagercol, 0, 0, guiColors, PAGER, 0, 0); if (!p) return NULL; /* if (ismdi()) { x = y = w = h = 0; } else { w = WIDTH ; h = HEIGHT; x = (devicewidth(NULL) - w) / 2; y = (deviceheight(NULL) - h) / 2 ; } */ w = WIDTH ; h = HEIGHT; /* centre a single pager, randomly place each of multiple pagers */ #ifdef USE_MDI if(ismdi()) { RECT *pR = RgetMDIsize(); w0 = pR->right; h0 = pR->bottom; } else { #endif w0 = devicewidth(NULL); h0 = deviceheight(NULL); #ifdef USE_MDI } #endif x = (w0 - w) / 2; x = x > 20 ? x:20; y = (h0 - h) / 2; y = y > 20 ? y:20; if(pagerMultiple) { #ifdef Win32 DWORD rand = GetTickCount(); #else int rand = 0; #endif int w0 = 0.4*x, h0 = 0.4*y; w0 = w0 > 20 ? w0 : 20; h0 = h0 > 20 ? h0 : 20; x += (rand % w0) - w0/2; y += ((rand/w0) % h0) - h0/2; } c = (pager) newwindow("PAGER", rect(x, y, w, h), Document | StandardWindow | Menubar | VScrollbar | HScrollbar | TrackMouse); if (!c) { freeConsoleData(p); return NULL; } setdata(c, p); if(h == 0) HEIGHT = getheight(c); if(w == 0) WIDTH = getwidth(c); COLS = WIDTH / FW - 1; ROWS = HEIGHT / FH - 1; BORDERX = (WIDTH - COLS*FW) / 2; BORDERY = (HEIGHT - ROWS*FH) / 2; gsetcursor(c, ArrowCursor); gchangescrollbar(c, VWINSB, 0, 0, ROWS, 0); gchangescrollbar(c, HWINSB, 0, COLS-1, COLS, 1); setbackground(c, guiColors[pagerbg]); #ifdef USE_MDI if (ismdi()) { int btsize = 24; rect r = rect(2, 2, btsize, btsize); control tb, bt; addto(c); MCHECK(tb = newtoolbar(btsize + 4)); gsetcursor(tb, ArrowCursor); addto(tb); MCHECK(bt = newtoolbutton(open_image, r, menueditoropen)); MCHECK(addtooltip(bt, G_("Open script"))); gsetcursor(bt, ArrowCursor); /* wants NULL as data, not the pager */ r.x += (btsize + 6) ; MCHECK(bt = newtoolbutton(copy1_image, r, pagerpaste)); MCHECK(addtooltip(bt, G_("Paste to console"))); gsetcursor(bt, ArrowCursor); setdata(bt, (void *) c); r.x += (btsize + 6) ; MCHECK(bt = newtoolbutton(copy1_image, r, pagerpastecmds)); MCHECK(addtooltip(bt, G_("Paste commands to console"))); gsetcursor(bt, ArrowCursor); setdata(bt, (void *) c); r.x += (btsize + 6) ; MCHECK(bt = newtoolbutton(print_image, r, pagerprint)); MCHECK(addtooltip(bt, G_("Print"))); gsetcursor(bt, ArrowCursor); setdata(bt, (void *) c); r.x += (btsize + 6) ; MCHECK(bt = newtoolbutton(console_image, r, pagerconsole)); MCHECK(addtooltip(bt, G_("Return focus to Console"))); gsetcursor(bt, ArrowCursor); setdata(bt, (void *) c); } #endif addto(c); MCHECK(m = gpopup(pagermenuact, PagerPopup)); setdata(m, c); setdata(p->mpopcopy = PagerPopup[0].m, c); setdata(p->mpoppaste = PagerPopup[1].m, c); setdata(p->mpoppastecmds = PagerPopup[2].m, c); setdata(PagerPopup[3].m, c); setdata(PagerPopup[5].m, c); setdata(PagerPopup[7].m, c); MCHECK(m = newmenubar(pagermenuact)); setdata(m, c); MCHECK(newmenu(G_("File"))); MCHECK(m = newmenuitem(G_("New script"), 'N', menueditornew)); MCHECK(m = newmenuitem(G_("Open script..."), 'O', menueditoropen)); MCHECK(m = newmenuitem(G_("Print..."), 'P', pagerprint)); setdata(m, c); MCHECK(m = newmenuitem(G_("Save to File..."), 'S', pagersavefile)); setdata(m, c); MCHECK(m = newmenuitem("-", 0, NULL)); MCHECK(m = newmenuitem(G_("Close"), 0, pagerclose)); setdata(m, c); MCHECK(newmenu(G_("Edit"))); MCHECK(p->mcopy = newmenuitem(G_("Copy"), 'C', pagercopy)); setdata(p->mcopy, c); MCHECK(p->mpaste = newmenuitem(G_("Paste to console"), 'V', pagerpaste)); setdata(p->mpaste, c); MCHECK(p->mpastecmds = newmenuitem(G_("Paste commands to console"), 0, pagerpastecmds)); setdata(p->mpastecmds, c); MCHECK(m = newmenuitem(G_("Select all"), 'A', pagerselectall)); setdata(m, c); if (!pagerMultiple) { MCHECK(newmenu(G_("View"))); for (i = 0; i < PAGERMAXKEPT; i++) { snprintf(pagerTitles[i], PAGERMAXTITLE+8, "&%c. ", 'A' + i); MCHECK(pagerMenus[i] = newmenuitem(&pagerTitles[i][1], 0, pagerchangeview)); setvalue(pagerMenus[i], i); } } #ifdef USE_MDI if (ismdi()) newmdimenu(); if (ismdi() && !(RguiMDI & RW_TOOLBAR)) toolbar_hide(); #endif MCHECK(BM = newbitmap(WIDTH, HEIGHT, 2)); setdata(c, p); sethit(c, console_sbf); setresize(c, consoleresize); setredraw(c, drawconsole); setdel(c, delpager); setclose(c, pagerbclose); setkeyaction(c, console_ctrlkeyin); setkeydown(c, console_normalkeyin); setmousedrag(c, console_mousedrag); setmouserepeat(c, console_mouserep); setmousedown(c, console_mousedown); return(c); } static pager newpager1win(const char *wtitle, const char *filename, int enc, int deleteonexit) { if (!pagerInstance && !(pagerInstance = pagercreate())) { R_ShowMessage(G_("Unable to create pager window")); return NULL; } if (!pageraddfile(wtitle, filename, enc, deleteonexit)) return NULL; pagerupdateview(); return pagerInstance; } static pager newpagerNwin(const char *wtitle, const char *filename, int enc, int deleteonexit) { pager c = pagercreate(); ConsoleData p; if (!c) return NULL; settext(c, wtitle); p = getdata(c); if (!(p->lbuf = file2xbuf(filename, enc, deleteonexit))) { del(c); return NULL; } if (c) { gchangescrollbar(c, VWINSB, 0, NUMLINES - 1 , ROWS, 0); show(c); } return c; } pager newpager(const char *title, const char *filename, int enc, const char *header, int deleteonexit) { char wtitle[PAGERMAXTITLE+1]; pager c; /* if (ismdi()) pagerMultiple = 1;*/ strncpy(wtitle, title, PAGERMAXTITLE); wtitle[PAGERMAXTITLE] = '\0'; if(strlen(header) && ((strlen(header) + strlen(wtitle) + 4) < PAGERMAXTITLE)) { if(strlen(wtitle)) strcat(wtitle, " - "); strcat(wtitle, header); } if (!pagerMultiple) c = newpager1win(wtitle, filename, enc, deleteonexit); else c = newpagerNwin(wtitle, filename, enc, deleteonexit); if (c) { haveusedapager++; BringToTop(c, 0); } return c; }