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