The R Project SVN R

Rev

Rev 87180 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4394 ripley 1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
3
 *  file console.c
24577 ripley 4
 *  Copyright (C) 1998--2003  Guido Masarotto and Brian Ripley
43882 ripley 5
 *  Copyright (C) 2004-8      The R Foundation
88867 kalibera 6
 *  Copyright (C) 2004-2025   The R Core Team
4394 ripley 7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  (at your option) any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
42300 ripley 19
 *  along with this program; if not, a copy is available at
68956 ripley 20
 *  https://www.R-project.org/Licenses/
4394 ripley 21
 */
22
 
8872 pd 23
#ifdef HAVE_CONFIG_H
24
#include <config.h>
25
#endif
26
 
32888 ripley 27
#include "win-nls.h"
32976 ripley 28
#include <R_ext/Boolean.h>
29
extern Rboolean mbcslocale;
32888 ripley 30
 
9212 ripley 31
#define USE_MDI 1
18653 ripley 32
extern void R_ProcessEvents(void);
64107 murdoch 33
extern void R_WaitEvent(void);
9212 ripley 34
 
36901 ripley 35
#define WIN32_LEAN_AND_MEAN 1
4394 ripley 36
#include <windows.h>
37
#include <string.h>
38
#include <ctype.h>
32578 ripley 39
#include <wchar.h>
35776 ripley 40
#include <limits.h>
60260 ripley 41
#include <rlocale.h>
55057 murdoch 42
#include <R_ext/Memory.h>
4394 ripley 43
#include "graphapp/ga.h"
9212 ripley 44
#ifdef USE_MDI
4394 ripley 45
#include "graphapp/stdimg.h"
9212 ripley 46
#endif
4394 ripley 47
#include "console.h"
9044 ripley 48
#include "consolestructs.h"
4394 ripley 49
#include "rui.h"
43882 ripley 50
#include "getline/wc_history.h"
66108 ripley 51
#include "Startup.h" /* for CharacterMode */
40717 ripley 52
#include <Fileio.h>
4394 ripley 53
 
51627 ripley 54
#include <stdint.h>
47852 ripley 55
 
45213 ripley 56
/* Surrogate Pairs Macro */
57
#define SURROGATE_PAIRS_HI_MIN  ((uint16_t)0xd800)
58
#define SURROGATE_PAIRS_HI_MAX  ((uint16_t)0xdbff)
59
#define SURROGATE_PAIRS_LO_MIN  ((uint16_t)0xdc00)
60
#define SURROGATE_PAIRS_LO_MAX  ((uint16_t)0xdfff)
61
#define SURROGATE_PAIRS_BIT_SZ  ((uint32_t)10)
62
#define SURROGATE_PAIRS_MASK    (((uint16_t)1 << SURROGATE_PAIRS_BIT_SZ)-1)
63
#define IsSurrogatePairsHi(_h)  (SURROGATE_PAIRS_HI_MIN == \
45214 ripley 64
		      ((uint16_t)(_h) &~ (uint16_t)SURROGATE_PAIRS_MASK ))
45213 ripley 65
#define IsSurrogatePairsLo(_l)  (SURROGATE_PAIRS_LO_MIN == \
45214 ripley 66
		      ((uint16_t)(_l) &~ (uint16_t)SURROGATE_PAIRS_MASK ))
45213 ripley 67
 
53662 ripley 68
#ifdef __GNUC__
69
# undef alloca
70
# define alloca(x) __builtin_alloca((x))
71
#endif
72
 
86871 kalibera 73
#include <Defn.h>
74
 
40959 ripley 75
static void performCompletion(control c);
76
 
32589 ripley 77
 
43882 ripley 78
static inline int wcswidth(const wchar_t *s)
32578 ripley 79
{
43882 ripley 80
    return mbcslocale ? Ri18n_wcswidth(s, wcslen(s)) : wcslen(s);
32578 ripley 81
}
32605 ripley 82
 
43882 ripley 83
static inline int wcwidth(const wchar_t s)
32605 ripley 84
{
43882 ripley 85
    return mbcslocale ? Ri18n_wcwidth(s) : 1;
32605 ripley 86
}
32578 ripley 87
 
43882 ripley 88
static void setCURCOL(ConsoleData p)
32605 ripley 89
{
43882 ripley 90
    wchar_t *P = LINE(NUMLINES - 1);
37591 ripley 91
    int w0 = 0;
92
 
43882 ripley 93
    for (; P < LINE(NUMLINES - 1) + prompt_len + cur_pos; P++)
94
	if(*P == L'\r') w0 = 0; else w0 += wcwidth(*P);
37591 ripley 95
 
96
    CURCOL = w0;
32605 ripley 97
}
98
 
99
 
4394 ripley 100
/* xbuf */
101
 
9212 ripley 102
xbuf newxbuf(xlong dim, xint ms, xint shift)
4394 ripley 103
{
104
    xbuf  p;
105
 
11169 ripley 106
    p = (xbuf) malloc(sizeof(struct structXBUF));
4394 ripley 107
    if (!p)
108
	return NULL;
43882 ripley 109
    p->b = (wchar_t *) malloc((dim + 1) * sizeof(wchar_t));
4394 ripley 110
    if (!p->b) {
11169 ripley 111
	free(p);
4394 ripley 112
	return NULL;
113
    }
11169 ripley 114
    p->user = (int *) malloc(ms * sizeof(int));
4394 ripley 115
    if (!p->user) {
11169 ripley 116
	free(p->b);
117
	free(p);
4394 ripley 118
	return NULL;
119
    }
43882 ripley 120
    p->s = (wchar_t **) malloc(ms * sizeof(wchar_t *));
4394 ripley 121
    if (!p->s) {
11169 ripley 122
	free(p->b);
123
	free(p->user);
124
	free(p);
4394 ripley 125
	return NULL;
126
    }
127
    p->ns = 1;
128
    p->ms = ms;
129
    p->shift = shift;
130
    p->dim = dim;
131
    p->av = dim;
132
    p->free = p->b;
133
    p->s[0] = p->b;
134
    p->user[0] = -1;
43882 ripley 135
    *p->b = L'\0';
4394 ripley 136
    return p;
137
}
138
 
11588 ripley 139
/* reallocate increased buffer sizes and update pointers */
140
void xbufgrow(xbuf p, xlong dim, xint ms)
141
{
142
    if(dim > p->dim) {
45349 ripley 143
	wchar_t *ret = (wchar_t *) realloc(p->b, (dim + 1)*sizeof(wchar_t));
11588 ripley 144
	if(ret) {
45349 ripley 145
	    int i, change;
146
	    change = ret - p->b;
147
	    p->b = ret;
11588 ripley 148
	    p->av += change;
149
	    p->free += change;
150
	    for (i = 0; i < p->ns; i++)  p->s[i] += change;
151
	    p->dim = dim;
152
	}
153
    }
154
    if(ms > p->ms) {
45349 ripley 155
	wchar_t **ret = (wchar_t **) realloc(p->s, ms * sizeof(wchar_t *));
11588 ripley 156
	if(ret) {
45349 ripley 157
	    int *ret2 = (int *) realloc(p->user, ms * sizeof(int));
11588 ripley 158
	    if(ret2) {
45349 ripley 159
		p->s = ret;
160
		p->user = ret2;
11588 ripley 161
		p->ms = ms;
162
	    }
163
	}
164
    }
165
}
166
 
24308 ripley 167
void xbufdel(xbuf p)
9212 ripley 168
{
4394 ripley 169
   if (!p) return;
11169 ripley 170
   free(p->s);
171
   free(p->b);
172
   free(p->user);
173
   free(p);
4394 ripley 174
}
175
 
176
static void xbufshift(xbuf p)
177
{
178
    xint  i;
179
    xlong mshift;
43882 ripley 180
    wchar_t *new0;
4394 ripley 181
 
182
    if (p->shift >= p->ns) {
183
	p->ns = 1;
184
	p->av = p->dim;
185
	p->free = p->b;
186
	p->s[0] = p->b;
43882 ripley 187
	*p->b = L'\0';
4394 ripley 188
	p->user[0] = -1;
189
	return;
190
    }
191
    new0 = p->s[p->shift];
192
    mshift = new0 - p->s[0];
43882 ripley 193
    memmove(p->b, p->s[p->shift], (p->dim - mshift) * sizeof(wchar_t));
4394 ripley 194
    memmove(p->user, &p->user[p->shift], (p->ms - p->shift) * sizeof(int));
195
    for (i = p->shift; i < p->ns; i++)
196
	p->s[i - p->shift] = p->s[i] - mshift;
197
    p->ns = p->ns - p->shift;
198
    p->free -= mshift;
199
    p->av += mshift;
200
}
201
 
7913 ripley 202
static int xbufmakeroom(xbuf p, xlong size)
4394 ripley 203
{
204
    if (size > p->dim) return 0;
8505 pd 205
    while ((p->av < size) || (p->ns == p->ms)) {
80559 kalibera 206
	/* PR#17851 lost-scrollbar/lost-history issue could be handled here.
207
	   Comment from Bill Dunlap: "One could change the p->av<size case to
208
	   discard only enough lines to make space for the new characters, but
209
	   I don't know if this extra complexity would be worthwhile." */
4394 ripley 210
	xbufshift(p);
8505 pd 211
    }
4394 ripley 212
    p->av -= size;
213
    return 1;
214
}
215
 
8505 pd 216
#define XPUTC(c) {xbufmakeroom(p,1); *p->free++=c;}
4394 ripley 217
 
43882 ripley 218
void xbufaddxc(xbuf p, wchar_t c)
4394 ripley 219
{
220
    int   i;
221
 
222
    switch (c) {
43882 ripley 223
    case L'\a':
4394 ripley 224
	gabeep();
225
	break;
43882 ripley 226
    case L'\b':
227
	if ((p->s[p->ns - 1])[0]) {
228
	    p->free--;
229
	    p->av++;
4394 ripley 230
	}
7913 ripley 231
	break;
43882 ripley 232
    case L'\t':
233
	XPUTC(L' ');
8093 ripley 234
	*p->free = '\0';
43882 ripley 235
	/* Changed to  width in 2.7.0 */
236
	for (i = wcswidth(p->s[p->ns - 1]); (i % TABSIZE); i++) XPUTC(L' ');
4394 ripley 237
	break;
43882 ripley 238
    case L'\n':
239
	XPUTC(L'\0');
4394 ripley 240
	p->s[p->ns] = p->free;
8093 ripley 241
	p->user[p->ns++] = -1;
4394 ripley 242
	break;
37591 ripley 243
    default:
4394 ripley 244
	XPUTC(c);
245
    }
43882 ripley 246
    *p->free = L'\0';
4394 ripley 247
}
248
 
43882 ripley 249
void xbufaddxs(xbuf p, const wchar_t *s, int user)
4394 ripley 250
{
43882 ripley 251
    const wchar_t *ps;
4394 ripley 252
    int   l;
253
 
43882 ripley 254
    l = user ? (p->s[p->ns - 1])[0] : -1;
255
    for (ps = s; *ps; ps++) xbufaddxc(p, *ps);
4394 ripley 256
    p->user[p->ns - 1] = l;
257
}
258
 
44069 ripley 259
#define IN_CONSOLE
260
#include "rgui_UTF8.h"
44027 ripley 261
extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);
262
static size_t enctowcs(wchar_t *wc, char *s, int n)
263
{
264
    size_t nc = 0;
265
    char *pb, *pe;
44069 ripley 266
    if((pb = strchr(s, UTF8in[0])) && *(pb+1) == UTF8in[1] &&
267
       *(pb+2) == UTF8in[2]) {
44027 ripley 268
	*pb = '\0';
269
	nc += mbstowcs(wc, s, n);
74921 kalibera 270
	*pb = UTF8in[0]; /* preserve input string, needed in consolewrites */
44027 ripley 271
	pb += 3; pe = pb;
45067 ripley 272
	while(*pe &&
62163 murdoch 273
	      !((pe = strchr(pe, UTF8out[0])) && *(pe+1) == UTF8out[1] &&
44069 ripley 274
	      *(pe+2) == UTF8out[2])) pe++;
44027 ripley 275
	if(!*pe) return nc; /* FIXME */;
276
	*pe = '\0';
277
	/* convert string starting at pb from UTF-8 */
278
	nc += Rf_utf8towcs(wc+nc, pb, (pe-pb));
74921 kalibera 279
	*pe = UTF8out[0]; /* preserve input string, needed in consolewrites */
44027 ripley 280
	pe += 3;
281
	nc += enctowcs(wc+nc, pe, n-nc);
282
    } else nc = mbstowcs(wc, s, n);
283
    return nc;
284
}
285
 
43882 ripley 286
static void xbufadds(xbuf p, const char *s, int user)
287
{
63222 ripley 288
    int n = strlen(s) + 1; /* UCS-2 must be no more chars */
57573 ripley 289
    if (n < 1000) {
290
	wchar_t tmp[n];
291
	enctowcs(tmp, (char *) s, n);
292
	xbufaddxs(p, tmp, user);
293
    } else {
294
	/* very long line */
63222 ripley 295
	wchar_t *tmp = (wchar_t*) malloc(n * sizeof(wchar_t));
57573 ripley 296
	enctowcs(tmp, (char *) s, n);
297
	xbufaddxs(p, tmp, user);
63222 ripley 298
	free(tmp);
57573 ripley 299
    }
43882 ripley 300
}
301
 
4394 ripley 302
static void xbuffixl(xbuf p)
303
{
55061 ripley 304
    wchar_t *ps;
4394 ripley 305
 
43882 ripley 306
    if (!p->ns) return;
4394 ripley 307
    ps = p->s[p->ns - 1];
43882 ripley 308
    p->free = ps + wcslen(ps);
4394 ripley 309
    p->av = p->dim - (p->free - p->b);
310
}
311
 
312
 
313
/* console */
314
 
46826 ripley 315
rgb guiColors[numGuiColors] = {
46784 murdoch 316
	White, Black, gaRed, /* consolebg, consolefg, consoleuser, */
317
	White, Black, gaRed, /* pagerbg, pagerfg, pagerhighlight,  */
318
	White, Black, gaRed, /* dataeditbg, dataeditfg, dataedituser */
86636 kalibera 319
	White, Black,        /* editorbg, editorfg                 */
320
	White                /* dataeditnbg                        */
46784 murdoch 321
};
8081 ripley 322
 
9860 ripley 323
extern int R_HistorySize;  /* from Defn.h */
324
 
9212 ripley 325
ConsoleData
11588 ripley 326
newconsoledata(font f, int rows, int cols, int bufbytes, int buflines,
51948 murdoch 327
	       rgb *guiColors, int kind, int buffered, int cursor_blink)
