/* * R : A Computer Language for Statistical Data Analysis * Copyright (C) 1998--2005 Guido Masarotto and Brian Ripley * Copyright (C) 2004--2023 The R Foundation * * 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" #include /* R user interface based on GraphApp */ #include "Defn.h" #undef append /* defined by graphapp/internal.h */ #include #include #undef DEBUG /* needed for mingw-runtime 2.0 */ /* the user menu code looks at the internal structure */ #define GA_EXTERN #include "graphapp/internal.h" #include "graphapp/ga.h" #include "graphapp/stdimg.h" #include "console.h" #include "rui.h" #include "preferences.h" #include #include "getline/wc_history.h" /* for wgl_load/savehistory */ #include /* for SA_DEFAULT */ #define TRACERUI(a) extern Rboolean UserBreak; console RConsole = NULL; int RguiMDI = RW_MDI | RW_TOOLBAR | RW_STATUSBAR; int MDIset = 0; window RFrame = NULL; /* some compilers want initialized for export */ rect MDIsize; extern int ConsoleAcceptCmd, R_is_running; extern Rboolean DebugMenuitem; Rboolean R_LoadRconsole = TRUE; /* used in commandLineArgs */ static menubar RMenuBar; static popup RConsolePopup; static menuitem msource, mdisplay, mload, msave, mloadhistory, msavehistory, mpaste, mpastecmds, mcopy, mcopypaste, mlazy, mcomplete, mfncomplete, mconfig, mls, mrm, msearch, mde, mtools, mstatus; static int lmanintro, lmanref, lmandata, lmanlang, lmanext, lmanint, lmanadmin, lmanSweave; static menu m; static char cmd[1024]; static HelpMenuItems hmenu; static PkgMenuItems pmenu; #include "editor.h" /* menu callbacks */ /* We need to handle \ in paths which are to be passed to R code. Since these can include \\ for network drives, we cannot just use /, although we did prior to R 2.4.0. MBCS-aware since 2.4.0. */ /* Returns the number of bytes, excluding the terminator, needed. If bufsize is large enough, it contains the terminated result. */ static size_t double_backslashes(char *s, char *out, size_t bufsize) { char *p = s; size_t needed = 0; int i; if(mbcslocale) { mbstate_t mb_st; int used; mbs_init(&mb_st); while((used = Mbrtowc(NULL, p, MB_CUR_MAX, &mb_st))) { if(*p == '\\') { needed ++; if (needed < bufsize) *out++ = '\\'; } for(i = 0; i < used; i++) { needed ++; if (needed < bufsize) *out++ = *p; p++; } } } else { for (; *p; p++) { if(*p == '\\') { needed ++; if (needed < bufsize) *out++ = '\\'; } needed ++; if (needed < bufsize) *out++ = *p; } } needed ++; if (needed <= bufsize) *out = '\0'; return needed - 1; /* exclude terminator */ } void Rconsolecmd(char *cmd) { consolecmd(RConsole, cmd); } static void closeconsole(control m) { consolecmd(RConsole, "q()"); // R_CleanUp(SA_DEFAULT, 0, 1); } /* Returns the number of bytes, excluding the terminator, needed. If bufsize is large enough, it contains the terminated result. */ static size_t quote_fn(wchar_t *fn, char *s, size_t bufsize) { char *p = s; wchar_t *w; int used; size_t needed = 0; char chars[MB_CUR_MAX]; for (w = fn; *w; w++) { if(*w == L'\\') { needed += 2; if (needed < bufsize) { *p++ = '\\'; *p++ = '\\'; } } else { used = wctomb(chars, *w); if(used > 0) { needed += used; if (needed < bufsize) { memcpy(p, chars, used); p += used; } } else { needed += 6; if (needed < bufsize) { sprintf(p, "\\u%04x", (unsigned int) *w); p += 6; /* strips terminator written by sprintf */ } } } } needed ++; if (needed <= bufsize) *p = '\0'; return needed - 1; /* exclude terminator */ } static void cmdfileW(char *fun, wchar_t *fn) { size_t needed, qfnbytes; qfnbytes = quote_fn(fn, NULL, 0); /* source (" thefile ") \0 */ needed = strlen(fun) + 2 + qfnbytes + 2 + 1; char *cmd = (char *) malloc(needed); if (!cmd) return; strcpy(cmd, fun); strcat(cmd, "(\""); quote_fn(fn, cmd + strlen(cmd), qfnbytes + 1); strcat(cmd, "\")"); consolecmd(RConsole, cmd); /* command length limited by NKEYS */ free(cmd); } static void cmdfile(char *fun, char *fn) { size_t needed, dblbytes; dblbytes = double_backslashes(fn, NULL, 0); /* source (" thefile ") \0 */ needed = strlen(fun) + 2 + dblbytes + 2 + 1; char *cmd = (char *)malloc(needed); if (!cmd) return; strcpy(cmd, fun); strcat(cmd, "(\""); double_backslashes(fn, cmd + strlen(cmd), dblbytes + 1); strcat(cmd, "\")"); consolecmd(RConsole, cmd); /* command length limited by NKEYS */ free(cmd); } static void menusource(control m) { wchar_t *fn; if (!ConsoleAcceptCmd) return; setuserfilterW(L"R files (*.R)\0*.R\0S files (*.q, *.ssc, *.S)\0*.q;*.ssc;*.S\0All files (*.*)\0*.*\0\0"); fn = askfilenameW(G_("Select file to source"), ""); if (fn) cmdfileW("source", fn); else show(RConsole); } static void menudisplay(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole,"local({fn<-choose.files(filters=Filters[c('R','txt','All'),],index=4)\nfile.show(fn,header=fn,title='')})"); } static void menuloadimage(control m) { wchar_t *fn; if (!ConsoleAcceptCmd) return; setuserfilterW(L"R images (*.RData)\0*.RData\0R images - old extension (*.rda)\0*.rda\0All files (*.*)\0*.*\0\0"); fn = askfilenameW(G_("Select image to load"), ""); if (fn) cmdfileW("load", fn); else show(RConsole); } static void menusaveimage(control m) { wchar_t *fn; if (!ConsoleAcceptCmd) return; setuserfilterW(L"R images (*.RData)\0*.RData\0All files (*.*)\0*.*\0\0"); fn = askfilesaveW(G_("Save image in"), ".RData"); if (fn) { size_t fnlen = wcslen(fn); if (fn[fnlen - 2] == L'.' && fn[fnlen - 1] == L'*') fn[fnlen - 2] = L'\0'; cmdfileW("save.image", fn); } else show(RConsole); } static void menuloadhistory(control m) { char *fn; setuserfilter("All files (*.*)\0*.*\0\0"); fn = askfilename(G_("Load history from"), R_HistoryFile); if (fn) wgl_loadhistory(fn); } static void menusavehistory(control m) { char *s; setuserfilter("All files (*.*)\0*.*\0\0"); s = askfilesave(G_("Save history in"), R_HistoryFile); if (s) { R_setupHistory(); /* re-read the history size */ wgl_savehistory(s, R_HistorySize); } } static void menuchangedir(control m) { askchangedir(); } static void menuprint(control m) { consoleprint(RConsole); } static void menusavefile(control m) { consolesavefile(RConsole, 0); } static void menuexit(control m) { closeconsole(m); } static void menuselectall(control m) { consoleselectall(RConsole); /* show(RConsole); */ } static void menucopy(control m) { if (consolecancopy(RConsole)) consolecopy(RConsole); else askok(G_("No selection")); /* show(RConsole); */ } static void menupaste(control m) { if (consolecanpaste(RConsole)) consolepaste(RConsole); else askok(G_("No text available")); /* show(RConsole); */ } static void menupastecmds(control m) { if (consolecanpaste(RConsole)) consolepastecmds(RConsole); else askok(G_("No text available")); } static void menucopypaste(control m) { if (consolecancopy(RConsole)) { consolecopy(RConsole); consolepaste(RConsole); } else askok(G_("No selection")); /* show(RConsole); */ } /* button* versions force focus back to the console: needed for PR#3285 */ static void buttoncopy(control m) { menucopy(m); show(RConsole); } static void buttonpaste(control m) { menupaste(m); show(RConsole); } static void buttoncopypaste(control m) { menucopypaste(m); show(RConsole); } static void buttonkill(control m) { show(RConsole); UserBreak = TRUE; } void menuclear(control m) { consoleclear(RConsole); } static void menude(control m) { char *s; SEXP var; if (!ConsoleAcceptCmd) return; s = askstring(G_("Name of data frame or matrix"), ""); if(s) { var = findVar(install(s), R_GlobalEnv); if (var != R_UnboundValue) { snprintf(cmd, 1024,"fix(%s)", s); consolecmd(RConsole, cmd); } else { snprintf(cmd, 1024, G_("'%s' cannot be found"), s); askok(cmd); } } /* show(RConsole); */ } void menuconfig(control m) { Rgui_configure(); /* show(RConsole); */ } static void menutools(control m) { if(ischecked(mtools)) { toolbar_hide(); uncheck(mtools); } else { toolbar_show(); check(mtools); } } void showstatusbar(void) { if(ismdi() && !ischecked(mstatus)) { addstatusbar(); check(mstatus); } } static void menustatus(control m) { if(ischecked(mstatus)) { delstatusbar(); uncheck(mstatus); } else { addstatusbar(); check(mstatus); } } static void menulazy(control m) { consoletogglelazy(RConsole); /* show(RConsole); */ } extern void set_completion_available(int x); static int filename_completion_on = 1; static int check_file_completion(void) { /* ought really to ask utils */ return filename_completion_on; } static void menucomplete(control m) { if(ischecked(mcomplete)) { set_completion_available(0); uncheck(mcomplete); uncheck(mfncomplete); } else { set_completion_available(-1); check(mcomplete); if(check_file_completion()) check(mfncomplete); else uncheck(mfncomplete); } } static void menufncomplete(control m) { char cmd[200], *c0; if(ischecked(mfncomplete)) { c0 = "FALSE"; uncheck(mfncomplete); filename_completion_on = 0; } else { c0 = "TRUE"; check(mfncomplete); filename_completion_on = 1; } snprintf(cmd, 200, "utils::rc.settings(files=%s)", c0); consolecmd(RConsole, cmd); } static void menuconsolestayontop(control m) { BringToTop(RConsole, 2); } static void menukill(control m) { UserBreak = TRUE; } static void menukillall(control m) { consolenewline(RConsole); Rf_jump_to_toplevel(); } static Rboolean isdebuggerpresent(void) { typedef BOOL (*R_CheckDebugger)(void); R_CheckDebugger entry; /* XP or later */ entry = (R_CheckDebugger) GetProcAddress((HMODULE)GetModuleHandle("KERNEL32"), "IsDebuggerPresent"); if (entry == NULL) return(FALSE); else return (Rboolean) entry(); } void breaktodebugger(void) { #if (defined(__i386) || defined(__x86_64)) __asm__("int $3"); #elif defined(__aarch64__) __asm__("brk #0xf000"); #endif } static void menudebug(control m) { breaktodebugger(); } static void menuls(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole,"ls()"); /* show(RConsole); */ } static void menurm(control m) { if (!ConsoleAcceptCmd) return; if (askyesno(G_("Are you sure?")) == YES) consolecmd(RConsole, "rm(list=ls(all=TRUE))"); else show(RConsole); } static void menusearch(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "search()"); /* show(RConsole); */ } static void menupkgload(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "local({pkg <- select.list(sort(.packages(all.available = TRUE)),graphics=TRUE)\nif(nchar(pkg)) library(pkg, character.only=TRUE)})"); /* show(RConsole); */ } static void menupkgupdate(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "update.packages(ask='graphics',checkBuilt=TRUE)"); /* show(RConsole); */ } static void menupkgcranmirror(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "chooseCRANmirror()"); } static void menupkgrepos(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "setRepositories()"); } static void menupkginstallpkgs(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "utils:::menuInstallPkgs()"); /* show(RConsole); */ } static void menupkginstalllocal(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "utils:::menuInstallLocal()"); } static void menucascade(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "utils::arrangeWindows(action='cascade')"); } static void menutilehoriz(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "utils::arrangeWindows(action='horizontal')"); } static void menutilevert(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "utils::arrangeWindows(action='vertical')"); } static void menuminimizegroup(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "utils::arrangeWindows(action='minimize')"); } static void menurestoregroup(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "utils::arrangeWindows(action='restore')"); } static void menuconsolehelp(control m) { consolehelp(); /* show(RConsole); */ } static void menuhelp(control m) { char *s; static char olds[256] = ""; if (!ConsoleAcceptCmd) return; s = askstring(G_("Help on"), olds); /* show(RConsole); */ if (s) { snprintf(cmd, 1024, "help(\"%s\")", s); if (strlen(s) > 255) s[255] = '\0'; strcpy(olds, s); consolecmd(RConsole, cmd); } else show(RConsole); } static void menumainman(control m) { internal_shellexec("doc\\manual\\R-intro.pdf"); } static void menumainref(control m) { internal_shellexec("doc\\manual\\fullrefman.pdf"); } static void menumaindata(control m) { internal_shellexec("doc\\manual\\R-data.pdf"); } static void menumainext(control m) { internal_shellexec("doc\\manual\\R-exts.pdf"); } static void menumainint(control m) { internal_shellexec("doc\\manual\\R-ints.pdf"); } static void menumainlang(control m) { internal_shellexec("doc\\manual\\R-lang.pdf"); } static void menumainadmin(control m) { internal_shellexec("doc\\manual\\R-admin.pdf"); } static void menumainSweave(control m) { internal_shellexec("library\\utils\\doc\\Sweave.pdf"); } static void menuhelpsearch(control m) { char *s; static char olds[256] = ""; if (!ConsoleAcceptCmd) return; s = askstring(G_("Search help"), olds); if (s && strlen(s)) { snprintf(cmd, 1024, "help.search(\"%s\")", s); if (strlen(s) > 255) s[255] = '\0'; strcpy(olds, s); consolecmd(RConsole, cmd); } else show(RConsole); } static void menusearchRsite(control m) { char *s; static char olds[256] = ""; if (!ConsoleAcceptCmd) return; s = askstring(G_("Search for words in help list archives and documentation"), olds); if (s && strlen(s)) { snprintf(cmd, 1024, "RSiteSearch(\"%s\")", s); if (strlen(s) > 255) s[255] = '\0'; strcpy(olds, s); consolecmd(RConsole, cmd); } else show(RConsole); } static void menuapropos(control m) { char *s; static char olds[256] = ""; if (!ConsoleAcceptCmd) return; s = askstring(G_("Apropos"), olds); /* show(RConsole); */ if (s) { snprintf(cmd, 1024, "apropos(\"%s\")", s); if (strlen(s) > 255) s[255] = '\0'; strcpy(olds, s); consolecmd(RConsole, cmd); } else show(RConsole); } static void menuhelpstart(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "help.start()"); /* show(RConsole); internal_shellexec("doc\\html\\index.html"); */ } static void menuFAQ(control m) { internal_shellexec("doc\\manual\\R-FAQ.html"); } static void menurwFAQ(control m) { internal_shellexec("doc\\html\\rw-FAQ.html"); } static void menuabout(control m) { char s[400], s2[256]; PrintVersionString(s2, 256); snprintf(s, 400, "%s\n%s %s %s", s2, "Copyright (C)", R_YEAR, "The R Foundation for Statistical Computing"); askok(s); /* show(RConsole); */ } static void menuRhome(control m) { ShellExecute(NULL, "open", "https://www.r-project.org", NULL, NULL, SW_SHOW); } static void menuCRAN(control m) { if (!ConsoleAcceptCmd) return; consolecmd(RConsole, "utils:::menuShowCRAN()"); } /* some menu commands can be issued only if R is waiting for input */ void helpmenuact(HelpMenuItems hmenu) { if (ConsoleAcceptCmd) { enable(hmenu->mhelp); enable(hmenu->mhelpstart); enable(hmenu->mhelpsearch); enable(hmenu->msearchRsite); enable(hmenu->mapropos); enable(hmenu->mCRAN); } else { disable(hmenu->mhelp); disable(hmenu->mhelpstart); disable(hmenu->mhelpsearch); disable(hmenu->msearchRsite); disable(hmenu->mapropos); disable(hmenu->mCRAN); } } void pkgmenuact(PkgMenuItems pmenu) { if (ConsoleAcceptCmd) { enable(pmenu->mpkgl); enable(pmenu->mpkgm); enable(pmenu->mpkgi); enable(pmenu->mpkgil); enable(pmenu->mpkgu); enable(pmenu->mrepos); } else { disable(pmenu->mpkgl); disable(pmenu->mpkgm); disable(pmenu->mpkgi); disable(pmenu->mpkgil); disable(pmenu->mpkgu); disable(pmenu->mrepos); } } static void menuact(control m) { if (consolegetlazy(RConsole)) check(mlazy); else uncheck(mlazy); /* display needs pager set */ if (R_is_running) enable(mdisplay); else disable(mdisplay); if (ConsoleAcceptCmd) { enable(msource); enable(mload); enable(msave); enable(mls); enable(mrm); enable(msearch); } else { disable(msource); disable(mload); disable(msave); disable(mls); disable(mrm); disable(msearch); } if (consolecancopy(RConsole)) { enable(mcopy); enable(mcopypaste); } else { disable(mcopy); disable(mcopypaste); } if (consolecanpaste(RConsole)) { enable(mpaste); enable(mpastecmds); } else { disable(mpaste); disable(mpastecmds); } helpmenuact(hmenu); pkgmenuact(pmenu); draw(RMenuBar); } #define MCHECK(m) {if(!(m)) {del(RConsole); return 0;}} static Rboolean tryLoadRconsole(char *format, char *varname, struct structGUI* gui) { char *varvalue = getenv(varname); size_t needed = snprintf(NULL, 0, format, varvalue) + 1; char *optf = (char *)malloc(needed); if (optf && (snprintf(optf, needed, format, varvalue) < needed) && loadRconsole(gui, optf)) { free(optf); return TRUE; } return FALSE; } void readconsolecfg(void) { char fn[128]; int sty = Plain; struct structGUI gui; getDefaults(&gui); if (R_LoadRconsole) { if (!tryLoadRconsole("%s/Rconsole", "R_USER", &gui) && !tryLoadRconsole("%s/etc/Rconsole", "R_HOME", &gui)) { app_cleanup(); RConsole = NULL; exit(10); } } if (gui.tt_font) { strcpy(fn, "TT "); strcpy(fn+3, gui.font); } else strcpy(fn, gui.font); MDIsize = gui.MDIsize; if (gui.MDI) RguiMDI |= RW_MDI; else RguiMDI &= ~RW_MDI; if (MDIset == 1) RguiMDI |= RW_MDI; if (MDIset == -1) RguiMDI &= ~RW_MDI; if (gui.toolbar) RguiMDI |= RW_TOOLBAR; else RguiMDI &= ~RW_TOOLBAR; if (gui.statusbar) RguiMDI |= RW_STATUSBAR; else RguiMDI &= ~RW_STATUSBAR; if (!strcmp(gui.style, "normal")) sty = Plain; if (!strcmp(gui.style, "bold")) sty = Bold; if (!strcmp(gui.style, "italic")) sty = Italic; Rwin_graphicsx = gui.grx; Rwin_graphicsy = gui.gry; if(strlen(gui.language)) { char *buf = malloc(50); snprintf(buf, 50, "LANGUAGE=%s", gui.language); putenv(buf); /* no free here: storage remains in use */ } setconsoleoptions(fn, sty, gui.pointsize, gui.crows, gui.ccols, gui.cx, gui.cy, gui.guiColors, gui.prows, gui.pcols, gui.pagerMultiple, gui.setWidthOnResize, gui.cbb, gui.cbl, gui.buffered, gui.cursor_blink); } static void dropconsole(control m, char *fn) { char *p; p = Rf_strrchr(fn, '.'); if(p) { if (!ConsoleAcceptCmd) return; /* OK even in MBCS */ if(stricmp(p+1, "R") == 0) cmdfile("source", fn); /* OK even in MBCS */ else if(stricmp(p+1, "RData") == 0 || stricmp(p+1, "rda")) cmdfile("load", fn); return; } askok(G_("Can only drag-and-drop .R, .RData and .rda files")); } static MenuItem ConsolePopup[] = { /* Numbers used below */ {GN_("Copy"), menucopy, 'C', 0}, /* 0 */ {GN_("Paste"), menupaste, 'V', 0}, /* 1 */ {GN_("Paste commands only"), menupastecmds, 0, 0}, /* 2 */ {GN_("Copy and paste"), menucopypaste, 'X', 0}, /* 3 */ {"-", 0, 0, 0}, {GN_("Clear window"), menuclear, 'L', 0}, /* 5 */ {"-", 0, 0, 0}, {GN_("Select all"), menuselectall, 0, 0}, /* 7 */ {"-", 0, 0}, {GN_("Buffered output"), menulazy, 'W', 0}, /* 9 */ {GN_("Stay on top"), menuconsolestayontop, 0, 0}, /* 10 */ LASTMENUITEM }; static void popupact(control m) { if (consolegetlazy(RConsole)) check(ConsolePopup[9].m); else uncheck(ConsolePopup[9].m); if (consolecancopy(RConsole)) { enable(ConsolePopup[0].m); enable(ConsolePopup[3].m); } else { disable(ConsolePopup[0].m); disable(ConsolePopup[3].m); } if (consolecanpaste(RConsole)) { enable(ConsolePopup[1].m); enable(ConsolePopup[2].m); } else { disable(ConsolePopup[1].m); disable(ConsolePopup[2].m); } if (ismdi()) disable(ConsolePopup[10].m); else { if (isTopmost(RConsole)) check(ConsolePopup[10].m); else uncheck(ConsolePopup[10].m); } } /* Package management menu is common to all R windows */ int RguiPackageMenu(PkgMenuItems pmenu) { MCHECK(newmenu(G_("Packages"))); MCHECK(pmenu->mpkgl = newmenuitem(G_("Load package..."), 0, menupkgload)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(pmenu->mpkgm = newmenuitem(G_("Set CRAN mirror..."), 0, menupkgcranmirror)); MCHECK(pmenu->mrepos = newmenuitem(G_("Select repositories..."), 0, menupkgrepos)); MCHECK(pmenu->mpkgi = newmenuitem(G_("Install package(s)..."), 0, menupkginstallpkgs)); MCHECK(pmenu->mpkgu = newmenuitem(G_("Update packages..."), 0, menupkgupdate)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(pmenu->mpkgil = newmenuitem(G_("Install package(s) from local files..."), 0, menupkginstalllocal)); return 0; } static void CheckForManuals(void) { lmanintro = check_doc_file("doc\\manual\\R-intro.pdf"); lmanref = check_doc_file("doc\\manual\\fullrefman.pdf"); lmandata = check_doc_file("doc\\manual\\R-data.pdf"); lmanlang = check_doc_file("doc\\manual\\R-lang.pdf"); lmanext = check_doc_file("doc\\manual\\R-exts.pdf"); lmanint = check_doc_file("doc\\manual\\R-ints.pdf"); lmanadmin = check_doc_file("doc\\manual\\R-admin.pdf"); lmanSweave = check_doc_file("library\\utils\\doc\\Sweave.pdf"); } /* Help functions common to all R windows. These should be appended to each context-specific help menu */ int RguiCommonHelp(menu m, HelpMenuItems hmenu) { addto(m); MCHECK(hmenu->mFAQ = newmenuitem(G_("FAQ on R"), 0, menuFAQ)); if (!check_doc_file("doc\\manual\\R-FAQ.html")) disable(hmenu->mFAQ); MCHECK(hmenu->mrwFAQ = newmenuitem(G_("FAQ on R for &Windows"), 0, menurwFAQ)); if (!check_doc_file("doc\\html\\rw-FAQ.html")) disable(hmenu->mrwFAQ); if (!lmanintro && !lmanref && !lmandata && !lmanlang && !lmanext && !lmanint && !lmanadmin && !lmanSweave) { MCHECK(hmenu->mman0 = newmenuitem(G_("Manuals (in PDF)"), 0, NULL)); disable(hmenu->mman0); } else { MCHECK(hmenu->mman = newsubmenu(m, G_("Manuals (in PDF)"))); MCHECK(hmenu->mmanintro = newmenuitem("An &Introduction to R", 0, menumainman)); if (!lmanintro) disable(hmenu->mmanintro); MCHECK(hmenu->mmanref = newmenuitem("R &Reference", 0, menumainref)); if (!lmanref) disable(hmenu->mmanref); MCHECK(hmenu->mmandata = newmenuitem("R Data Import/Export", 0, menumaindata)); if (!lmandata) disable(hmenu->mmandata); MCHECK(hmenu->mmanlang = newmenuitem("R Language Definition", 0, menumainlang)); if (!lmanlang) disable(hmenu->mmanlang); MCHECK(hmenu->mmanext = newmenuitem("Writing R Extensions", 0, menumainext)); if (!lmanext) disable(hmenu->mmanext); MCHECK(hmenu->mmanint = newmenuitem("R Internals", 0, menumainint)); if (!lmanint) disable(hmenu->mmanint); MCHECK(hmenu->mmanadmin = newmenuitem("R Installation and Administration", 0, menumainadmin)); if (!lmanadmin) disable(hmenu->mmanadmin); MCHECK(hmenu->mmanSweave = newmenuitem("Sweave User", 0, menumainSweave)); if (!lmanSweave) disable(hmenu->mmanSweave); } addto(m); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(hmenu->mhelp = newmenuitem(G_("R functions (text)..."), 0, menuhelp)); MCHECK(hmenu->mhelpstart = newmenuitem(G_("Html help"), 0, menuhelpstart)); if (!check_doc_file("doc\\html\\index.html")) disable(hmenu->mhelpstart); MCHECK(hmenu->mhelpsearch = newmenuitem(G_("Search help..."), 0, menuhelpsearch)); MCHECK(hmenu->msearchRsite = newmenuitem("search.r-project.org ...", 0, menusearchRsite)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(hmenu->mapropos = newmenuitem(G_("Apropos..."), 0, menuapropos)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(newmenuitem(G_("R Project home page"), 0, menuRhome)); MCHECK(hmenu->mCRAN = newmenuitem(G_("CRAN home page"), 0, menuCRAN)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(newmenuitem(G_("About"), 0, menuabout)); return 0; } static int RguiWindowMenu(void) { if (ismdi()) newmdimenu(); else { MCHECK(newmenu(G_("Windows"))); MCHECK(newmenuitem(G_("Cascade"), 0, menucascade)); MCHECK(newmenuitem(G_("Tile &Horizontally"), 0, menutilehoriz)); MCHECK(newmenuitem(G_("Tile &Vertically"), 0, menutilevert)); MCHECK(newmenuitem(G_("Minimize group"), 0, menuminimizegroup)); MCHECK(newmenuitem(G_("Restore group"), 0, menurestoregroup)); } return 0; } #include int setupui(void) { char *p, *ctype, Rlocale[1000] = ""; /* Windows' locales can be very long */ initapp(0, 0); /* set locale before doing anything with menus */ /* localeCP is used for the current locale when translating text in ui */ /* FIXME: sync this with R_check_locale/setup_Rmainloop */ setlocale(LC_CTYPE, ""); /* necessary in case next fails to set a valid locale */ if((p = getenv("LC_ALL"))) strncpy(Rlocale, p, sizeof(Rlocale)-1); else if((p = getenv("LC_CTYPE"))) strncpy(Rlocale, p, sizeof(Rlocale)-1); if (strcmp(Rlocale, "C") == 0) strcpy(Rlocale, "en"); setlocale(LC_CTYPE, Rlocale); mbcslocale = MB_CUR_MAX > 1; ctype = setlocale(LC_CTYPE, NULL); p = strrchr(ctype, '.'); localeCP = 1252; if(p) { if (isdigit(p[1])) localeCP = atoi(p+1); else if (!strcasecmp(p+1, "UTF-8") || !strcasecmp(p+1, "UTF8")) localeCP = 65001; } readconsolecfg(); int flags = StandardWindow | Document | Menubar; if(mbcslocale) flags |= UseUnicode; if (RguiMDI & RW_MDI) { TRACERUI("Rgui"); RFrame = newwindow( #ifdef _WIN64 "RGui", #else "RGui (32-bit)", #endif MDIsize, StandardWindow | Menubar | Workspace); setclose(RFrame, closeconsole); show(RFrame); TRACERUI("Rgui done"); TRACERUI("Console"); if (!(RConsole = newconsole("R Console", flags ))) return 0; TRACERUI("Console done"); } else { TRACERUI("Console"); #ifdef _WIN64 if (!(RConsole = newconsole("R Console", flags ))) return 0; #else if (!(RConsole = newconsole("R Console (32-bit)", flags ))) return 0; #endif TRACERUI("Console done"); } if (ismdi()) { int btsize = 24; rect r = rect(2, 2, btsize, btsize); control tb, bt; MCHECK(tb = newtoolbar(btsize + 4)); addto(tb); MCHECK(bt = newtoolbutton(open_image, r, menueditoropen)); MCHECK(addtooltip(bt, G_("Open script"))); r.x += (btsize + 1) ; MCHECK(bt = newtoolbutton(open1_image, r, menuloadimage)); MCHECK(addtooltip(bt, G_("Load workspace"))); r.x += (btsize + 1) ; MCHECK(bt = newtoolbutton(save_image, r, menusaveimage)); MCHECK(addtooltip(bt, G_("Save workspace"))); r.x += (btsize + 6); MCHECK(bt = newtoolbutton(copy_image, r, buttoncopy)); MCHECK(addtooltip(bt, G_("Copy"))); r.x += (btsize + 1); MCHECK(bt = newtoolbutton(paste_image, r, buttonpaste)); MCHECK(addtooltip(bt, G_("Paste"))); r.x += (btsize + 1); MCHECK(bt = newtoolbutton(copypaste_image, r, buttoncopypaste)); MCHECK(addtooltip(bt, G_("Copy and paste"))); r.x += (btsize + 6); MCHECK(bt = newtoolbutton(stop_image, r, buttonkill)); MCHECK(addtooltip(bt, G_("Stop current computation"))); r.x += (btsize + 6) ; MCHECK(bt = newtoolbutton(print_image, r, menuprint)); MCHECK(addtooltip(bt, G_("Print"))); } if (ismdi() && (RguiMDI & RW_STATUSBAR)) { TRACERUI("status bar"); addstatusbar(); addto(RConsole); TRACERUI("status bar done"); } if (ismdi()) { char s[256]; PrintVersionString(s, 256); setstatus(s); } addto(RConsole); setclose(RConsole, closeconsole); setdrop(RConsole, dropconsole); MCHECK(RConsolePopup = gpopup(popupact, ConsolePopup)); MCHECK(RMenuBar = newmenubar(menuact)); MCHECK(newmenu(G_("File"))); MCHECK(msource = newmenuitem(G_("Source R code..."), 0, menusource)); MCHECK(newmenuitem(G_("New script"), 0, menueditornew)); MCHECK(newmenuitem(G_("Open script..."), 0, menueditoropen)); MCHECK(mdisplay = newmenuitem(G_("Display file(s)..."), 0, menudisplay)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(mload = newmenuitem(G_("Load Workspace..."), 0, menuloadimage)); MCHECK(msave = newmenuitem(G_("Save Workspace..."), 'S', menusaveimage)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(mloadhistory = newmenuitem(G_("Load History..."), 0, menuloadhistory)); MCHECK(msavehistory = newmenuitem(G_("Save History..."), 0, menusavehistory)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(newmenuitem(G_("Change dir..."), 0, menuchangedir)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(newmenuitem(G_("Print..."), 'P', menuprint)); MCHECK(newmenuitem(G_("Save to File..."), 0, menusavefile)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(newmenuitem(G_("Exit"), 0, menuexit)); MCHECK(newmenu(G_("Edit"))); MCHECK(mcopy = newmenuitem(G_("Copy"), 'C', menucopy)); MCHECK(mpaste = newmenuitem(G_("Paste"), 'V', menupaste)); MCHECK(mpastecmds = newmenuitem(G_("Paste commands only"), 0, menupastecmds)); MCHECK(mcopypaste = newmenuitem(G_("Copy and Paste"), 'X', menucopypaste)); MCHECK(newmenuitem(G_("Select all"), 0, menuselectall)); MCHECK(newmenuitem(G_("Clear console"), 'L', menuclear)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(mde = newmenuitem(G_("Data editor..."), 0, menude)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(mconfig = newmenuitem(G_("GUI preferences..."), 0, menuconfig)); if (ismdi()) { MCHECK(newmenu(G_("View"))); MCHECK(mtools = newmenuitem(G_("Toolbar"), 0, menutools)); MCHECK(mstatus = newmenuitem(G_("Statusbar"), 0, menustatus)); if(RguiMDI & RW_TOOLBAR) check(mtools); if(RguiMDI & RW_STATUSBAR) check(mstatus); } MCHECK(newmenu(G_("Misc"))); MCHECK(newmenuitem(G_("Stop current computation \tESC"), 0, menukill)); MCHECK(newmenuitem(G_("Stop all computations"), 0, menukillall)); if (DebugMenuitem || isdebuggerpresent()) MCHECK(newmenuitem(G_("Break to debugger"), 0, menudebug)); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(mlazy = newmenuitem(G_("Buffered output"), 'W', menulazy)); MCHECK(mcomplete = newmenuitem(G_("Word completion"), 0, menucomplete)); check(mcomplete); MCHECK(mfncomplete = newmenuitem(G_("Filename completion"), 0, menufncomplete)); if(check_file_completion()) check(mfncomplete); else uncheck(mfncomplete); MCHECK(newmenuitem("-", 0, NULL)); MCHECK(mls = newmenuitem(G_("List objects"), 0, menuls)); MCHECK(mrm = newmenuitem(G_("Remove all objects"), 0, menurm)); MCHECK(msearch = newmenuitem(G_("List search &path"), 0, menusearch)); pmenu = (PkgMenuItems) malloc(sizeof(struct structPkgMenuItems)); RguiPackageMenu(pmenu); RguiWindowMenu(); MCHECK(m = newmenu(G_("Help"))); MCHECK(newmenuitem(G_("Console"), 0, menuconsolehelp)); MCHECK(newmenuitem("-", 0, NULL)); CheckForManuals(); hmenu = (HelpMenuItems) malloc(sizeof(struct structHelpMenuItems)); RguiCommonHelp(m, hmenu); consolesetbrk(RConsole, menukill, ESC, 0); wgl_hist_init(R_HistorySize, 0); if (R_RestoreHistory) wgl_loadhistory(R_HistoryFile); if (ismdi() && !(RguiMDI & RW_TOOLBAR)) toolbar_hide(); show(RConsole); return 1; } static RECT RframeRect; /* for use by pagercreate */ RECT *RgetMDIsize(void) { GetClientRect(hwndClient, &RframeRect); return &RframeRect; } int RgetMDIwidth(void) { return RgetMDIsize()->right; } int RgetMDIheight(void) { return RgetMDIsize()->bottom; } static menu *usermenus; static char **usermenunames; static Uitem *umitems; static int nmenus=0, nitems=0, alloc_menus=-1, alloc_items=-1; static void menuuser(control m) { int item = m->max; char *p = umitems[item]->action; if (strcmp(p, "none") == 0) return; Rconsolecmd(p); } int numwinmenus(void) { return(nmenus); } char *getusermenuname(int pos) { return(usermenunames[pos]); } menuItems *wingetmenuitems(const char *mname, char *errmsg) { menuItems *items; char mitem[1002], *p, *q, *r; int i,j = 0; if (strlen(mname) > 1000) { strcpy(errmsg, G_("'mname' is limited to 1000 bytes")); return NULL; } q = (char *)malloc(1000 * sizeof(char)); r = (char *)malloc(1002 * sizeof(char)); items = (menuItems *)malloc(sizeof(menuItems)); if(nitems > 0) items->mItems = (Uitem *)malloc(alloc_items * sizeof(Uitem)); strcpy(mitem, mname); strcat(mitem, "/"); for (i = 0; i < nitems; i++) { p = strstr(umitems[i]->name, mitem); if (p == NULL) continue; /* the 'mitem' pattern might be showing up */ /* as a substring in another valid name. Make sure */ /* this isn't the case */ if (strlen(p) != strlen(umitems[i]->name)) continue; strcpy(q, p+strlen(mitem)); /* Due to the way menu items are stored, it can't be */ /* determined if this is say item 'foo' from menu 'Blah/bar' */ /* or item 'bar/foo' from menu 'Blah'. Check this manually */ /* by adding the item label to the menu we're looking for. */ snprintf(r, 1002, "%s%s", mitem, umitems[i]->m->text); if (strcmp(r, p) != 0) continue; items->mItems[j] = (Uitem)malloc(sizeof(uitem)); items->mItems[j]->name = (char *)malloc((strlen(q) + 1) * sizeof(char)); items->mItems[j]->action = (char *)malloc((strlen(umitems[i]->action) + 1) * sizeof(char)); strcpy(items->mItems[j]->name, q); strcpy(items->mItems[j]->action, umitems[i]->action); j++; } free(q); free(r); items->numItems = j; if (j == 0) sprintf(errmsg, G_("menu %s does not exist"), mname); return(items); } void freemenuitems(menuItems *items) { int j; for (j = 0; j < items->numItems; j++) { free(items->mItems[j]->name); free(items->mItems[j]->action); free(items->mItems[j]); } free(items->mItems); free(items); } static menu getMenu(const char * name) { int i; for (i = 0; i < nmenus; i++) if (strcmp(name, usermenunames[i]) == 0) return(usermenus[i]); if (strcmp(name, "$ConsolePopup") == 0) return(RConsolePopup); else if (strcmp(name, "$ConsoleMain") == 0) return(RMenuBar); else if (strncmp(name, "$Graph", 6) == 0) return(getGraphMenu(name)); else return(NULL); } int winaddmenu(const char *name, char *errmsg) { const char *submenu = name; char *p, start[501]; menu parent; if (getMenu(name)) return 0; /* Don't add repeats */ if (nmenus >= alloc_menus) { if(alloc_menus <= 0) { alloc_menus = 10; usermenus = (menu *) malloc(sizeof(menu) * alloc_menus); usermenunames = (char **) malloc(sizeof(char *) * alloc_menus); } else { alloc_menus += 10; usermenus = (menu *) realloc(usermenus, sizeof(menu) * alloc_menus); usermenunames = (char **) realloc(usermenunames, sizeof(char *) * alloc_menus); } } if (strlen(name) > 500) { strcpy(errmsg, G_("'menu' is limited to 500 bytes")); return 5; } p = Rf_strrchr(name, '/'); if (p) { submenu = p + 1; strcpy(start, name); *Rf_strrchr(start, '/') = '\0'; parent = getMenu(start); if (!parent) { strcpy(errmsg, G_("base menu does not exist")); return 3; } m = newsubmenu(parent, submenu); } else { addto(RMenuBar); m = newmenu(submenu); } if (m) { usermenus[nmenus] = m; usermenunames[nmenus] = strdup(name); // FIXME: allocation could fail. nmenus++; show(RConsole); return 0; } else { strcpy(errmsg, G_("failed to allocate menu")); return 1; } } int winaddmenuitem(const char * item, const char * menu, const char * action, char *errmsg) { int i, im; menuitem m; char mitem[1002], *p; /* if (nitems > 499) { strcpy(errmsg, G_("too many menu items have been created")); return 2; } */ if (strlen(item) + strlen(menu) > 1000) { strcpy(errmsg, G_("menu + item is limited to 1000 bytes")); return 5; } for (im = 0; im < nmenus; im++) { if (strcmp(menu, usermenunames[im]) == 0) break; } if (im == nmenus) { strcpy(errmsg, G_("menu does not exist")); return 3; } strcpy(mitem, menu); strcat(mitem, "/"); strcat(mitem, item); for (i = 0; i < nitems; i++) { if (strcmp(mitem, umitems[i]->name) == 0) break; } if (i < nitems) { /* existing item */ if (strcmp(action, "enable") == 0) { enable(umitems[i]->m); } else if (strcmp(action, "disable") == 0) { disable(umitems[i]->m); } else { p = umitems[i]->action; p = realloc(p, strlen(action) + 1); if(!p) { strcpy(errmsg, G_("failed to allocate char storage")); return 4; } strcpy(p, action); } } else { addto(usermenus[im]); m = newmenuitem(item, 0, menuuser); if (m) { if(alloc_items <= nitems) { if(alloc_items <= 0) { alloc_items = 100; umitems = (Uitem *) malloc(sizeof(Uitem) * alloc_items); } else { alloc_items += 100; umitems = (Uitem *) realloc(umitems, sizeof(Uitem) * alloc_items); } } umitems[nitems] = (Uitem) malloc(sizeof(uitem)); umitems[nitems]->m = m; umitems[nitems]->name = p = (char *) malloc(strlen(mitem) + 1); if(!p) { strcpy(errmsg, G_("failed to allocate char storage")); return 4; } strcpy(p, mitem); if(!p) { strcpy(errmsg, G_("failed to allocate char storage")); return 4; } umitems[nitems]->action = p = (char *) malloc(strlen(action) + 1); strcpy(p, action); m->max = nitems; nitems++; } else { strcpy(errmsg, G_("failed to allocate menuitem")); return 1; } } show(RConsole); return 0; } int windelmenu(const char * menu, char *errmsg) { int i, j, count = 0, len = strlen(menu); j = 0; for (i = 0; i < nmenus; i++) { if (strcmp(menu, usermenunames[i]) == 0 || (strncmp(menu, usermenunames[i], len) == 0 && usermenunames[i][len] == '/')) { remove_menu_item(usermenus[i]); count++; } else { if (j < i) { strcpy(usermenunames[j], usermenunames[i]); usermenus[j] = usermenus[i]; } j++; } } nmenus -= count; if (!count) { strcpy(errmsg, G_("menu does not exist")); return 3; } /* Delete any menu items in this menu */ for (j = nitems - 1; j >= 0; j--) { if (strncmp(menu, umitems[j]->name, len) == 0 && umitems[j]->name[len] == '/') windelmenuitem(umitems[j]->name + len + 1, menu, errmsg); } show(RConsole); return 0; } void windelmenus(const char * prefix) { int i, len = strlen(prefix); for (i = nmenus-1; i >=0; i--) { if (strncmp(prefix, usermenunames[i], len) == 0) windelmenu(usermenunames[i], G_("menu not found")); } } int windelmenuitem(const char * item, const char * menu, char *errmsg) { int i; char mitem[1002]; if (strlen(item) + strlen(menu) > 1000) { strcpy(errmsg, G_("menu + item is limited to 1000 bytes")); return 5; } strcpy(mitem, menu); strcat(mitem, "/"); strcat(mitem, item); for (i = 0; i < nitems; i++) { if (strcmp(mitem, umitems[i]->name) == 0) break; } if (i == nitems) { strcpy(errmsg, G_("menu or item does not exist")); return 3; } delobj(umitems[i]->m); strcpy(umitems[i]->name, "invalid"); free(umitems[i]->action); show(RConsole); return 0; }