The R Project SVN R

Rev

Rev 79040 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/*
 *  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--2020  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 <config.h>
#endif

#include "win-nls.h"

#ifdef Win32
#define USE_MDI 1
#endif

#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include "graphapp/ga.h"
#ifdef USE_MDI
#include "graphapp/stdimg.h"
#endif
#include "console.h"
#include "consolestructs.h"
#include "rui.h"
#include <Startup.h> /* 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) {
    wchar_t wfn[MAX_PATH+1];
    Rf_utf8towcs(wfn, name, MAX_PATH+1);
    f = CreateFileW(wfn, GENERIC_READ, FILE_SHARE_READ,
            NULL, OPEN_EXISTING, 0, NULL);
    } 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;
}