4394 ripley 328
{
329
    ConsoleData p;
330
 
331
    initapp(0, 0);
11169 ripley 332
    p = (ConsoleData) malloc(sizeof(struct structConsoleData));
4394 ripley 333
    if (!p)
334
	return NULL;
335
    p->kind = kind;
56884 ripley 336
    /* PR#14624 claimed this was needed, with no example */
56888 ripley 337
    p->chbrk = p->modbrk = '\0';
87028 kalibera 338
    p->pushed_back_char = L'\0';
4394 ripley 339
    if (kind == CONSOLE) {
11588 ripley 340
	p->lbuf = newxbuf(bufbytes, buflines, SLBUF);
4394 ripley 341
	if (!p->lbuf) {
11169 ripley 342
	    free(p);
4394 ripley 343
	    return NULL;
344
	}
45067 ripley 345
	p->kbuf = malloc(NKEYS * sizeof(wchar_t));
4394 ripley 346
	if (!p->kbuf) {
347
	    xbufdel(p->lbuf);
11169 ripley 348
	    free(p);
4394 ripley 349
	    return NULL;
350
	}
351
    } else {
352
	p->lbuf = NULL;
353
	p->kbuf = NULL;
354
    }
70116 murdoch 355
    BM = NULL;
82778 kalibera 356
    p->rows = rows; /* ROWS */
357
    p->cols = cols; /* COLS */
46784 murdoch 358
    for (int i=0; i<numGuiColors; i++)
46826 ripley 359
	p->guiColors[i] = guiColors[i];
4394 ripley 360
    p->f = f;
361
    FH = fontheight(f);
362
    FW = fontwidth(f);
363
    WIDTH = (COLS + 1) * FW;
8707 ripley 364
    HEIGHT = (ROWS + 1) * FH + 1; /* +1 avoids size problems in MDI */
4394 ripley 365
    FV = FC = 0;
70116 murdoch 366
    NEWFV = NEWFC = 0;
4394 ripley 367
    p->firstkey = p->numkeys = 0;
368
    p->clp = NULL;
70116 murdoch 369
    CURROW = -1;
9862 ripley 370
    p->overwrite = 0;
4394 ripley 371
    p->needredraw = 0;
41381 ripley 372
    p->wipe_completion = 0;
4394 ripley 373
    p->my0 = p->my1 = -1;
374
    p->mx0 = 5;
375
    p->mx1 = 14;
376
    p->sel = 0;
11075 ripley 377
    p->input = 0;
37860 ripley 378
    p->lazyupdate = buffered;
51948 murdoch 379
    p->cursor_blink = cursor_blink;
4394 ripley 380
    return (p);
381
}
382
 
43882 ripley 383
static int col_to_pos(ConsoleData p, int x)
41380 murdoch 384
{
385
    if(mbcslocale) {
43882 ripley 386
	int w0 = 0, cnt = 0;
387
	wchar_t *P = LINE(CURROW);
388
	for (; w0 < x && *P && P - LINE(CURROW) <= max_pos + prompt_len; ) {
389
	    w0 += Ri18n_wcwidth(*P++);
390
	    cnt++;
41380 murdoch 391
	}
43882 ripley 392
	return(cnt - prompt_len);
393
    } else
394
	return(min(x - prompt_len, max_pos));
41380 murdoch 395
}
396
 
397
static int within_input(ConsoleData p, int mx, int my)
398
{
43882 ripley 399
    return(my == CURROW && mx >= prompt_wid && col_to_pos(p, mx) < max_pos);
45067 ripley 400
}
41380 murdoch 401
 
402
/* Intersect the mouse selection with the input region. If no overlap or !apply, do nothing*/
403
static int intersect_input(ConsoleData p, int apply)
404
{
405
    int my0 = p->my0, my1 = p->my1, mx0 = p->mx0, mx1 = p->mx1, temp;
406
    if (my0 > my1 || (my0 == my1 && mx0 > my1)) { /* put them in order */
45067 ripley 407
	temp = my0;
408
	my0 = my1;
409
	my1 = temp;
410
	temp = mx0;
411
	mx0 = mx1;
412
	mx1 = temp;
41380 murdoch 413
    }
45067 ripley 414
 
41380 murdoch 415
    if (my1 < CURROW || my0 > CURROW) return(0);
416
    if (my0 < CURROW) mx0 = 0;
417
    if (my1 > CURROW) mx1 = COLS;
43882 ripley 418
    if (mx1 < CURCOL || col_to_pos(p, mx0) >= max_pos) return(0);
41380 murdoch 419
    mx0 = max(mx0, prompt_wid);
43882 ripley 420
    while (col_to_pos(p, mx1) >= max_pos) mx1--;
41380 murdoch 421
    if (apply) {
422
	p->mx0 = mx0;
423
	p->mx1 = mx1;
424
	p->my0 = my0;
425
	p->my1 = my1;
426
    }
427
    return(1);
428
}
429
 
32605 ripley 430
/* Here fch and lch are columns, and we have to cope with both MBCS
431
   and double-width chars. */
82386 kalibera 432
/* caller must manage R_alloc stack */
4394 ripley 433
static void writelineHelper(ConsoleData p, int fch, int lch,
43882 ripley 434
			    rgb fgr, rgb bgr, int j, int len, wchar_t *s)
4394 ripley 435
{
436
    rect  r;
437
 
32578 ripley 438
    /* This is right, since columns are of fixed size */
4394 ripley 439
    r = rect(BORDERX + fch * FW, BORDERY + j * FH, (lch - fch + 1) * FW, FH);
70116 murdoch 440
    gfillrect(BM, bgr, r);
7454 ripley 441
 
32606 ripley 442
    if (len > FC+fch) {
32578 ripley 443
	/* Some of the string is visible: */
32976 ripley 444
	if(mbcslocale) {
43882 ripley 445
	    int i, w0, nc;
52804 ripley 446
	    wchar_t *P = s, *q;
43882 ripley 447
	    Rboolean leftedge;
32605 ripley 448
 
43882 ripley 449
	    nc = (wcslen(s) + 1) * sizeof(wchar_t); /* overkill */
55057 murdoch 450
	    wchar_t *buff = (wchar_t*) R_alloc(nc, sizeof(wchar_t));
52804 ripley 451
	    q = buff;
32976 ripley 452
	    leftedge = FC && (fch == 0);
453
	    if(leftedge) fch++;
43882 ripley 454
	    for (w0 = -FC; w0 < fch && *P; P++) /* should have enough ... */
455
		w0 += Ri18n_wcwidth(*P);
32976 ripley 456
	    /* Now we have got to on or just after the left edge.
457
	       Possibly have a widechar hanging over.
458
	       If so, fill with blanks.
459
	    */
43882 ripley 460
	    if(w0 > fch) for(i = 0; i < w0 - fch; i++) *q++ = L' ';
32605 ripley 461
 
43882 ripley 462
	    if (leftedge) *q++ = L'$';
32618 ripley 463
 
32976 ripley 464
	    while (w0 < lch) {
43882 ripley 465
		if(!*P) break;
466
		w0 += Ri18n_wcwidth(*P);
32976 ripley 467
		if(w0 > lch) break; /* char straddling the right edge
468
				       is not displayed */
43882 ripley 469
		*q++ = *P++;
32976 ripley 470
	    }
43882 ripley 471
	    if((len > FC+COLS) && (lch == COLS - 1)) *q++ = L'$';
472
	    else *q++ = *P++;
473
	    *q = L'\0';
70116 murdoch 474
	    gdrawwcs(BM, p->f, fgr, pt(r.x, r.y), buff);
37676 ripley 475
	} else {
43882 ripley 476
	    int last;
477
	    wchar_t ch, chf, chl;
32976 ripley 478
	    /* we don't know the string length, so modify it in place */
43882 ripley 479
	    if (FC && (fch == 0)) {chf = s[FC]; s[FC] = '$';} else chf = L'\0';
32976 ripley 480
	    if ((len > FC+COLS) && (lch == COLS - 1)) {
481
		chl = s[FC+lch]; s[FC+lch] = '$';
43882 ripley 482
	    } else chl = L'\0';
32976 ripley 483
	    last = FC + lch + 1;
43882 ripley 484
	    if (len > last) {ch = s[last]; s[last] = L'\0';} else ch = L'\0';
70116 murdoch 485
	    gdrawwcs(BM, p->f, fgr, pt(r.x, r.y), &s[FC+fch]);
32976 ripley 486
	    /* restore the string */
487
	    if (ch) s[last] = ch;
488
	    if (chl) s[FC+lch] = chl;
489
	    if (chf) s[FC] = chf;
32578 ripley 490
	}
4394 ripley 491
    }
492
}
493
 
494
#define WLHELPER(a, b, c, d) writelineHelper(p, a, b, c, d, j, len, s)
495
 
11075 ripley 496
/* write line i of the buffer at row j on bitmap */
51948 murdoch 497
static int writeline(control c, ConsoleData p, int i, int j)
4394 ripley 498
{
43882 ripley 499
    wchar_t *s, *stmp, *p0;
4394 ripley 500
    int   insel, len, col1, d;
501
    int   c1, c2, c3, x0, y0, x1, y1;
32606 ripley 502
    rect r;
46784 murdoch 503
    int   bg, fg, highlight, base;
82386 kalibera 504
    const void *vmax = NULL;
46826 ripley 505
 
82386 kalibera 506
    vmax = vmaxget();
46784 murdoch 507
    if (p->kind == CONSOLE) base = consolebg;
508
    else if (p->kind == PAGER) base = pagerbg;
509
    else base = dataeditbg;
46826 ripley 510
 
46784 murdoch 511
    bg = p->guiColors[base];
512
    fg = p->guiColors[base+1];
513
    highlight = p->guiColors[base+2];
4394 ripley 514
 
82386 kalibera 515
    if ((i < 0) || (i >= NUMLINES)) {
516
	vmaxset(vmax);
517
	return 0;
518
    }
37591 ripley 519
    stmp = s = LINE(i);
43882 ripley 520
    len = wcswidth(stmp);
37591 ripley 521
    /* If there is a \r in the line, we need to preprocess it */
43882 ripley 522
    if((p0 = wcschr(s, L'\r'))) {
37591 ripley 523
	int l, l1;
524
	stmp = LINE(i);
82386 kalibera 525
	/* stmp may be large particularly when using txtProgressBar */
526
	s = (wchar_t *) R_alloc(wcslen(stmp) + 1, sizeof(wchar_t));
37591 ripley 527
	l = p0 - stmp;
43882 ripley 528
	wcsncpy(s, stmp, l);
37591 ripley 529
	stmp = p0 + 1;
43882 ripley 530
	while((p0 = wcschr(stmp, L'\r'))) {
37591 ripley 531
	    l1 = p0 - stmp;
43882 ripley 532
	    wcsncpy(s, stmp, l1);
37591 ripley 533
	    if(l1 > l) l = l1;
45067 ripley 534
	    stmp = p0 + 1;
37591 ripley 535
	}
43882 ripley 536
	l1 = wcslen(stmp);
537
	wcsncpy(s, stmp, l1);
45067 ripley 538
	if(l1 > l) l = l1;
43882 ripley 539
	s[l] = L'\0';
37591 ripley 540
	len = l; /* for redraw that uses len */
541
    }
4394 ripley 542
    col1 = COLS - 1;
543
    insel = p->sel ? ((i - p->my0) * (i - p->my1)) : 1;
544
    if (insel < 0) {
46784 murdoch 545
	WLHELPER(0, col1, bg, fg);
82386 kalibera 546
	vmaxset(vmax);
32605 ripley 547
	return len;
4394 ripley 548
    }
549
    if ((USER(i) >= 0) && (USER(i) < FC + COLS)) {
550
	if (USER(i) <= FC)
46784 murdoch 551
	    WLHELPER(0, col1, highlight, bg);
4394 ripley 552
	else {
553
	    d = USER(i) - FC;
46784 murdoch 554
	    WLHELPER(0, d - 1, fg, bg);
555
	    WLHELPER(d, col1, highlight, bg);
4394 ripley 556
	}
7913 ripley 557
    } else if (USER(i) == -2) {
46784 murdoch 558
	WLHELPER(0, col1, highlight, bg);
4394 ripley 559
    } else
46784 murdoch 560
	WLHELPER(0, col1, fg, bg);
32605 ripley 561
    /* This is the cursor, and it may need to be variable-width */
70116 murdoch 562
    if ((CURROW >= 0) && (CURCOL >= FC) && (CURCOL < FC + COLS) &&
41380 murdoch 563
	(i == NUMLINES - 1) && (p->sel == 0 || !intersect_input(p, 0))) {
564
	if (!p->overwrite) {
83479 kalibera 565
	    if (p->cursor_blink) 
51948 murdoch 566
	    	setcaret(c, BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH, 
567
	    	            p->cursor_blink == 1 ? 1 : FW/4, FH);
568
 
569
	    if (p->cursor_blink < 2) {
570
	    	r = rect(BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH, FW/4, FH);
70116 murdoch 571
	    	gfillrect(BM, highlight, r);
51948 murdoch 572
	    }
41380 murdoch 573
	} else if(mbcslocale) { /* determine the width of the current char */
43882 ripley 574
	    int w0;
575
	    wchar_t *P = s, wc = 0, nn[2] = L" ";
576
	    for (w0 = 0; w0 <= CURCOL; P++) {
577
		wc = *P;
578
		if(!*P) break;
35821 ripley 579
		w0 += Ri18n_wcwidth(wc);
32605 ripley 580
	    }
35776 ripley 581
	    /* term string '\0' box width = 1 fix */
45067 ripley 582
	    w0 = wc ? Ri18n_wcwidth(wc) : 1;
43882 ripley 583
	    nn[0] = wc;
83479 kalibera 584
	    if (p->cursor_blink) 
51948 murdoch 585
	    	setcaret(c, BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH, 
586
	    		    p->cursor_blink == 1 ? 1 : FW/4, FH);
587
	    if (p->cursor_blink < 2) {
588
	    	r = rect(BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH,
589
		         w0 * FW, FH);
70116 murdoch 590
	    	gfillrect(BM, highlight, r);
591
	    	gdrawwcs(BM, p->f, bg, pt(r.x, r.y), nn);
51948 murdoch 592
	    }
593
	} else {
83479 kalibera 594
	    if (p->cursor_blink) 
51948 murdoch 595
		setcaret(c, BORDERX + (CURCOL - FC) * FW, BORDERY + j * FH, 
596
		            p->cursor_blink == 1 ? 1 : FW, FH);
597
	    if (p->cursor_blink < 2) 
598
	    	WLHELPER(CURCOL - FC, CURCOL - FC, bg, highlight); 
599
	}
32605 ripley 600
    }
82386 kalibera 601
    if (insel != 0) {
602
	vmaxset(vmax);
603
	return len;
604
    }
4394 ripley 605
    c1 = (p->my0 < p->my1);
606
    c2 = (p->my0 == p->my1);
607
    c3 = (p->mx0 < p->mx1);
608
    if (c1 || (c2 && c3)) {
609
	x0 = p->mx0; y0 = p->my0;
610
	x1 = p->mx1; y1 = p->my1;
611
    } else {
612
	x0 = p->mx1; y0 = p->my1;
613
	x1 = p->mx0; y1 = p->my0;
614
    }
615
    if (i == y0) {
82386 kalibera 616
	if (FC + COLS < x0) {
617
	    vmaxset(vmax);
618
	    return len;
619
	}
33699 ripley 620
	if(mbcslocale) {
43882 ripley 621
	    int w0, w1 = 1;
622
	    wchar_t *P = s;
623
	    for (w0 = 0; w0 < x0; P++) {
624
		if(!*P) break;
625
		w1 = Ri18n_wcwidth(*P);
33699 ripley 626
		w0 += w1;
627
	    }
628
	    if(w0 > x0) x0 = w0 - w1;
629
	}
4394 ripley 630
	c1 = (x0 > FC) ? (x0 - FC) : 0;
631
    } else
632
	c1 = 0;
633
    if (i == y1) {
82386 kalibera 634
	if (FC > x1) {
635
	    vmaxset(vmax);
636
	    return len;
637
	}
33699 ripley 638
	if(mbcslocale) {
43882 ripley 639
	    int w0;
640
	    wchar_t *P = s;
641
	    for (w0 = 0; w0 <= x1; P++) {
642
		if(!*P) break;
643
		w0 += Ri18n_wcwidth(*P);
33699 ripley 644
	    }
43882 ripley 645
	    x1 = w0 - 1;
33699 ripley 646
	}
4394 ripley 647
	c2 = (x1 > FC + COLS) ? (COLS - 1) : (x1 - FC);
648
    } else
649
	c2 = COLS - 1;
46784 murdoch 650
    WLHELPER(c1, c2, bg, fg);
82386 kalibera 651
    vmaxset(vmax);
7454 ripley 652
    return len;
4394 ripley 653
}
654
 
