The R Project SVN R

Rev

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