41381 ripley 655
void drawconsole(control c, rect r) /* r is unused here */
32605 ripley 656
{
657
    ConsoleData p = getdata(c);
658
 
7454 ripley 659
    int i, ll, wd, maxwd = 0;
4394 ripley 660
 
661
    ll = min(NUMLINES, ROWS);
32616 ripley 662
    if(!BM) return;;     /* This is a workaround for PR#1711.
663
			    BM should never be null here */
46784 murdoch 664
    if (p->kind == PAGER)
46826 ripley 665
	gfillrect(BM, p->guiColors[pagerbg], getrect(BM));
46784 murdoch 666
    else
46826 ripley 667
	gfillrect(BM, p->guiColors[consolebg], getrect(BM));
32606 ripley 668
    if(!ll) return;;
7454 ripley 669
    for (i = 0; i < ll; i++) {
670
	wd = WRITELINE(NEWFV + i, i);
671
	if(wd > maxwd) maxwd = wd;
672
    }
4394 ripley 673
    RSHOW(getrect(c));
674
    FV = NEWFV;
675
    p->needredraw = 0;
7454 ripley 676
/* always display scrollbar if FC > 0 */
32606 ripley 677
    if(maxwd < COLS - 1) maxwd = COLS - 1;
7454 ripley 678
    maxwd += FC;
32606 ripley 679
    gchangescrollbar(c, HWINSB, FC, maxwd-FC, COLS,
45067 ripley 680
		     p->kind == CONSOLE || NUMLINES > ROWS);
4394 ripley 681
    gchangescrollbar(c, VWINSB, FV, NUMLINES - 1 , ROWS, p->kind == CONSOLE);
32605 ripley 682
}
4394 ripley 683
 
9212 ripley 684
void setfirstvisible(control c, int fv)
32605 ripley 685
{
686
    ConsoleData p = getdata(c);
687
 
4394 ripley 688
    int  ds, rw, ww;
689
 
32606 ripley 690
    if (NUMLINES <= ROWS) return;;
4394 ripley 691
    if (fv < 0) fv = 0;
692
    else if (fv > NUMLINES - ROWS) fv = NUMLINES - ROWS;
693
    if (fv < 0) fv = 0;
694
    ds = fv - FV;
32606 ripley 695
    if ((ds == 0) && !p->needredraw) return;;
4394 ripley 696
    if (abs(ds) > 1) {
45067 ripley 697
	NEWFV = fv;
698
	REDRAW;
699
	return;;
4394 ripley 700
    }
701
    if (p->needredraw) {
45067 ripley 702
	ww = min(NUMLINES, ROWS) - 1;
703
	rw = FV + ww;
82386 kalibera 704
	WRITELINE(rw, ww);
45067 ripley 705
	if (ds == 0) {
4394 ripley 706
	    RSHOW(RLINE(ww));
45067 ripley 707
	    return;;
708
	}
4394 ripley 709
    }
710
    if (ds == 1) {
45067 ripley 711
	gscroll(BM, pt(0, -FH), RMLINES(0, ROWS - 1));
46784 murdoch 712
	if (p->kind == PAGER)
713
	    gfillrect(BM, p->guiColors[pagerbg], RLINE(ROWS - 1));
714
	else
715
	    gfillrect(BM, p->guiColors[consolebg], RLINE(ROWS - 1));
4394 ripley 716
	WRITELINE(fv + ROWS - 1, ROWS - 1);
717
    }
718
    else if (ds == -1) {
45067 ripley 719
	gscroll(BM, pt(0, FH), RMLINES(0, ROWS - 1));
46784 murdoch 720
	if (p->kind == PAGER)
721
	    gfillrect(BM, p->guiColors[pagerbg], RLINE(0));
46826 ripley 722
	else
46784 murdoch 723
	    gfillrect(BM, p->guiColors[consolebg], RLINE(0));
4394 ripley 724
	WRITELINE(fv, 0);
725
    }
726
    RSHOW(getrect(c));
727
    FV = fv;
728
    NEWFV = fv;
729
    p->needredraw = 0;
730
    gchangescrollbar(c, VWINSB, fv, NUMLINES - 1 , ROWS, p->kind == CONSOLE);
32605 ripley 731
}
4394 ripley 732
 
9212 ripley 733
void setfirstcol(control c, int newcol)
32605 ripley 734
{
735
    ConsoleData p = getdata(c);
736
 
4394 ripley 737
    int i, ml, li, ll;
738
 
739
    ll = (NUMLINES < ROWS) ? NUMLINES : ROWS;
740
    if (newcol > 0) {
741
	for (i = 0, ml = 0; i < ll; i++) {
37591 ripley 742
	    /* <FIXME> this should really take \r into account */
43882 ripley 743
	    li = wcswidth(LINE(NEWFV + i));
4394 ripley 744
	    ml = (ml < li) ? li : ml;
745
	}
746
	ml = ml - COLS;
747
	ml = 5*(ml/5 + 1);
748
	if (newcol > ml) newcol = ml;
749
    }
750
    if (newcol < 0) newcol = 0;
751
    FC = newcol;
752
    REDRAW;
32605 ripley 753
}
4394 ripley 754
 
9212 ripley 755
void console_mousedrag(control c, int button, point pt)
32605 ripley 756
{
757
    ConsoleData p = getdata(c);
758
 
4394 ripley 759
    pt.x -= BORDERX;
760
    pt.y -= BORDERY;
761
    if (button & LeftButton) {
762
	int r, s;
763
	r=((pt.y > 32000) ? 0 : ((pt.y > HEIGHT) ? HEIGHT : pt.y))/FH;
764
	s=((pt.x > 32000) ? 0 : ((pt.x > WIDTH) ? WIDTH : pt.x))/FW;
765
	if ((r < 0) || (r > ROWS) || (s < 0) || (s > COLS))
45067 ripley 766
	    return;;
4394 ripley 767
	p->my1 = FV + r;
768
	p->mx1 = FC + s;
769
	p->needredraw = 1;
41380 murdoch 770
	p->sel = 1;
45067 ripley 771
 
41380 murdoch 772
	if (within_input(p, p->mx1, p->my1)) {
43882 ripley 773
	    cur_pos = col_to_pos(p, p->mx1);
41380 murdoch 774
	    setCURCOL(p);
775
	}
4394 ripley 776
	if (pt.y <= 0) setfirstvisible(c, FV - 3);
777
	else if (pt.y >= ROWS*FH) setfirstvisible(c, FV+3);
778
	if (pt.x <= 0) setfirstcol(c, FC - 3);
779
	else if (pt.x >= COLS*FW) setfirstcol(c, FC+3);
780
	else REDRAW;
781
    }
32605 ripley 782
}
4394 ripley 783
 
9212 ripley 784
void console_mouserep(control c, int button, point pt)
32605 ripley 785
{
786
    ConsoleData p = getdata(c);
787
 
9212 ripley 788
    if ((button & LeftButton) && (p->sel)) console_mousedrag(c, button,pt);
32605 ripley 789
}
4394 ripley 790
 
9212 ripley 791
void console_mousedown(control c, int button, point pt)
32605 ripley 792
{
793
    ConsoleData p = getdata(c);
794
 
4394 ripley 795
    pt.x -= BORDERX;
796
    pt.y -= BORDERY;
797
    if (p->sel) {
45067 ripley 798
	p->sel = 0;
799
	p->needredraw = 1;
4394 ripley 800
    }
801
    if (button & LeftButton) {
802
	p->my0 = FV + pt.y/FH;
803
	p->mx0 = FC + pt.x/FW;
45067 ripley 804
	if (within_input(p, p->mx0, p->my0) ||
43882 ripley 805
	    (p->my0 == CURROW && p->mx0 > prompt_wid)) {
806
	    cur_pos = col_to_pos(p, p->mx0);
41380 murdoch 807
	    setCURCOL(p);
808
	    p->needredraw = 1;
809
	}
4394 ripley 810
    }
41380 murdoch 811
    if (p->needredraw) REDRAW;
32605 ripley 812
}
4394 ripley 813
 
814
void consoletogglelazy(control c)
32605 ripley 815
{
816
    ConsoleData p = getdata(c);
817
 
4394 ripley 818
    if (p->kind == PAGER) return;
819
    p->lazyupdate = (p->lazyupdate + 1) % 2;
32605 ripley 820
}
4394 ripley 821
 
822
int consolegetlazy(control c)
32605 ripley 823
{
824
    ConsoleData p = getdata(c);
825
    return p->lazyupdate;
826
}
4394 ripley 827
 
32606 ripley 828
 
4394 ripley 829
void consoleflush(control c)
32605 ripley 830
{
831
    REDRAW;
832
}
4394 ripley 833
 
10227 ripley 834
 
20824 ripley 835
/* These are the getline keys ^A ^E ^B ^F ^N ^P ^K ^H ^D ^U ^T ^O,
836
   plus ^Z for EOF.
837
 
838
   We also use ^C ^V/^Y ^X (copy/paste/both) ^W ^L
839
*/
4394 ripley 840
#define BEGINLINE 1
9862 ripley 841
#define ENDLINE   5
842
#define CHARLEFT 2
843
#define CHARRIGHT 6
844
#define NEXTHISTORY 14
845
#define PREVHISTORY 16
846
#define KILLRESTOFLINE 11
4394 ripley 847
#define BACKCHAR  8
9862 ripley 848
#define DELETECHAR 4
4394 ripley 849
#define KILLLINE 21
9862 ripley 850
#define CHARTRANS 20
851
#define OVERWRITE 15
20824 ripley 852
#define EOFKEY 26
4394 ripley 853
 
40927 ripley 854
/* ^I for completion */
855
 
856
#define TABKEY 9
857
 
858
/* free ^G ^Q ^R ^S, perhaps ^J */
859
 
41381 ripley 860
static void checkpointpos(xbuf p, int save)
861
{
862
    static int ns, av;
43882 ripley 863
    static wchar_t *free;
41381 ripley 864
    if(save) {
865
	ns = p->ns;
866
	av = p->av;
867
	free = p->free;
868
    } else {
869
	p->ns = ns;
870
	p->av = av;
871
	p->free = free;
872
    }
873
}
874
 
43882 ripley 875
static void storekey(control c, int k)
32605 ripley 876
{
877
    ConsoleData p = getdata(c);
878
 
41381 ripley 879
    if (p->wipe_completion) {
880
	p->wipe_completion = 0;
881
	checkpointpos(p->lbuf, 0);
48039 ripley 882
	/* mark whole of current line as user input */
883
	USER(NUMLINES-1) = 0;
41381 ripley 884
	p->needredraw = 1;
885
	REDRAW;
886
    }
4394 ripley 887
    if (p->kind == PAGER) return;
888
    if (k == BKSP) k = BACKCHAR;
40927 ripley 889
    if (k == TABKEY) {
45067 ripley 890
	performCompletion(c);
40927 ripley 891
	return;
892
    }
4394 ripley 893
    if (p->numkeys >= NKEYS) {
894
	gabeep();
32606 ripley 895
	return;;
46826 ripley 896
    }
897
    p->kbuf[(p->firstkey + p->numkeys) % NKEYS] = k;
898
    p->numkeys++;
32605 ripley 899
}
4394 ripley 900
 
82285 kalibera 901
static void storekeys(control c, const char *str)
902
{
903
    if (strlen(str) == 0) return; 
904
    if(isUnicodeWindow(c)) {
905
	size_t sz = (strlen(str) + 1) * sizeof(wchar_t);
82386 kalibera 906
	const void *vmax = NULL;
907
	vmax = vmaxget();
82285 kalibera 908
	wchar_t *wcs = (wchar_t*) R_alloc(strlen(str) + 1, sizeof(wchar_t));
909
	memset(wcs, 0, sz);
910
	mbstowcs(wcs, str, sz-1);
911
	int i;
912
	for(i = 0; wcs[i]; i++) storekey(c, wcs[i]);
82386 kalibera 913
	vmaxset(vmax);
82285 kalibera 914
    } else {
915
	const char *ch;
916
	for (ch = str; *ch; ch++) storekey(c, (unsigned char) *ch);
917
    }
918
}
919
 
43117 ripley 920
static void storetab(control c)
921
{
45067 ripley 922
    ConsoleData p = getdata(c);
923
    p->kbuf[(p->firstkey + p->numkeys) % NKEYS] = L' ';
924
    p->numkeys++;
43117 ripley 925
}
40927 ripley 926
 
43117 ripley 927
 
40927 ripley 928
#include <Rinternals.h>
929
#include <R_ext/Parse.h>
930
 
43238 ripley 931
static int completion_available = -1;
40927 ripley 932
 
43238 ripley 933
void set_completion_available(int x)
41109 ripley 934
{
43238 ripley 935
    completion_available = x;
41109 ripley 936
}
937
 
45067 ripley 938
 
40959 ripley 939
static void performCompletion(control c)
40927 ripley 940
{
941
    ConsoleData p = getdata(c);
82285 kalibera 942
    int i, alen, max_show = 10, cursor_position = CURCOL - prompt_wid;
43882 ripley 943
    wchar_t *partial_line = LINE(NUMLINES - 1) + prompt_wid;
41771 ripley 944
    const char *additional_text;
40927 ripley 945
    SEXP cmdSexp, cmdexpr, ans = R_NilValue;
946
    ParseStatus status;
82340 kalibera 947
    const void *vmax = NULL;
40927 ripley 948
 
43238 ripley 949
    if(!completion_available) {
43117 ripley 950
	storetab(c);
951
	return;
952
    }
45067 ripley 953
 
43238 ripley 954
    if(completion_available < 0) {
41029 ripley 955
	char *p = getenv("R_COMPLETION");
956
	if(p && strcmp(p, "FALSE") == 0) {
43238 ripley 957
	    completion_available = 0;
43117 ripley 958
	    storetab(c);
45067 ripley 959
	    return;
41029 ripley 960
	}
40927 ripley 961
	/* First check if namespace is loaded */
86821 luke 962
	if(R_findVarInFrame(R_NamespaceRegistry, install("utils"))
43238 ripley 963
	   != R_UnboundValue) completion_available = 1;
40927 ripley 964
	else { /* Then try to load it */
43238 ripley 965
	    char *p = "try(loadNamespace('utils'), silent=TRUE)";
40927 ripley 966
	    PROTECT(cmdSexp = mkString(p));
967
	    cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
968
	    if(status == PARSE_OK) {
969
		for(i = 0; i < length(cmdexpr); i++)
970
		    eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
971
	    }
972
	    UNPROTECT(2);
86821 luke 973
	    if(R_findVarInFrame(R_NamespaceRegistry, install("utils"))
43238 ripley 974
	       != R_UnboundValue) completion_available = 1;
40930 ripley 975
	    else {
43238 ripley 976
		completion_available = 0;
40930 ripley 977
		return;
978
	    }
40927 ripley 979
	}
980
    }
981
 
72455 murdoch 982
    alen = wcslen(partial_line);
983
    wchar_t orig[alen + 1], pline[2*alen + 1],
984
            *pchar = pline, achar;
985
    wcscpy(orig, partial_line);
986
    for (i = 0; i < alen; i++) {
987
        achar = orig[i];
988
	if (achar == '"' || achar == '\\') *pchar++ = '\\';
989
	*pchar++ = achar;
990
    }
991
    *pchar = 0;
62583 ripley 992
    size_t len = wcslen(pline) + 100; 
993
    char cmd[len];
994
    snprintf(cmd, len, "utils:::.win32consoleCompletion(\"%ls\", %d)",
995
	     pline, cursor_position);
40927 ripley 996
    PROTECT(cmdSexp = mkString(cmd));
997
    cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
998
    if (status != PARSE_OK) {
999
	UNPROTECT(2);
1000
	/* Uncomment next line to debug */
1001
	/* Rprintf("failed: %s \n", cmd); */
1002
	/* otherwise pretend that nothing happened and return */
1003
	return;
1004
    }
1005
    /* Loop is needed here as EXPSEXP will be of length > 1 */
1006
    for(i = 0; i < length(cmdexpr); i++)
1007
	ans = eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
1008
    UNPROTECT(2);
82340 kalibera 1009
    PROTECT(ans);
40927 ripley 1010
 
1011
    /* ans has the form list(addition, possible), where 'addition' is
1012
       unique additional text if any, and 'possible' is a character
1013
       vector holding possible completions if any (already formatted
1014
       for linewise printing in the current implementation).  If
1015
       'possible' has any content, we want to print those (or show in
1016
       status bar or whatever).  Otherwise add the 'additional' text
1017
       at the cursor */
1018
 
1019
#define ADDITION 0
1020
#define POSSIBLE 1
1021
 
82340 kalibera 1022
    vmax = vmaxget();
40927 ripley 1023
    alen = length(VECTOR_ELT(ans, POSSIBLE));
82340 kalibera 1024
    /* could translate directly to wchar_t */
1025
    additional_text = translateChar(STRING_ELT( VECTOR_ELT(ans, ADDITION), 0 ));
40927 ripley 1026
    if (alen) {
45067 ripley 1027
	/* make a copy of the current string first */
72455 murdoch 1028
	wchar_t p1[wcslen(LINE(NUMLINES - 1)) + 1];
1029
	wcscpy(p1, LINE(NUMLINES - 1));
41381 ripley 1030
	checkpointpos(p->lbuf, 1);
62583 ripley 1031
	size_t len = MB_CUR_MAX * wcslen(p1) + 1; 
72455 murdoch 1032
	char buf1[len+1];
1033
	snprintf(buf1, len+1, "%ls\n", p1);
40959 ripley 1034
	consolewrites(c, buf1);
1035
 
40927 ripley 1036
	for (i = 0; i < min(alen, max_show); i++) {
66932 murdoch 1037
            consolewrites(c, "\n");
82340 kalibera 1038
	    consolewrites(c, translateChar(STRING_ELT(VECTOR_ELT(ans, POSSIBLE), i)));
40927 ripley 1039
	}
1040
	if (alen > max_show)
66932 murdoch 1041
	    consolewrites(c, "\n[...truncated]");
1042
	consolewrites(c, "\n");
41381 ripley 1043
	p->wipe_completion = 1;
40927 ripley 1044
    }
82340 kalibera 1045
    vmaxset(vmax);
1046
    UNPROTECT(1); /* ans */
82285 kalibera 1047
    storekeys(c, additional_text);
40927 ripley 1048
    return;
1049
}
1050
 
41380 murdoch 1051
/* deletes that part of the selection which is on the input line */
1052
static void deleteselected(ConsoleData p)
1053
{
1054
    if (p->sel) {
45067 ripley 1055
	int s0, s1;
1056
	wchar_t *cur_line;
1057
	if (intersect_input(p, 1)) {
41380 murdoch 1058
	    /* convert to bytes after the prompt */
43882 ripley 1059
	    s0 = col_to_pos(p, p->mx0);
1060
	    s1 = col_to_pos(p, p->mx1);
41380 murdoch 1061
	    cur_line = LINE(CURROW) + prompt_len;
43882 ripley 1062
	    for(int i = s0; i <= max_pos; i++)
41380 murdoch 1063
		cur_line[i] = cur_line[i + s1 - s0 + 1];
43882 ripley 1064
	    max_pos -= s1 - s0 + 1;
1065
	    cur_line[max_pos] = L'\0';
45067 ripley 1066
	    if (cur_pos > s0)
43882 ripley 1067
		cur_pos = cur_pos > s1 ? cur_pos - (s1 - s0 + 1) : s0;
41380 murdoch 1068
	    setCURCOL(p);
1069
	    p->needredraw = 1;
1070
	}
1071
    }
1072
}
1073
 
46677 ripley 1074
/* cmd is in native encoding */
43795 ripley 1075
void consolecmd(control c, const char *cmd)
32605 ripley 1076
{
1077
    ConsoleData p = getdata(c);
1078
 
4394 ripley 1079
    int i;
4605 ripley 1080
    if (p->sel) {
45067 ripley 1081
	deleteselected(p);
4605 ripley 1082
	p->sel = 0;
4645 ripley 1083
	p->needredraw = 1;
4605 ripley 1084
	REDRAW;
1085
    }
4394 ripley 1086
    storekey(c, BEGINLINE);
1087
    storekey(c, KILLRESTOFLINE);
82285 kalibera 1088
    storekeys(c, cmd);
4394 ripley 1089
    storekey(c, '\n');
46826 ripley 1090
/* if we are editing we save the actual line
1091
   FIXME: not right if Unicode */
70116 murdoch 1092
    if (CURROW > -1) {
43882 ripley 1093
	char buf[2000], *cp; /* maximum 2 bytes/char */
70116 murdoch 1094
	wchar_t *wc = &(LINE(NUMLINES - 1)[prompt_len]);
43882 ripley 1095
	memset(buf, 0, 2000);
1096
	wcstombs(buf, wc, 1000);
1097
	for (cp = buf; *cp; cp++) storekey(c, *cp);
1098
	for (i = max_pos; i > cur_pos; i--) storekey(c, CHARLEFT);
4394 ripley 1099
    }
32605 ripley 1100
}
4394 ripley 1101
 
43882 ripley 1102
static int CleanTranscript(wchar_t *tscpt, wchar_t *cmds)
28544 murdoch 1103
{
1104
    /*
1105
     * Filter R commands out of a string that contains
1106
     * prompts, commands, and output.
1107
     * Uses a simple algorithm that just looks for '>'
41313 murdoch 1108
     * prompts and '+' continuation following a simple prompt prefix.
28544 murdoch 1109
     * Always return the length of the string required
1110
     * to hold the filtered commands.
1111
     * If cmds is a non-null pointer, write the commands
1112
     * to cmds & terminate with null.
1113
     */
1114
    int incommand = 0, startofline = 1, len = 0;
43882 ripley 1115
    wchar_t nonprefix[] = L">+ \t\n\r";
28544 murdoch 1116
    while (*tscpt) {
1117
	if (startofline) {
41313 murdoch 1118
	    /* skip initial whitespace */
43882 ripley 1119
	    while (*tscpt == L' ' || *tscpt == L'\t') tscpt++;
41313 murdoch 1120
	    /* skip over the prompt prefix */
43882 ripley 1121
	    while (*tscpt && !wcschr(nonprefix, *tscpt)) tscpt++;
1122
	    if (*tscpt == L'>' || (incommand && *tscpt == L'+')) {
28544 murdoch 1123
		tscpt++;
43882 ripley 1124
		if (*tscpt == L' ' || *tscpt == L'\t') tscpt++;
28544 murdoch 1125
		incommand = 1;
1126
	    } else {
1127
		incommand = 0;
1128
	    }
1129
	    startofline = 0;
1130
	} else {
1131
	    if (incommand) {
43882 ripley 1132
		if (cmds) *(cmds++) = *tscpt;
28544 murdoch 1133
		len++;
1134
	    }
43882 ripley 1135
	    if (*tscpt == L'\n') startofline = 1;
28544 murdoch 1136
	    tscpt++;
1137
	}
1138
    }
1139
    if (cmds) {
1140
	/* seem to have to terminate with two nulls, otherwise
1141
	   pasting empty commands doesn't work correctly (e.g.,
1142
	   when clipboard contains 'XXX') */
1143
	*cmds = '\0';
1144
	*(cmds+1) = '\0';
1145
    }
1146
    return(len+2);
1147
}
1148
 
28991 murdoch 1149
/* Send a single newline to the console */
1150
void consolenewline(control c)
1151
{
1152
    storekey(c, '\n');
1153
}
1154
 
28544 murdoch 1155
/* the following four routines are system dependent */
4394 ripley 1156
void consolepaste(control c)
32605 ripley 1157
{
1158
    ConsoleData p = getdata(c);
1159
 
4394 ripley 1160
    HGLOBAL hglb;
43882 ripley 1161
    wchar_t *pc, *new = NULL;
4605 ripley 1162
    if (p->sel) {
45067 ripley 1163
	deleteselected(p);
4605 ripley 1164
	p->sel = 0;
4645 ripley 1165
	p->needredraw = 1;
4605 ripley 1166
	REDRAW;
1167
     }
45491 ripley 1168
    if (p->kind == PAGER) return;
4394 ripley 1169
    if ( OpenClipboard(NULL) &&
45067 ripley 1170
	 (hglb = GetClipboardData(CF_UNICODETEXT)) &&
1171
	 (pc = (wchar_t *) GlobalLock(hglb)))
4394 ripley 1172
    {
45067 ripley 1173
	if (p->clp) {
1174
	   new = realloc((void *)p->clp,
43882 ripley 1175
			 (wcslen(p->clp) + wcslen(pc) + 1) * sizeof(wchar_t));
45067 ripley 1176
	}
1177
	else {
1178
	   new = malloc((wcslen(pc) + 1) * sizeof(wchar_t)) ;
1179
	   if (new) new[0] = L'\0';
1180
	   p->already = p->numkeys;
1181
	   p->pclp = 0;
1182
	}
1183
	if (new) {
45213 ripley 1184
	   int i;
45067 ripley 1185
	   p->clp = new;
45213 ripley 1186
	   /* Surrogate Pairs Block */
45214 ripley 1187
	   for (i = 0; i < wcslen(pc); i++)
1188
	       if (IsSurrogatePairsHi(pc[i]) && i+1 < wcslen(pc) &&
1189
		    IsSurrogatePairsLo(pc[i+1]) ) {
1190
		   pc[i] = L'?';
1191
		   pc[i+1] = L'?';
1192
		   i++;
1193
	       }
45067 ripley 1194
	   wcscat(p->clp, pc);
1195
	}
1196
	else {
1197
	   R_ShowMessage(G_("Not enough memory"));
1198
	}
1199
	GlobalUnlock(hglb);
4394 ripley 1200
    }
1201
    CloseClipboard();
32605 ripley 1202
}
4394 ripley 1203
 
28544 murdoch 1204
void consolepastecmds(control c)
32605 ripley 1205
{
1206
    ConsoleData p = getdata(c);
1207
 
28544 murdoch 1208
    HGLOBAL hglb;
43882 ripley 1209
    wchar_t *pc, *new = NULL;
28544 murdoch 1210
    if (p->sel) {
45067 ripley 1211
	deleteselected(p);
28544 murdoch 1212
	p->sel = 0;
1213
	p->needredraw = 1;
1214
	REDRAW;
1215
     }
32606 ripley 1216
    if (p->kind == PAGER) return;;
28544 murdoch 1217
    if ( OpenClipboard(NULL) &&
45067 ripley 1218
	 (hglb = GetClipboardData(CF_UNICODETEXT)) &&
1219
	 (pc = (wchar_t *) GlobalLock(hglb)))
28544 murdoch 1220
    {
45067 ripley 1221
	if (p->clp) {
1222
	    new = realloc((void *)p->clp,
1223
			  (wcslen(p->clp) + CleanTranscript(pc, 0))
43882 ripley 1224
			  * sizeof(wchar_t));
45067 ripley 1225
	}
1226
	else {
43882 ripley 1227
	    new = malloc(CleanTranscript(pc, 0) * sizeof(wchar_t));
28544 murdoch 1228
	    if (new) new[0] = '\0';
1229
	    p->already = p->numkeys;
1230
	    p->pclp = 0;
45067 ripley 1231
	}
1232
	if (new) {
1233
	    p->clp = new;
28544 murdoch 1234
	    /* copy just the commands from the clipboard */
1235
	    for (; *new; ++new); /* append to the end of 'new' */
1236
	    CleanTranscript(pc, new);
45067 ripley 1237
	}
1238
	else {
33001 ripley 1239
	    R_ShowMessage(G_("Not enough memory"));
45067 ripley 1240
	}
1241
	GlobalUnlock(hglb);
28544 murdoch 1242
    }
1243
    CloseClipboard();
32605 ripley 1244
}
28544 murdoch 1245
 
32605 ripley 1246
/* This works with columns, not chars or bytes */
4394 ripley 1247
static void consoletoclipboardHelper(control c, int x0, int y0, int x1, int y1)
32605 ripley 1248
{
1249
    ConsoleData p = getdata(c);
1250
 
4394 ripley 1251
    HGLOBAL hglb;
1252
    int ll, i, j;
43882 ripley 1253
    wchar_t *s;
4394 ripley 1254
 
32976 ripley 1255
    if(mbcslocale) {
43882 ripley 1256
	int w0, x00 = x0, x11=100000;
1257
	i = y0; ll = 1; /* terminator */
32976 ripley 1258
	while (i <= y1) {
43882 ripley 1259
	    wchar_t *P = LINE(i);
1260
	    for (w0 = 0; w0 < x00 && *P; P++) w0 += Ri18n_wcwidth(*P);
32976 ripley 1261
	    x00 = 0;
1262
	    if(i == y1) x11 = x1+1; /* cols are 0-based */
1263
	    while (w0 < x11 && *P) {
43882 ripley 1264
		ll++;
1265
		w0 += Ri18n_wcwidth(*P++);
32976 ripley 1266
	    }
1267
	    if(w0 < x11) ll += 2;  /* \r\n */
1268
	    i++;
32616 ripley 1269
	}
37676 ripley 1270
    } else {
32976 ripley 1271
	i = y0; j = x0; ll = 1; /* terminator */
1272
	while ((i < y1) || ((i == y1) && (j <= x1))) {
1273
	    if (LINE(i)[j]) {
1274
		ll++;
1275
		j++;
43882 ripley 1276
	    } else {
32976 ripley 1277
		ll += 2;
1278
		i++;
1279
		j = 0;
1280
	    }
32616 ripley 1281
	}
1282
    }
32976 ripley 1283
 
45067 ripley 1284
 
43882 ripley 1285
    if (!(hglb = GlobalAlloc(GHND, ll * sizeof(wchar_t)))){
45067 ripley 1286
	R_ShowMessage(G_("Insufficient memory: text not copied to the clipboard"));
1287
	return;
4394 ripley 1288
    }
43882 ripley 1289
    if (!(s = (wchar_t *)GlobalLock(hglb))){
45067 ripley 1290
	R_ShowMessage(G_("Insufficient memory: text not copied to the clipboard"));
1291
	return;
4394 ripley 1292
    }
32976 ripley 1293
    if(mbcslocale) {
43882 ripley 1294
	int w0, x00 = x0, x11=100000;
1295
	wchar_t *P;
1296
	i = y0;
32976 ripley 1297
	while (i <= y1) {
1298
	    P = LINE(i);
43882 ripley 1299
	    for (w0 = 0; w0 < x00 && *P; P++) w0 += Ri18n_wcwidth(*P);
32976 ripley 1300
	    x00 = 0;
1301
	    if(i == y1) x11 = x1+1;
1302
	    while (w0 < x11 && *P) {
43882 ripley 1303
		w0 += Ri18n_wcwidth(*P);
1304
		*s++ = *P++;
32976 ripley 1305
	    }
77919 kalibera 1306
	    if(w0 < x11) {
1307
		*s++ = L'\r'; *s++ = L'\n';
1308
	    }
32976 ripley 1309
	    i++;
32616 ripley 1310
	}
37676 ripley 1311
    } else {
32976 ripley 1312
	i = y0; j = x0;
1313
	while ((i < y1) || ((i == y1) && (j <= x1))) {
43882 ripley 1314
	    wchar_t ch = LINE(i)[j];
32976 ripley 1315
	    if (ch) {
1316
		*s++ = ch;
1317
		j++;
1318
	    } else {
43882 ripley 1319
		*s++ = L'\r'; *s++ = L'\n';
32976 ripley 1320
		i++;
1321
		j = 0;
1322
	    }
32616 ripley 1323
	}
1324
    }
43882 ripley 1325
    *s = L'\0';
4394 ripley 1326
    GlobalUnlock(hglb);
1327
    if (!OpenClipboard(NULL) || !EmptyClipboard()) {
45067 ripley 1328
	R_ShowMessage(G_("Unable to open the clipboard"));
1329
	GlobalFree(hglb);
1330
	return;;
4394 ripley 1331
    }
43882 ripley 1332
    SetClipboardData(CF_UNICODETEXT, hglb);
4394 ripley 1333
    CloseClipboard();
32605 ripley 1334
}
4394 ripley 1335
 
1336
/* end of system dependent part */
1337
 
9212 ripley 1338
int consolecanpaste(control c)
1339
{
1340
    return clipboardhastext();
1341
}
1342
 
1343
 
4394 ripley 1344
int consolecancopy(control c)
32605 ripley 1345
{
1346
    ConsoleData p = getdata(c);
1347
    return p->sel;
1348
}
4394 ripley 1349
 
32605 ripley 1350
 
4394 ripley 1351
void consolecopy(control c)
32605 ripley 1352
{
1353
    ConsoleData p = getdata(c);
1354
 
4394 ripley 1355
    if (p->sel) {
1356
	int len, c1, c2, c3;
1357
	int x0, y0, x1, y1;
1358
	if (p->my0 >= NUMLINES) p->my0 = NUMLINES - 1;
1359
	if (p->my0 < 0) p->my0 = 0;
43882 ripley 1360
	len = wcswidth(LINE(p->my0));
4394 ripley 1361
	if (p->mx0 >= len) p->mx0 = len - 1;
1362
	if (p->mx0 < 0) p->mx0 = 0;
1363
	if (p->my1 >= NUMLINES) p->my1 = NUMLINES - 1;
1364
	if (p->my1 < 0) p->my1 = 0;
43882 ripley 1365
	len = wcswidth(LINE(p->my1));
10976 ripley 1366
	if (p->mx1 >= len) p->mx1 = len/* - 1*/;
4394 ripley 1367
	if (p->mx1 < 0) p->mx1 = 0;
1368
	c1 = (p->my0 < p->my1);
1369
	c2 = (p->my0 == p->my1);
1370
	c3 = (p->mx0 < p->mx1);
1371
	if (c1 || (c2 && c3)) {
1372
	   x0 = p->mx0; y0 = p->my0;
1373
	   x1 = p->mx1; y1 = p->my1;
1374
	}
1375
	else {
1376
	   x0 = p->mx1; y0 = p->my1;
1377
	   x1 = p->mx0; y1 = p->my0;
1378
	}
1379
	consoletoclipboardHelper(c, x0, y0, x1, y1);
1380
	REDRAW;
1381
    }
32605 ripley 1382
}
4394 ripley 1383
 
1384
void consoleselectall(control c)
32605 ripley 1385
{
1386
    ConsoleData p = getdata(c);
1387
 
4394 ripley 1388
   if (NUMLINES) {
1389
       p->sel = 1;
1390
       p->my0 = p->mx0 = 0;
1391
       p->my1 = NUMLINES - 1;
43882 ripley 1392
       p->mx1 = wcslen(LINE(p->my1));
4394 ripley 1393
       REDRAW;
1394
    }
32605 ripley 1395
}
4394 ripley 1396
 
45067 ripley 1397
/*
43882 ripley 1398
   This works in CJK as the IME puts CJK characters in the
1399
   input buffer as 2 bytes, and they are retrieved successively
1400
*/
9212 ripley 1401
void console_normalkeyin(control c, int k)
32605 ripley 1402
{
1403
    ConsoleData p = getdata(c);
1404
 
4394 ripley 1405
    int st;
1406
 
6994 pd 1407
    st = ggetkeystate();
4394 ripley 1408
    if ((p->chbrk) && (k == p->chbrk) &&
1409
	((!p->modbrk) || ((p->modbrk) && (st == p->modbrk)))) {
1410
	p->fbrk(c);
1411
	return;
1412
    }
1413
    if (st == CtrlKey)
1414
	switch (k + 'A' - 1) {
9862 ripley 1415
	    /* most are stored as themselves */
4394 ripley 1416
	case 'C':
1417
	    consolecopy(c);
1418
	    st = -1;
1419
	    break;
1420
	case 'V':
1421
	case 'Y':
10217 pd 1422
	    if(p->kind == PAGER) {
24308 ripley 1423
		consolecopy(c);
10345 ripley 1424
		if (CharacterMode == RGui) consolepaste(RConsole);
10217 pd 1425
	    }
1426
	    else consolepaste(c);
4394 ripley 1427
	    st = -1;
1428
	    break;
1429
	case 'X':
1430
	    consolecopy(c);
1431
	    consolepaste(c);
1432
	    st = -1;
1433
	    break;
1434
	case 'W':
1435
	    consoletogglelazy(c);
1436
	    st = -1;
1437
	    break;
9862 ripley 1438
	case 'L':
10227 ripley 1439
	    consoleclear(c);
9862 ripley 1440
	    st = -1;
1441
	    break;
1442
	case 'O':
1443
	    p->overwrite = !p->overwrite;
41380 murdoch 1444
	    p->needredraw = 1;
9862 ripley 1445
	    st = -1;
1446
	    break;
4394 ripley 1447
	}
1448
    if (p->sel) {
45067 ripley 1449
	if (st != -1) deleteselected(p);
41380 murdoch 1450
	p->needredraw = 1;
4394 ripley 1451
	p->sel = 0;
1452
    }
41380 murdoch 1453
    if (p->needredraw) REDRAW;
4394 ripley 1454
    if (st == -1) return;
21959 ripley 1455
    if (p->kind == PAGER) {
1456
	if(k == 'q' || k == 'Q') pagerbclose(c);
23057 ripley 1457
	if(k == ' ') setfirstvisible(c, NEWFV + ROWS);
1458
	if(k == '-') setfirstvisible(c, NEWFV - ROWS);
1459
	if(k == 'F' - 'A' + 1) setfirstvisible(c, NEWFV + ROWS);
1460
	if(k == 'B' - 'A' + 1) setfirstvisible(c, NEWFV - ROWS);
23414 ripley 1461
	if(k == 1) consoleselectall(c);
21959 ripley 1462
	return;
1463
    }
9862 ripley 1464
    storekey(c, k);
32605 ripley 1465
}
4394 ripley 1466
 
9212 ripley 1467
void console_ctrlkeyin(control c, int key)
32605 ripley 1468
{
1469
    ConsoleData p = getdata(c);
1470
 
4605 ripley 1471
    int st;
4394 ripley 1472
 
6994 pd 1473
    st = ggetkeystate();
4394 ripley 1474
    if ((p->chbrk) && (key == p->chbrk) &&
1475
	((!p->modbrk) || ((p->modbrk) && (st == p->modbrk)))) {
1476
	p->fbrk(c);
1477
	return;
1478
    }
1479
    switch (key) {
4605 ripley 1480
     case PGUP: setfirstvisible(c, NEWFV - ROWS); break;
1481
     case PGDN: setfirstvisible(c, NEWFV + ROWS); break;
4394 ripley 1482
     case HOME:
1483
	 if (st == CtrlKey)
1484
	     setfirstvisible(c, 0);
1485
	 else
1486
	     if (p->kind == PAGER)
1487
		 setfirstcol(c, 0);
1488
	     else
1489
		 storekey(c, BEGINLINE);
1490
	 break;
1491
     case END:
1492
	 if (st == CtrlKey)
1493
	     setfirstvisible(c, NUMLINES);
1494
	 else
1495
	     storekey(c, ENDLINE);
1496
	 break;
1497
     case UP:
1498
	 if ((st == CtrlKey) || (p->kind == PAGER))
1499
	     setfirstvisible(c, NEWFV - 1);
1500
	 else
1501
	     storekey(c, PREVHISTORY);
1502
	 break;
1503
     case DOWN:
1504
	 if ((st == CtrlKey) || (p->kind == PAGER))
1505
	     setfirstvisible(c, NEWFV + 1);
1506
	 else
1507
	     storekey(c, NEXTHISTORY);
1508
	 break;
1509
     case LEFT:
1510
	 if ((st == CtrlKey) || (p->kind == PAGER))
1511
	     setfirstcol(c, FC - 5);
1512
	 else
1513
	     storekey(c, CHARLEFT);
1514
	 break;
1515
     case RIGHT:
1516
	 if ((st == CtrlKey) || (p->kind == PAGER))
1517
	     setfirstcol(c, FC + 5);
1518
	 else
1519
	     storekey(c, CHARRIGHT);
1520
	 break;
1521
     case DEL:
45067 ripley 1522
	 if (p->sel) {
1523
	     if (st == ShiftKey) consolecopy(c);
1524
	     deleteselected(p);
1525
	     p->sel = 0;
1526
	 } else  if (st == CtrlKey)
4394 ripley 1527
	     storekey(c, KILLRESTOFLINE);
1528
	 else
1529
	     storekey(c, DELETECHAR);
1530
	 break;
1531
     case ENTER:
45067 ripley 1532
	 deleteselected(p);
4394 ripley 1533
	 storekey(c, '\n');
1534
	 break;
1535
     case INS:
41380 murdoch 1536
	 if (st == ShiftKey) {
1537
	     deleteselected(p);
4394 ripley 1538
	     consolepaste(c);
41380 murdoch 1539
	 } else {
1540
	     p->overwrite = !p->overwrite;
1541
	     p->needredraw = 1;
1542
	 }
4394 ripley 1543
	 break;
1544
    }
4605 ripley 1545
    if (p->sel) {
4394 ripley 1546
	p->sel = 0;
4645 ripley 1547
	p->needredraw = 1;
4394 ripley 1548
    }
41380 murdoch 1549
    if (p->needredraw) REDRAW;
32605 ripley 1550
}
4394 ripley 1551
 
12256 pd 1552
static Rboolean incomplete = FALSE;
41771 ripley 1553
int consolewrites(control c, const char *s)
32605 ripley 1554
{
1555
    ConsoleData p = getdata(c);
1556
 
43882 ripley 1557
    wchar_t buf[1001];
11075 ripley 1558
    if(p->input) {
45067 ripley 1559
	int i, len = wcslen(LINE(NUMLINES - 1));
11075 ripley 1560
	/* save the input line */
43882 ripley 1561
	wcsncpy(buf, LINE(NUMLINES - 1), 1000);
1562
	buf[1000] = L'\0';
11075 ripley 1563
	/* now zap it */
43882 ripley 1564
	for(i = 0; i < len; i++) xbufaddxc(p->lbuf, L'\b');
12256 pd 1565
	if (incomplete) {
70116 murdoch 1566
	    NUMLINES--;
12256 pd 1567
	    p->lbuf->free--;
1568
	    p->lbuf->av++;
1569
	}
11075 ripley 1570
	USER(NUMLINES - 1) = -1;
1571
    }
4394 ripley 1572
    xbufadds(p->lbuf, s, 0);
1573
    FC = 0;
12256 pd 1574
    if(p->input) {
1575
	incomplete = (s[strlen(s) - 1] != '\n');
45067 ripley 1576
	if (incomplete) xbufaddxc(p->lbuf, L'\n');
43882 ripley 1577
	xbufaddxs(p->lbuf, buf, 1);
12256 pd 1578
    }
4394 ripley 1579
    if (strchr(s, '\n')) p->needredraw = 1;
65721 ripley 1580
    if (!p->lazyupdate) {
45067 ripley 1581
	setfirstvisible(c, NUMLINES - ROWS);
65721 ripley 1582
	REDRAW;
70116 murdoch 1583
    } else if (CURROW >= 0)
65721 ripley 1584
	setfirstvisible(c, NUMLINES - ROWS);
4394 ripley 1585
    else {
70116 murdoch 1586
	NEWFV = NUMLINES - ROWS;
1587
	if (NEWFV < 0) NEWFV = 0;
4394 ripley 1588
    }
12256 pd 1589
    if(p->input) REDRAW;
32605 ripley 1590
    return 0;
1591
}
4394 ripley 1592
 
9212 ripley 1593
void freeConsoleData(ConsoleData p)
4394 ripley 1594
{
1595
    if (!p) return;
70116 murdoch 1596
    if (BM) del(BM);
4394 ripley 1597
    if (p->kind == CONSOLE) {
45067 ripley 1598
	if (p->lbuf) xbufdel(p->lbuf);
11169 ripley 1599
	if (p->kbuf) free(p->kbuf);
4394 ripley 1600
    }
11169 ripley 1601
    free(p);
4394 ripley 1602
}
1603
 
1604
static void delconsole(control c)
1605
{
1606
    freeConsoleData(getdata(c));
1607
}
1608
 
1609
/* console readline (coded looking to the GNUPLOT 3.5 readline)*/
43882 ripley 1610
static wchar_t consolegetc(control c)
4394 ripley 1611
{
1612
    ConsoleData p;
43882 ripley 1613
    wchar_t ch;
4394 ripley 1614
 
1615
    p = getdata(c);
87028 kalibera 1616
    if (p->pushed_back_char) {
1617
	ch = p->pushed_back_char;
1618
	p->pushed_back_char = L'\0';
1619
	return ch;
1620
    }
4394 ripley 1621
    while((p->numkeys == 0) && (!p->clp))
1622
    {
64106 murdoch 1623
	R_WaitEvent();
7824 ripley 1624
	R_ProcessEvents();
4394 ripley 1625
    }
4605 ripley 1626
    if (p->sel) {
45067 ripley 1627
	deleteselected(p);
4605 ripley 1628
	p->sel = 0;
4645 ripley 1629
	p->needredraw = 1;
32605 ripley 1630
	setCURCOL(p); /* Needed? */
4605 ripley 1631
	REDRAW;
4645 ripley 1632
    }
43882 ripley 1633
    if (!p->already && p->clp) {
4605 ripley 1634
	ch = p->clp[p->pclp++];
1635
	if (!(p->clp[p->pclp])) {
11169 ripley 1636
	    free(p->clp);
4394 ripley 1637
	    p->clp = NULL;
1638
	}
11075 ripley 1639
    } else {
45252 ripley 1640
	if(isUnicodeWindow(c)) {
1641
	    ch = p->kbuf[p->firstkey];
1642
	    p->firstkey = (p->firstkey + 1) % NKEYS;
1643
	    p->numkeys--;
1644
	    if (p->already) p->already--;
1645
	} else {
80744 kalibera 1646
	    /* Will not work for stateful encodings */
1647
	    mbstate_t mb_st;
1648
	    memset(&mb_st, 0, sizeof(mbstate_t));
45252 ripley 1649
	    if(mbcslocale) {
1650
		/* Possibly multiple 'keys' for a single keystroke */
1651
		char tmp[20];
1652
		unsigned int used, i;
1653
 
1654
		for(i = 0; i < MB_CUR_MAX; i++)
1655
		    tmp[i] = p->kbuf[(p->firstkey + i) % NKEYS];
80744 kalibera 1656
		used = mbrtowc(&ch, tmp, MB_CUR_MAX, &mb_st);
45252 ripley 1657
		p->firstkey = (p->firstkey + used) % NKEYS;
1658
		p->numkeys -= used;
1659
		if (p->already) p->already -= used;
1660
	    } else {
1661
		ch = (unsigned char) p->kbuf[p->firstkey];
1662
		if(ch >=128) {
1663
		    char tmp[2] = " ";
1664
		    tmp[0] = ch;
80744 kalibera 1665
		    mbrtowc(&ch, tmp, 2, &mb_st);
45252 ripley 1666
		}
1667
		p->firstkey = (p->firstkey + 1) % NKEYS;
1668
		p->numkeys--;
1669
		if (p->already) p->already--;
1670
	    }
45225 ripley 1671
	}
4394 ripley 1672
    }
1673
    return ch;
1674
}
1675
 
87028 kalibera 1676
/* only works for a single wide character; only used at the end of line
1677
   during overflow */
1678
static void consoleungetc(control c, wchar_t ch)
1679
{
1680
    ConsoleData p;
1681
 
1682
    p = getdata(c);
1683
    p->pushed_back_char = ch;
1684
}
1685
 
1686
/*
1687
Has been used before at the end of line (during overflow); does not correctly
1688
handle completed input from clipboard (e.g. pasting a single character),
1689
it erroneously provides previous keyboard input (the completed clipboard
1690
input is lost).
1691
 
4394 ripley 1692
static void consoleunputc(control c)
32605 ripley 1693
{
1694
    ConsoleData p = getdata(c);
1695
 
38975 ripley 1696
    if(p->clp) p->pclp--;
1697
    else {
1698
	p->numkeys += 1;
1699
	if (p->firstkey > 0) p->firstkey -= 1;
1700
	else p->firstkey = NKEYS - 1;
1701
    }
32605 ripley 1702
}
87028 kalibera 1703
*/
4394 ripley 1704
 
32605 ripley 1705
/* This scrolls as far left as possible */
4394 ripley 1706
static void checkvisible(control c)
32605 ripley 1707
{
1708
    ConsoleData p = getdata(c);
1709
 
4394 ripley 1710
    int newfc;
1711
 
1712
    setfirstvisible(c, NUMLINES-ROWS);
1713
    newfc = 0;
32605 ripley 1714
    while ((CURCOL <= newfc) || (CURCOL > newfc+COLS-2)) newfc += 5;
4394 ripley 1715
    if (newfc != FC) setfirstcol(c, newfc);
32605 ripley 1716
}
4394 ripley 1717
 
1718
static void draweditline(control c)
32605 ripley 1719
{
1720
    ConsoleData p = getdata(c);
1721
    setCURCOL(p);
4394 ripley 1722
    checkvisible(c);
4605 ripley 1723
    if (p->needredraw) {
45067 ripley 1724
	REDRAW;
11075 ripley 1725
    } else {
70116 murdoch 1726
	WRITELINE(NUMLINES - 1, CURROW);
1727
	RSHOW(RLINE(CURROW));
4605 ripley 1728
    }
32605 ripley 1729
}
4394 ripley 1730
 
45491 ripley 1731
/* This needs to convert the nul-terminated wchar_t string 'in' to a
87027 kalibera 1732
   sensible string in buf.  It must not be empty, as R will
1733
   interpret that as EOF, but 'in' would not be empty. The returned string
1734
   should end in \n when 'in' does. The string is malloc'd by wcstobuf().
45491 ripley 1735
 
1736
   Our strategy is to convert character by character to the current
1737
   Windows locale, using \uxxxx escapes for invalid characters.
1738
*/
1739
 
87027 kalibera 1740
static void wcstobuf(char **buf, const wchar_t *in)
45491 ripley 1741
{
87027 kalibera 1742
    int used;
1743
    char *p, tmp[MB_CUR_MAX];
1744
    const wchar_t *wc;
76370 kalibera 1745
    wchar_t wc_check;
1746
    mbstate_t mb_st;
46677 ripley 1747
 
87027 kalibera 1748
    size_t needed = 0;
1749
    for(wc = in; *wc; wc++) {
1750
	used = wctomb(tmp, *wc);
76370 kalibera 1751
	if (used >= 0) {
1752
	    /* conversion was successful, but check that converting back gets
1753
	       the original result (it does not with best-fit transliteration)
1754
	       NOTE: WideCharToMultiByte may be faster */
1755
	    memset(&mb_st, 0, sizeof(mbstate_t));
87027 kalibera 1756
	    if (mbrtowc(&wc_check, tmp, used, &mb_st) < 0 || wc_check != *wc)
1757
		used = -1;
1758
	}
1759
	if (used < 0)
1760
	    used = 6;  /* \unnnn */
1761
	needed += used;
1762
    }
1763
    needed++; /* \0 */
1764
 
1765
    *buf = malloc(needed);
1766
    if (!*buf) {
1767
	R_ShowMessage(G_("Not enough memory"));
1768
	return;
1769
    }
1770
 
1771
    for(wc = in, p = *buf; *wc; wc++, p+=used) {
1772
	used = wctomb(p, *wc);
1773
	if (used >= 0) {
1774
	    memset(&mb_st, 0, sizeof(mbstate_t));
76370 kalibera 1775
	    if (mbrtowc(&wc_check, p, used, &mb_st) < 0 || wc_check != *wc) 
1776
		used = -1;
1777
	}
45491 ripley 1778
	if (used < 0) {
87027 kalibera 1779
	    /* Note that this hack only works inside string literals, where
1780
	       the parser accepts such escapes. Outside, one gets an error.
1781
	       This is not relevant with UTF-8 as the native encoding. */
1782
	    snprintf(p, 7, "\\u%04x", *wc);
1783
	    used = 6;
45491 ripley 1784
	}
1785
    }
87027 kalibera 1786
    *p = '\0';
45491 ripley 1787
}
1788
 
87027 kalibera 1789
/* len is a suggested number of characters per line. The line or its segment
1790
  is returned in malloc'd buffer as *buf and is zero-terminated.
1791
  The terminator is preceeded by \n only in the last segment of the
1792
  line.
1793
 
1794
  The code may be simpler if changed to reading lines of arbitrary length,
1795
  but getting there might not be worth the effort. */
1796
 
1797
int consolereads0(control c, const char *prompt, char **buf, int len,
43767 ripley 1798
		 int addtohistory)
32605 ripley 1799
{
1800
    ConsoleData p = getdata(c);
1801
 
83482 kalibera 1802
    if (p->cursor_blink == 2)
1803
	/* The caret may be visible via SetUpCaret flag (see newconsole) for
1804
	   startup. It needs to be hidden now to be protected agains redraw. */
1805
	showcaret(c, 0);
43882 ripley 1806
    wchar_t *cur_line, *P;
1807
    wchar_t *aLine;
37599 ripley 1808
    int ns0 = NUMLINES, w0 = 0, pre_prompt_len;
4394 ripley 1809
 
43882 ripley 1810
    pre_prompt_len = wcslen(LINE(NUMLINES - 1));
4394 ripley 1811
    /* print the prompt */
1812
    xbufadds(p->lbuf, prompt, 1);
32605 ripley 1813
    if (!xbufmakeroom(p->lbuf, len + 1)) return 1;
37591 ripley 1814
    P = aLine = LINE(NUMLINES - 1);
43882 ripley 1815
    prompt_len = wcslen(aLine);
1816
    for (; P < aLine + pre_prompt_len; P++)
45067 ripley 1817
	if(*P == L'\r') w0 = 0;
43882 ripley 1818
	else w0 += mbcslocale ? Ri18n_wcwidth(*P) : 1;
1819
    USER(NUMLINES - 1) = w0;
88867 kalibera 1820
    for(; *P; P++)
1821
	if(*P == L'\r') w0 = 0;
1822
	else w0 += mbcslocale ? Ri18n_wcwidth(*P) : 1;
1823
    prompt_wid = w0;
4394 ripley 1824
    if (NUMLINES > ROWS) {
70116 murdoch 1825
	CURROW = ROWS - 1;
1826
	NEWFV = NUMLINES - ROWS;
11075 ripley 1827
    } else {
70116 murdoch 1828
	CURROW = NUMLINES - 1;
1829
	NEWFV = 0;
4394 ripley 1830
    }
32605 ripley 1831
    CURCOL = prompt_wid;
70116 murdoch 1832
    FC = 0;
43882 ripley 1833
    cur_pos = 0;
1834
    max_pos = 0;
4394 ripley 1835
    cur_line = &aLine[prompt_len];
43882 ripley 1836
    cur_line[0] = L'\0';
4394 ripley 1837
    REDRAW;
1838
    for(;;) {
43882 ripley 1839
	wchar_t cur_char;
1840
	char chtype; /* boolean */
11075 ripley 1841
	p->input = 1;
83479 kalibera 1842
	/* The caret must not be shown when drawing, because it would
1843
	   be erased by redraws. We thus only show it when waiting for the
1844
	   keyboard input. REDRAW is also called when processing events,
1845
	   e.g. while the console window is loosing focus, so changing the
1846
	   caret visibility there was error prone. */
1847
	if (p->cursor_blink)
1848
	   showcaret(c, 1);
4394 ripley 1849
	cur_char = consolegetc(c);
83479 kalibera 1850
	showcaret(c, 0);
11075 ripley 1851
	p->input = 0;
43908 ripley 1852
	chtype = ((unsigned int) cur_char > 0x1f);
11075 ripley 1853
	if(NUMLINES != ns0) { /* we scrolled, e.g. cleared screen */
45067 ripley 1854
	    cur_line = LINE(NUMLINES - 1) + prompt_len;
11075 ripley 1855
	    ns0 = NUMLINES;
1856
	    if (NUMLINES > ROWS) {
70116 murdoch 1857
		CURROW = ROWS - 1;
1858
		NEWFV = NUMLINES - ROWS;
11075 ripley 1859
	    } else {
70116 murdoch 1860
		CURROW = NUMLINES - 1;
1861
		NEWFV = 0;
11075 ripley 1862
	    }
37599 ripley 1863
	    USER(NUMLINES - 1) = prompt_wid;
11075 ripley 1864
	    p->needredraw = 1;
10227 ripley 1865
	}
45067 ripley 1866
	if(chtype && (max_pos <= len - 2)) {
38975 ripley 1867
	    /* not a control char: we need to fit in the char\n\0 */
4394 ripley 1868
	    int i;
43882 ripley 1869
	    if(!p->overwrite) {
1870
		for(i = max_pos; i > cur_pos; i--)
1871
		    cur_line[i] = cur_line[i - 1];
4394 ripley 1872
	    }
43882 ripley 1873
	    cur_line[cur_pos] = cur_char;
1874
	    if(!p->overwrite || cur_pos == max_pos) {
1875
		max_pos += 1;
1876
		cur_line[max_pos] = L'\0';
1877
	    }
1878
	    cur_pos++;
32605 ripley 1879
	} else { /* a control char */
4394 ripley 1880
	    /* do normal editing commands */
1881
	    int i;
1882
	    switch(cur_char) {
1883
	    case BEGINLINE:
43882 ripley 1884
		cur_pos = 0;
4394 ripley 1885
		break;
1886
	    case CHARLEFT:
43882 ripley 1887
		if(cur_pos > 0) cur_pos--;
4394 ripley 1888
		break;
1889
	    case ENDLINE:
43882 ripley 1890
		cur_pos = max_pos;
4394 ripley 1891
		break;
1892
	    case CHARRIGHT:
43882 ripley 1893
		if(cur_pos < max_pos) cur_pos ++;
4394 ripley 1894
		break;
1895
	    case KILLRESTOFLINE:
43882 ripley 1896
		max_pos = cur_pos;
1897
		cur_line[max_pos] = L'\0';
4394 ripley 1898
		break;
1899
	    case KILLLINE:
43882 ripley 1900
		max_pos = cur_pos = 0;
1901
		cur_line[max_pos] = L'\0';
4394 ripley 1902
		break;
1903
	    case PREVHISTORY:
43882 ripley 1904
		P = wgl_hist_prev();
1905
		xbufmakeroom(p->lbuf, wcslen(P) + 1);
1906
		wcscpy(cur_line, P);
1907
		cur_pos = max_pos = wcslen(cur_line);
4394 ripley 1908
		break;
1909
	    case NEXTHISTORY:
43882 ripley 1910
		P = wgl_hist_next();
1911
		xbufmakeroom(p->lbuf, wcslen(P) + 1);
1912
		wcscpy(cur_line, P);
1913
		cur_pos = max_pos = wcslen(cur_line);
4394 ripley 1914
		break;
1915
	    case BACKCHAR:
43882 ripley 1916
		if(cur_pos > 0) {
1917
		    cur_pos--;
1918
		    for(i = cur_pos; i <= max_pos - 1; i++)
45067 ripley 1919
			cur_line[i] = cur_line[i + 1];
43882 ripley 1920
		    max_pos--;
4394 ripley 1921
		}
1922
		break;
1923
	    case DELETECHAR:
43882 ripley 1924
		if(max_pos == 0) break;
1925
		if(cur_pos < max_pos) {
1926
		    for(i = cur_pos; i <= max_pos - 1; i++)
1927
			cur_line[i] = cur_line[i + 1];
1928
		    max_pos--;
4394 ripley 1929
		}
1930
		break;
9862 ripley 1931
	    case CHARTRANS:
43882 ripley 1932
		if(cur_pos < 1) break;
1933
		if(cur_pos >= max_pos) break;
45067 ripley 1934
		cur_char = cur_line[cur_pos];
43882 ripley 1935
		cur_line[cur_pos] = cur_line[cur_pos-1];
1936
		cur_line[cur_pos-1] = cur_char;
9862 ripley 1937
		break;
38975 ripley 1938
	    default:   /* Another control char, or overflow */
43882 ripley 1939
		if (chtype || (cur_char == L'\n') || (cur_char == EOFKEY)) {
4394 ripley 1940
		    if (chtype) {
43882 ripley 1941
			if (cur_pos == max_pos) {
87028 kalibera 1942
			    consoleungetc(c, cur_char);
4394 ripley 1943
			} else {
1944
			    gabeep();
1945
			    break;
1946
			}
1947
		    }
43882 ripley 1948
		    if((cur_char == L'\n') || (cur_char == EOFKEY)) {
1949
			cur_line[max_pos] = L'\n';
1950
			cur_line[max_pos + 1] = L'\0';
1951
		    } else
1952
			cur_line[max_pos] = L'\0';
87027 kalibera 1953
		    if (buf)
1954
			wcstobuf(buf, cur_line);
45491 ripley 1955
		    //sprintf(buf, "%ls", cur_line);
1956
		    //if(strlen(buf) == 0) strcpy(buf, "invalid input\n");
70116 murdoch 1957
		    CURROW = -1;
43882 ripley 1958
		    cur_line[max_pos] = L'\0';
1959
		    if (max_pos && addtohistory) wgl_histadd(cur_line);
4394 ripley 1960
		    xbuffixl(p->lbuf);
1961
		    consolewrites(c, "\n");
1962
		    REDRAW;
32605 ripley 1963
		    return cur_char == EOFKEY;
4394 ripley 1964
		}
1965
		break;
1966
	    }
1967
	}
32605 ripley 1968
	draweditline(c);
4394 ripley 1969
    }
32605 ripley 1970
}
4394 ripley 1971
 
87027 kalibera 1972
int consolereads(control c, const char *prompt, char *buf, int len,
1973
		 int addtohistory)
1974
{
1975
    static char *line = NULL;
1976
    static size_t offset = 0;
1977
    static int res = 0;
1978
 
1979
    if (!line) {
1980
	char *segment;
1981
	size_t slen;
1982
	size_t llen;
1983
 
1984
	segment = NULL;
1985
	res = consolereads0(c, prompt, &segment, len, addtohistory);
1986
	if (!segment) {
1987
	    /* error */
1988
	    buf[0] = '\0';
1989
	    return res;
1990
	}
1991
	slen = strlen(segment);
1992
	if (!slen || (segment[slen - 1] == '\n' && slen < len - 1)
1993
	    || len == slen - 1) {
1994
 
1995
	    /* line ends within the segment (common case) or
1996
	       segment is empty (should not happen) or
1997
	       internal segment of line of same length as buffer */
1998
 
87180 kalibera 1999
	    if (slen)
2000
		memcpy(buf, segment, slen);
87027 kalibera 2001
	    free(segment);
2002
	    buf[slen] = '\0';
2003
	    return res;
2004
	}
2005
 
2006
	/* Now read the (rest of) the line and only after that present it
2007
	   to the parser. This is an emulation of the parse loop but is
2008
	   robust to variations in the number of bytes read in each iteration.
2009
 
2010
	   The problem is that consolereads0() cannot easily cap
2011
	   the number of bytes read to an exact boundary, because it was
2012
	   designed to work on wchar_ts (BMP characters): these differ in
2013
	   length in multi-byte representation and they may be replaced
2014
	   by escapes to avoid transliteration.
2015
 
2016
	   This hence captures some of the behavior of the parse loop by
2017
	   assuming a certain way to compute the continuation prompt. Also,
2018
	   it post-pones detection of some parse/lexer errors until the
2019
	   line is finished (so as if the editor allowed entering unlimited
2020
	   lines, but without actually fully supporting editing of such lines)
2021
	*/
2022
	line = segment;
2023
	llen = slen;
2024
	offset = 0;
2025
	segment = NULL;
2026
 
2027
	while(slen && line[llen - 1] != '\n') {
2028
	    const char *cprompt = CHAR(STRING_ELT(
2029
	                              GetOption1(install("continue")), 0));
2030
	    res |= consolereads0(c, cprompt, &segment, len, addtohistory);
2031
	    if (!segment) {
2032
		/* error */
2033
		buf[0] = '\0';
2034
		free(line);
2035
		line = NULL;
2036
		return res;
2037
	    }
2038
 
2039
	    slen = strlen(segment);
2040
	    char *newline = realloc(line, llen + slen + 1);
2041
	    if (!newline) {
2042
		/* allocation error */
2043
		buf[0] = '\0';
2044
		free(segment);
2045
		free(line);
2046
		line = NULL;
2047
		return res;
2048
	    }
2049
	    line = newline;
2050
	    memcpy(line + llen, segment, slen);
2051
	    llen += slen;
2052
	    line[llen] = '\0';
2053
	    free(segment);
2054
	    segment = NULL;
2055
	}
2056
    }
2057
 
2058
    if (line) {
2059
	size_t rlen = strlen(line + offset);
2060
	if (rlen < len) {
2061
	    memcpy(buf, line + offset, rlen);
2062
	    buf[rlen] = '\0';
2063
	    free(line);
2064
	    line = NULL;
2065
	    offset = 0;
2066
	} else {
2067
	    memcpy(buf, line + offset, len - 1);
2068
	    buf[len - 1] = '\0';
2069
	    offset += len - 1;
2070
	}
2071
    }
2072
    return res;
2073
}
2074
 
9212 ripley 2075
void console_sbf(control c, int pos)
32605 ripley 2076
{
2077
    ConsoleData p = getdata(c);
2078
 
7454 ripley 2079
    if (pos < 0) {
2080
	pos = -pos - 1 ;
2081
	if (FC != pos) setfirstcol(c, pos);
24308 ripley 2082
    } else
45067 ripley 2083
	if (FV != pos) setfirstvisible(c, pos);
32605 ripley 2084
}
4394 ripley 2085
 
43908 ripley 2086
void console_im(control c, font *f, point *pt)
2087
{
2088
  ConsoleData p = getdata(c);
2089
  pt->x = BORDERX + CURCOL * FW;
2090
  pt->y = BORDERY + CURROW * FH;
2091
  *f = consolefn;
2092
}
2093
 
4405 ripley 2094
void Rconsolesetwidth(int);
2095
int setWidthOnResize = 0;
4394 ripley 2096
 
4405 ripley 2097
int consolecols(console c)
2098
{
2099
    ConsoleData p = getdata(c);
4645 ripley 2100
 
70116 murdoch 2101
    return COLS;
4405 ripley 2102
}
2103
 
9212 ripley 2104
void consoleresize(console c, rect r)
32605 ripley 2105
{
2106
    ConsoleData p = getdata(c);
2107
 
4394 ripley 2108
    int rr, pcols = COLS;
2109
 
2110
    if (((WIDTH  == r.width) &&
2111
	 (HEIGHT == r.height)) ||
2112
	(r.width == 0) || (r.height == 0) ) /* minimize */
45067 ripley 2113
	return;;
4394 ripley 2114
/*
2115
 *  set first visible to keep the bottom line on a console,
2116
 *  the middle line on a pager
2117
 */
2118
    if (p->kind == CONSOLE) rr = FV + ROWS;
2119
    else rr = FV + ROWS/2;
2120
    ROWS = r.height/FH - 1;
2121
    if (p->kind == CONSOLE) rr -= ROWS;
2122
    else rr -= ROWS/2;
2123
    COLS = r.width/FW - 1;
2124
    WIDTH = r.width;
2125
    HEIGHT = r.height;
2126
    BORDERX = (WIDTH - COLS*FW) / 2;
2127
    BORDERY = (HEIGHT - ROWS*FH) / 2;
70116 murdoch 2128
    NEWFV = NUMLINES - ROWS;
2129
    if (NEWFV < 0) NEWFV = 0;
4394 ripley 2130
    del(BM);
2131
    BM = newbitmap(r.width, r.height, 2);
2132
    if (!BM) {
33001 ripley 2133
       R_ShowMessage(G_("Insufficient memory. Please close the console"));
4394 ripley 2134
       return ;
2135
    }
32606 ripley 2136
    if(!p->lbuf) return;;    /* don't implement resize if no content
6098 pd 2137
				   yet in pager */
70116 murdoch 2138
    if (CURROW >= 0) {
45067 ripley 2139
	if (NUMLINES > ROWS) {
70116 murdoch 2140
	    CURROW = ROWS - 1;
45067 ripley 2141
	} else
70116 murdoch 2142
	    CURROW = NUMLINES - 1;
4394 ripley 2143
    }
2144
    clear(c);
2145
    p->needredraw = 1;
2146
    setfirstvisible(c, rr);
4405 ripley 2147
    if (setWidthOnResize && p->kind == CONSOLE && COLS != pcols)
45067 ripley 2148
	Rconsolesetwidth(COLS);
32605 ripley 2149
}
4394 ripley 2150
 
2151
void consolesetbrk(console c, actionfn fn, char ch, char mod)
32605 ripley 2152
{
2153
    ConsoleData p = getdata(c);
2154
 
4394 ripley 2155
    p->chbrk = ch;
2156
    p->modbrk = mod;
2157
    p->fbrk = fn;
32605 ripley 2158
}
4394 ripley 2159
 
9212 ripley 2160
font consolefn = NULL;
49486 murdoch 2161
char fontname[LF_FACESIZE+4];
9212 ripley 2162
int fontsty, pointsize;
24577 ripley 2163
int consoler = 25, consolec = 80, consolex = 0, consoley = 0;
9212 ripley 2164
int pagerrow = 25, pagercol = 80;
2165
int pagerMultiple = 1, haveusedapager = 0;
37860 ripley 2166
int consolebufb = DIMLBUF, consolebufl = MLBUF, consolebuffered = 1;
61660 ripley 2167
static int consoleblink = 1;
4394 ripley 2168
 
2169
void
43795 ripley 2170
setconsoleoptions(const char *fnname,int fnsty, int fnpoints,
45067 ripley 2171
		  int rows, int cols, int consx, int consy,
46784 murdoch 2172
		  rgb *nguiColors,
11588 ripley 2173
		  int pgr, int pgc, int multiplewindows, int widthonresize,
51948 murdoch 2174
		  int bufbytes, int buflines, int buffered, int cursor_blink)
4394 ripley 2175
{
4448 guido 2176
    char msg[LF_FACESIZE + 128];
4394 ripley 2177
    strncpy(fontname, fnname, LF_FACESIZE);
43882 ripley 2178
    fontname[LF_FACESIZE] = L'\0';
4394 ripley 2179
    fontsty =   fnsty;
2180
    pointsize = fnpoints;
2181
    if (consolefn) del(consolefn);
2182
    consolefn = NULL;
83514 kalibera 2183
    /* keep in step with applyGUI() in preferences.c */
36950 ripley 2184
    if (strcmp(fontname, "FixedFont")) {
44575 ripley 2185
	consolefn = gnewfont(NULL, fnname, fnsty | FixedWidth, fnpoints, 0.0, 1);
36950 ripley 2186
	if (!consolefn) {
2187
	    /* This is unlikely to happen: it will find some match */
62583 ripley 2188
	    snprintf(msg, LF_FACESIZE + 128,
2189
		     G_("Font %s-%d-%d  not found.\nUsing system fixed font"),
2190
		     fontname, fontsty | FixedWidth, pointsize);
36950 ripley 2191
	    R_ShowMessage(msg);
2192
	    consolefn = FixedFont;
2193
	}
4448 guido 2194
    }
32589 ripley 2195
/*    if (!ghasfixedwidth(consolefn)) {
4645 ripley 2196
       sprintf(msg,
4605 ripley 2197
	       "Font %s-%d-%d has variable width.\nUsing system fixed font.",
45067 ripley 2198
	       fontname, fontsty, pointsize);
4649 ripley 2199
       R_ShowMessage(msg);
4394 ripley 2200
       consolefn = FixedFont;
32589 ripley 2201
       } */
4394 ripley 2202
    consoler = rows;
2203
    consolec = cols;
24577 ripley 2204
    consolex = consx;
2205
    consoley = consy;
46784 murdoch 2206
    for (int i=0; i<numGuiColors; i++)
46826 ripley 2207
	guiColors[i] = nguiColors[i];
4394 ripley 2208
    pagerrow = pgr;
2209
    pagercol = pgc;
2210
    pagerMultiple = multiplewindows;
2211
    setWidthOnResize = widthonresize;
11588 ripley 2212
    consolebufb = bufbytes;
2213
    consolebufl = buflines;
37860 ripley 2214
    consolebuffered = buffered;
51948 murdoch 2215
    consoleblink = cursor_blink;
4394 ripley 2216
}
2217
 
2218
void consoleprint(console c)
32605 ripley 2219
{
2220
    ConsoleData p = getdata(c);
24308 ripley 2221
 
32605 ripley 2222
 
10227 ripley 2223
    printer lpr;
2224
    int cc, rr, fh, cl, cp, clinp, i;
2225
    int top, left;
2226
    int x0, y0, x1, y1;
2227
    font f;
43882 ripley 2228
    wchar_t *s = L"";
2229
    char msg[LF_FACESIZE + 128], title[60];
2230
    wchar_t buf[1024];
10227 ripley 2231
    cursor cur;
32606 ripley 2232
    if (!(lpr = newprinter(0.0, 0.0, ""))) return;;
10227 ripley 2233
    show(c);
4394 ripley 2234
/*
2235
 * If possible, we avoid to use FixedFont for printer since it hasn't the
2236
 * right size
10227 ripley 2237
 */
2238
    f = gnewfont(lpr, strcmp(fontname, "FixedFont") ? fontname : "Courier New",
44575 ripley 2239
		 fontsty, pointsize, 0.0, 1);
10227 ripley 2240
    if (!f) {
2241
	/* Should not happen but....*/
62583 ripley 2242
	snprintf(msg, LF_FACESIZE + 128,
2243
		 G_("Font %s-%d-%d  not found.\nUsing system fixed font"),
2244
		 strcmp(fontname, "FixedFont") ? fontname : "Courier New",
2245
		 fontsty, pointsize);
10227 ripley 2246
	R_ShowMessage(msg);
2247
	f = FixedFont;
2248
    }
2249
    top = devicepixelsy(lpr) / 5;
2250
    left = devicepixelsx(lpr) / 5;
2251
    fh = fontheight(f);
2252
    rr = getheight(lpr) - top;
2253
    cc = getwidth(lpr) - 2*left;
39274 ripley 2254
    strncpy(title, GA_gettext(c), 59);
2255
    if (strlen(GA_gettext(c)) > 59) strcpy(&title[56], "...");
10227 ripley 2256
    cur = currentcursor();
2257
    setcursor(WatchCursor);
2258
 
2259
    /* Look for a selection */
2260
    if (p->sel) {
2261
	int len, c1, c2, c3;
2262
	if (p->my0 >= NUMLINES) p->my0 = NUMLINES - 1;
2263
	if (p->my0 < 0) p->my0 = 0;
43882 ripley 2264
	len = wcslen(LINE(p->my0));
10227 ripley 2265
	if (p->mx0 >= len) p->mx0 = len - 1;
2266
	if (p->mx0 < 0) p->mx0 = 0;
2267
	if (p->my1 >= NUMLINES) p->my1 = NUMLINES - 1;
2268
	if (p->my1 < 0) p->my1 = 0;
43882 ripley 2269
	len = wcslen(LINE(p->my1));
10227 ripley 2270
	if (p->mx1 >= len) p->mx1 = len - 1;
2271
	if (p->mx1 < 0) p->mx1 = 0;
2272
	c1 = (p->my0 < p->my1);
2273
	c2 = (p->my0 == p->my1);
2274
	c3 = (p->mx0 < p->mx1);
2275
	if (c1 || (c2 && c3)) {
2276
	    x0 = p->mx0; y0 = p->my0;
2277
	    x1 = p->mx1; y1 = p->my1;
2278
	}
2279
	else {
2280
	    x0 = p->mx1; y0 = p->my1;
2281
	    x1 = p->mx0; y1 = p->my0;
2282
	}
2283
    } else {
2284
	x0 = y0 = 0;
2285
	y1 = NUMLINES - 1;
43882 ripley 2286
	x1 = wcslen(LINE(y1));
10227 ripley 2287
    }
2288
 
2289
    cl = y0; /* current line */
2290
    clinp = rr;
2291
    cp = 1; /* current page */
2292
 
2293
    /* s is possible continuation line */
2294
    while ((cl <= y1) || (*s)) {
2295
	if (clinp + fh >= rr) {
2296
	    if (cp > 1) nextpage(lpr);
2297
	    gdrawstr(lpr, f, Black, pt(left, top), title);
62583 ripley 2298
	    snprintf(msg, LF_FACESIZE + 128, "Page %d", cp++);
24308 ripley 2299
	    gdrawstr(lpr, f, Black,
45067 ripley 2300
		     pt(cc - gstrwidth(lpr, f, msg) - 1, top),
10227 ripley 2301
		     msg);
2302
	    clinp = top + 2 * fh;
2303
	}
2304
	if (!*s) {
45067 ripley 2305
	    if (cl == y0) s = LINE(cl++) + x0;
10227 ripley 2306
	    else if (cl < y1) s = LINE(cl++);
2307
	    else if (cl == y1) {
43882 ripley 2308
		s = wcsncpy(buf, LINE(cl++), 1023);
2309
		s[min(x1, 1023) + 1] = L'\0';
10227 ripley 2310
	    } else break;
2311
	}
2312
	if (!*s) {
2313
	    clinp += fh;
2314
	} else {
43882 ripley 2315
	    wchar_t lc = L'\0';
2316
	    for (i = wcslen(s); i > 0; i--) {
10227 ripley 2317
		lc = s[i];
43882 ripley 2318
		s[i] = L'\0';
2319
		if (gwcswidth(lpr, f, s) < cc) break;
10227 ripley 2320
		s[i] = lc;
2321
	    }
43882 ripley 2322
	    gdrawwcs(lpr, f, Black, pt(left, clinp), s);
10227 ripley 2323
	    clinp += fh;
2324
	    s[i] = lc;
2325
	    s = s + i;
2326
	}
2327
    }
2328
 
2329
    if (f != FixedFont) del(f);
2330
    del(lpr);
2331
    setcursor(cur);
32605 ripley 2332
}
4394 ripley 2333
 
46842 ripley 2334
FILE *R_wfopen(const wchar_t *filename, const wchar_t *mode);
2335
 
17261 ripley 2336
void consolesavefile(console c, int pager)
32605 ripley 2337
{
2338
    ConsoleData p = getdata(c);
2339
 
46842 ripley 2340
    wchar_t *fn;
10976 ripley 2341
    cursor cur;
2342
    FILE *fp;
2343
    int x0, y0, x1, y1, cl;
43882 ripley 2344
    wchar_t *s, buf[1024];
10976 ripley 2345
 
48428 murdoch 2346
    setuserfilterW(L"Text files (*.txt)\0*.txt\0All files (*.*)\0*.*\0\0");
24308 ripley 2347
    if(p->sel)
46842 ripley 2348
	fn = askfilesaveW(G_("Save selection to"), "lastsave.txt");
10976 ripley 2349
    else
46842 ripley 2350
	fn = askfilesaveW(G_("Save console contents to"), "lastsave.txt");
10976 ripley 2351
    show(c);
2352
    if (fn) {
46842 ripley 2353
	fp = R_wfopen(fn, L"wt");
10976 ripley 2354
	if (!fp) return;
2355
	cur = currentcursor();
2356
	setcursor(WatchCursor);
2357
 
2358
	/* Look for a selection */
2359
	if (p->sel) {
2360
	    int len, c1, c2, c3;
2361
	    if (p->my0 >= NUMLINES) p->my0 = NUMLINES - 1;
2362
	    if (p->my0 < 0) p->my0 = 0;
43882 ripley 2363
	    len = wcslen(LINE(p->my0));
10976 ripley 2364
	    if (p->mx0 >= len) p->mx0 = len - 1;
2365
	    if (p->mx0 < 0) p->mx0 = 0;
2366
	    if (p->my1 >= NUMLINES) p->my1 = NUMLINES - 1;
2367
	    if (p->my1 < 0) p->my1 = 0;
43882 ripley 2368
	    len = wcslen(LINE(p->my1));
10976 ripley 2369
	    if (p->mx1 >= len) p->mx1 = len - 1;
2370
	    if (p->mx1 < 0) p->mx1 = 0;
2371
	    c1 = (p->my0 < p->my1);
2372
	    c2 = (p->my0 == p->my1);
2373
	    c3 = (p->mx0 < p->mx1);
2374
	    if (c1 || (c2 && c3)) {
2375
		x0 = p->mx0; y0 = p->my0;
2376
		x1 = p->mx1; y1 = p->my1;
2377
	    }
2378
	    else {
2379
		x0 = p->mx1; y0 = p->my1;
2380
		x1 = p->mx0; y1 = p->my0;
2381
	    }
2382
	} else {
2383
	    x0 = y0 = 0;
2384
	    y1 = NUMLINES - 1;
43882 ripley 2385
	    x1 = wcslen(LINE(y1));
10976 ripley 2386
	}
2387
 
2388
	for (cl = y0; cl <= y1; cl++) {
2389
	    if (cl == y0) s = LINE(cl) + x0;
2390
	    else if (cl < y1) s = LINE(cl);
2391
	    else if (cl == y1) {
43882 ripley 2392
		s = wcsncpy(buf, LINE(cl), 1023);
2393
		s[min(x1, 1023) + 1] = L'\0';
10976 ripley 2394
	    } else break;
43882 ripley 2395
	    fputws(s, fp); fputc('\n', fp);
10976 ripley 2396
	}
2397
	fclose(fp);
2398
	setcursor(cur);
2399
    }
32605 ripley 2400
}
10976 ripley 2401
 
2402
 
4394 ripley 2403
console newconsole(char *name, int flags)
2404
{
2405
    console c;
2406
    ConsoleData p;
2407
 
2408
    p = newconsoledata((consolefn) ? consolefn : FixedFont,
11588 ripley 2409
		       consoler, consolec, consolebufb, consolebufl,
46784 murdoch 2410
		       guiColors,
51948 murdoch 2411
		       CONSOLE, consolebuffered, consoleblink);
4394 ripley 2412
    if (!p) return NULL;
83482 kalibera 2413
    if (p->cursor_blink == 2)
2414
	/* Arrange for a dummy caret to be created when the new window
2415
	   gets focus. This is to help NVDA (screen reader) to choose
2416
	   a correct display model and correctly detect characters
2417
	   under the caret. After the first redraw, which will happen
2418
	   right away in consolereads, a correct caret will be set up
2419
	   and destroyed/restored when loosing/gaining focus. */
2420
	flags |= SetUpCaret;
24577 ripley 2421
    c = (console) newwindow(name, rect(consolex, consoley, WIDTH, HEIGHT),
7454 ripley 2422
			    flags | TrackMouse | VScrollbar | HScrollbar);
4394 ripley 2423
    HEIGHT = getheight(c);
2424
    WIDTH  = getwidth(c);
2425
    COLS = WIDTH / FW - 1;
2426
    ROWS = HEIGHT / FH - 1;
4605 ripley 2427
    gsetcursor(c, ArrowCursor);
4394 ripley 2428
    gchangescrollbar(c, VWINSB, 0, 0, ROWS, 1);
7454 ripley 2429
    gchangescrollbar(c, HWINSB, 0, COLS-1, COLS, 1);
4394 ripley 2430
    BORDERX = (WIDTH - COLS*FW) / 2;
2431
    BORDERY = (HEIGHT - ROWS*FH) / 2;
46784 murdoch 2432
    setbackground(c, guiColors[consolebg]);
4394 ripley 2433
    BM = newbitmap(WIDTH, HEIGHT, 2);
2434
    if (!c || !BM ) {
2435
	freeConsoleData(p);
2436
	del(c);
2437
	return NULL;
2438
    }
2439
    setdata(c, p);
9212 ripley 2440
    sethit(c, console_sbf);
4394 ripley 2441
    setresize(c, consoleresize);
2442
    setredraw(c, drawconsole);
2443
    setdel(c, delconsole);
9212 ripley 2444
    setkeyaction(c, console_ctrlkeyin);
2445
    setkeydown(c, console_normalkeyin);
2446
    setmousedrag(c, console_mousedrag);
2447
    setmouserepeat(c, console_mouserep);
2448
    setmousedown(c, console_mousedown);
43908 ripley 2449
    setim(c, console_im);
4394 ripley 2450
    return(c);
2451
}
2452
 
84952 kalibera 2453
void  consolehelp(void)
4394 ripley 2454
{
2455
    char s[4096];
2456
 
33093 ripley 2457
    strcpy(s,G_("Scrolling.\n"));
2458
    strcat(s,G_("  Keyboard: PgUp, PgDown, Ctrl+Arrows, Ctrl+Home, Ctrl+End,\n"));
2459
    strcat(s,G_("  Mouse: use the scrollbar(s).\n\n"));
2460
    strcat(s,G_("Editing.\n"));
2461
    strcat(s,G_("  Moving the cursor: \n"));
2462
    strcat(s,G_("     Left arrow or Ctrl+B: move backward one character;\n"));
2463
    strcat(s,G_("     Right arrow or Ctrl+F: move forward one character;\n"));
2464
    strcat(s,G_("     Home or Ctrl+A: go to beginning of line;\n"));
2465
    strcat(s,G_("     End or Ctrl+E: go to end of line;\n"));
2466
    strcat(s,G_("  History: Up and Down Arrows, Ctrl+P, Ctrl+N\n"));
2467
    strcat(s,G_("  Deleting:\n"));
41380 murdoch 2468
    strcat(s,G_("     Del or Ctrl+D: delete current character or selection;\n"));
33093 ripley 2469
    strcat(s,G_("     Backspace: delete preceding character;\n"));
2470
    strcat(s,G_("     Ctrl+Del or Ctrl+K: delete text from current character to end of line.\n"));
2471
    strcat(s,G_("     Ctrl+U: delete all text from current line.\n"));
2472
    strcat(s,G_("  Copy and paste.\n"));
2473
    strcat(s,G_("     Use the mouse (with the left button held down) to mark (select) text.\n"));
2474
    strcat(s,G_("     Use Shift+Del (or Ctrl+C) to copy the marked text to the clipboard and\n"));
2475
    strcat(s,G_("     Shift+Ins (or Ctrl+V or Ctrl+Y) to paste the content of the clipboard (if any)  \n"));
2476
    strcat(s,G_("     to the console, Ctrl+X first copy then paste\n"));
2477
    strcat(s,G_("  Misc:\n"));
2478
    strcat(s,G_("     Ctrl+L: Clear the console.\n"));
41380 murdoch 2479
    strcat(s,G_("     Ctrl+O or INS: Toggle overwrite mode: initially off.\n"));
33093 ripley 2480
    strcat(s,G_("     Ctrl+T: Interchange current char with one to the left.\n"));
2481
    strcat(s,G_("\nNote: Console is updated only when some input is required.\n"));
2482
    strcat(s,G_("  Use Ctrl+W to toggle this feature off/on.\n\n"));
2483
    strcat(s,G_("Use ESC to stop the interpreter.\n\n"));
41096 ripley 2484
    strcat(s,G_("TAB starts completion of the current word.\n\n"));
33093 ripley 2485
    strcat(s,G_("Standard Windows hotkeys can be used to switch to the\n"));
2486
    strcat(s,G_("graphics device (Ctrl+Tab or Ctrl+F6 in MDI, Alt+Tab in SDI)"));
4394 ripley 2487
    askok(s);
2488
}
2489
 
10227 ripley 2490
void consoleclear(control c)
32605 ripley 2491
{
2492
    ConsoleData p = getdata(c);
2493
 
10227 ripley 2494
    xbuf l = p->lbuf;
2495
    int oldshift = l->shift;
11075 ripley 2496
 
10227 ripley 2497
    l->shift = (l->ns - 1);
2498
    xbufshift(l);
2499
    l->shift = oldshift;
2500
    NEWFV = 0;
70116 murdoch 2501
    CURROW = 0;
10227 ripley 2502
    REDRAW;
32605 ripley 2503
}