The R Project SVN R

Rev

Rev 63181 | Rev 64107 | 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
62583 ripley 6
 *  Copyright (C) 2004-13     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
20
 *  http://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);
9212 ripley 33
 
36901 ripley 34
#define WIN32_LEAN_AND_MEAN 1
4394 ripley 35
#include <windows.h>
36
#include <string.h>
37
#include <ctype.h>
32578 ripley 38
#include <wchar.h>
35776 ripley 39
#include <limits.h>
60260 ripley 40
#include <rlocale.h>
55057 murdoch 41
#include <R_ext/Memory.h>
4394 ripley 42
#include "graphapp/ga.h"
9212 ripley 43
#ifdef USE_MDI
4394 ripley 44
#include "graphapp/stdimg.h"
9212 ripley 45
#endif
4394 ripley 46
#include "console.h"
9044 ripley 47
#include "consolestructs.h"
4394 ripley 48
#include "rui.h"
43882 ripley 49
#include "getline/wc_history.h"
10345 ripley 50
#include "Startup.h" /* for UImode */
40717 ripley 51
#include <Fileio.h>
4394 ripley 52
 
51627 ripley 53
#include <stdint.h>
47852 ripley 54
 
45213 ripley 55
/* Surrogate Pairs Macro */
56
#define SURROGATE_PAIRS_HI_MIN  ((uint16_t)0xd800)
57
#define SURROGATE_PAIRS_HI_MAX  ((uint16_t)0xdbff)
58
#define SURROGATE_PAIRS_LO_MIN  ((uint16_t)0xdc00)
59
#define SURROGATE_PAIRS_LO_MAX  ((uint16_t)0xdfff)
60
#define SURROGATE_PAIRS_BIT_SZ  ((uint32_t)10)
61
#define SURROGATE_PAIRS_MASK    (((uint16_t)1 << SURROGATE_PAIRS_BIT_SZ)-1)
62
#define IsSurrogatePairsHi(_h)  (SURROGATE_PAIRS_HI_MIN == \
45214 ripley 63
		      ((uint16_t)(_h) &~ (uint16_t)SURROGATE_PAIRS_MASK ))
45213 ripley 64
#define IsSurrogatePairsLo(_l)  (SURROGATE_PAIRS_LO_MIN == \
45214 ripley 65
		      ((uint16_t)(_l) &~ (uint16_t)SURROGATE_PAIRS_MASK ))
45213 ripley 66
 
53662 ripley 67
#ifdef __GNUC__
68
# undef alloca
69
# define alloca(x) __builtin_alloca((x))
70
#endif
71
 
10345 ripley 72
extern UImode  CharacterMode;
4394 ripley 73
 
40959 ripley 74
static void performCompletion(control c);
75
 
32589 ripley 76
 
43882 ripley 77
static inline int wcswidth(const wchar_t *s)
32578 ripley 78
{
43882 ripley 79
    return mbcslocale ? Ri18n_wcswidth(s, wcslen(s)) : wcslen(s);
32578 ripley 80
}
32605 ripley 81
 
43882 ripley 82
static inline int wcwidth(const wchar_t s)
32605 ripley 83
{
43882 ripley 84
    return mbcslocale ? Ri18n_wcwidth(s) : 1;
32605 ripley 85
}
32578 ripley 86
 
43882 ripley 87
static void setCURCOL(ConsoleData p)
32605 ripley 88
{
43882 ripley 89
    wchar_t *P = LINE(NUMLINES - 1);
37591 ripley 90
    int w0 = 0;
91
 
43882 ripley 92
    for (; P < LINE(NUMLINES - 1) + prompt_len + cur_pos; P++)
93
	if(*P == L'\r') w0 = 0; else w0 += wcwidth(*P);
37591 ripley 94
 
95
    CURCOL = w0;
32605 ripley 96
}
97
 
98
 
4394 ripley 99
/* xbuf */
100
 
9212 ripley 101
xbuf newxbuf(xlong dim, xint ms, xint shift)
4394 ripley 102
{
103
    xbuf  p;
104
 
11169 ripley 105
    p = (xbuf) malloc(sizeof(struct structXBUF));
4394 ripley 106
    if (!p)
107
	return NULL;
43882 ripley 108
    p->b = (wchar_t *) malloc((dim + 1) * sizeof(wchar_t));
4394 ripley 109
    if (!p->b) {
11169 ripley 110
	free(p);
4394 ripley 111
	return NULL;
112
    }
11169 ripley 113
    p->user = (int *) malloc(ms * sizeof(int));
4394 ripley 114
    if (!p->user) {
11169 ripley 115
	free(p->b);
116
	free(p);
4394 ripley 117
	return NULL;
118
    }
43882 ripley 119
    p->s = (wchar_t **) malloc(ms * sizeof(wchar_t *));
4394 ripley 120
    if (!p->s) {
11169 ripley 121
	free(p->b);
122
	free(p->user);
123
	free(p);
4394 ripley 124
	return NULL;
125
    }
126
    p->ns = 1;
127
    p->ms = ms;
128
    p->shift = shift;
129
    p->dim = dim;
130
    p->av = dim;
131
    p->free = p->b;
132
    p->s[0] = p->b;
133
    p->user[0] = -1;
43882 ripley 134
    *p->b = L'\0';
4394 ripley 135
    return p;
136
}
137
 
11588 ripley 138
/* reallocate increased buffer sizes and update pointers */
139
void xbufgrow(xbuf p, xlong dim, xint ms)
140
{
141
    if(dim > p->dim) {
45349 ripley 142
	wchar_t *ret = (wchar_t *) realloc(p->b, (dim + 1)*sizeof(wchar_t));
11588 ripley 143
	if(ret) {
45349 ripley 144
	    int i, change;
145
	    change = ret - p->b;
146
	    p->b = ret;
11588 ripley 147
	    p->av += change;
148
	    p->free += change;
149
	    for (i = 0; i < p->ns; i++)  p->s[i] += change;
150
	    p->dim = dim;
151
	}
152
    }
153
    if(ms > p->ms) {
45349 ripley 154
	wchar_t **ret = (wchar_t **) realloc(p->s, ms * sizeof(wchar_t *));
11588 ripley 155
	if(ret) {
45349 ripley 156
	    int *ret2 = (int *) realloc(p->user, ms * sizeof(int));
11588 ripley 157
	    if(ret2) {
45349 ripley 158
		p->s = ret;
159
		p->user = ret2;
11588 ripley 160
		p->ms = ms;
161
	    }
162
	}
163
    }
164
}
165
 
24308 ripley 166
void xbufdel(xbuf p)
9212 ripley 167
{
4394 ripley 168
   if (!p) return;
11169 ripley 169
   free(p->s);
170
   free(p->b);
171
   free(p->user);
172
   free(p);
4394 ripley 173
}
174
 
175
static void xbufshift(xbuf p)
176
{
177
    xint  i;
178
    xlong mshift;
43882 ripley 179
    wchar_t *new0;
4394 ripley 180
 
181
    if (p->shift >= p->ns) {
182
	p->ns = 1;
183
	p->av = p->dim;
184
	p->free = p->b;
185
	p->s[0] = p->b;
43882 ripley 186
	*p->b = L'\0';
4394 ripley 187
	p->user[0] = -1;
188
	return;
189
    }
190
    new0 = p->s[p->shift];
191
    mshift = new0 - p->s[0];
43882 ripley 192
    memmove(p->b, p->s[p->shift], (p->dim - mshift) * sizeof(wchar_t));
4394 ripley 193
    memmove(p->user, &p->user[p->shift], (p->ms - p->shift) * sizeof(int));
194
    for (i = p->shift; i < p->ns; i++)
195
	p->s[i - p->shift] = p->s[i] - mshift;
196
    p->ns = p->ns - p->shift;
197
    p->free -= mshift;
198
    p->av += mshift;
199
}
200
 
7913 ripley 201
static int xbufmakeroom(xbuf p, xlong size)
4394 ripley 202
{
203
    if (size > p->dim) return 0;
8505 pd 204
    while ((p->av < size) || (p->ns == p->ms)) {
4394 ripley 205
	xbufshift(p);
8505 pd 206
    }
4394 ripley 207
    p->av -= size;
208
    return 1;
209
}
210
 
8505 pd 211
#define XPUTC(c) {xbufmakeroom(p,1); *p->free++=c;}
4394 ripley 212
 
43882 ripley 213
void xbufaddxc(xbuf p, wchar_t c)
4394 ripley 214
{
215
    int   i;
216
 
217
    switch (c) {
43882 ripley 218
    case L'\a':
4394 ripley 219
	gabeep();
220
	break;
43882 ripley 221
    case L'\b':
222
	if ((p->s[p->ns - 1])[0]) {
223
	    p->free--;
224
	    p->av++;
4394 ripley 225
	}
7913 ripley 226
	break;
43882 ripley 227
    case L'\t':
228
	XPUTC(L' ');
8093 ripley 229
	*p->free = '\0';
43882 ripley 230
	/* Changed to  width in 2.7.0 */
231
	for (i = wcswidth(p->s[p->ns - 1]); (i % TABSIZE); i++) XPUTC(L' ');
4394 ripley 232
	break;
43882 ripley 233
    case L'\n':
234
	XPUTC(L'\0');
4394 ripley 235
	p->s[p->ns] = p->free;
8093 ripley 236
	p->user[p->ns++] = -1;
4394 ripley 237
	break;
37591 ripley 238
    default:
4394 ripley 239
	XPUTC(c);
240
    }
43882 ripley 241
    *p->free = L'\0';
4394 ripley 242
}
243
 
43882 ripley 244
void xbufaddxs(xbuf p, const wchar_t *s, int user)
4394 ripley 245
{
43882 ripley 246
    const wchar_t *ps;
4394 ripley 247
    int   l;
248
 
43882 ripley 249
    l = user ? (p->s[p->ns - 1])[0] : -1;
250
    for (ps = s; *ps; ps++) xbufaddxc(p, *ps);
4394 ripley 251
    p->user[p->ns - 1] = l;
252
}
253
 
44069 ripley 254
#define IN_CONSOLE
255
#include "rgui_UTF8.h"
44027 ripley 256
extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);
257
static size_t enctowcs(wchar_t *wc, char *s, int n)
258
{
259
    size_t nc = 0;
260
    char *pb, *pe;
44069 ripley 261
    if((pb = strchr(s, UTF8in[0])) && *(pb+1) == UTF8in[1] &&
262
       *(pb+2) == UTF8in[2]) {
44027 ripley 263
	*pb = '\0';
264
	nc += mbstowcs(wc, s, n);
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));
273
	pe += 3;
274
	nc += enctowcs(wc+nc, pe, n-nc);
275
    } else nc = mbstowcs(wc, s, n);
276
    return nc;
277
}
278
 
43882 ripley 279
static void xbufadds(xbuf p, const char *s, int user)
280
{
63222 ripley 281
    int n = strlen(s) + 1; /* UCS-2 must be no more chars */
57573 ripley 282
    if (n < 1000) {
283
	wchar_t tmp[n];
284
	enctowcs(tmp, (char *) s, n);
285
	xbufaddxs(p, tmp, user);
286
    } else {
287
	/* very long line */
63222 ripley 288
	wchar_t *tmp = (wchar_t*) malloc(n * sizeof(wchar_t));
57573 ripley 289
	enctowcs(tmp, (char *) s, n);
290
	xbufaddxs(p, tmp, user);
63222 ripley 291
	free(tmp);
57573 ripley 292
    }
43882 ripley 293
}
294
 
4394 ripley 295
static void xbuffixl(xbuf p)
296
{
55061 ripley 297
    wchar_t *ps;
4394 ripley 298
 
43882 ripley 299
    if (!p->ns) return;
4394 ripley 300
    ps = p->s[p->ns - 1];
43882 ripley 301
    p->free = ps + wcslen(ps);
4394 ripley 302
    p->av = p->dim - (p->free - p->b);
303
}
304
 
305
 
306
/* console */
307
 
46826 ripley 308
rgb guiColors[numGuiColors] = {
46784 murdoch 309
	White, Black, gaRed, /* consolebg, consolefg, consoleuser, */
310
	White, Black, gaRed, /* pagerbg, pagerfg, pagerhighlight,  */
311
	White, Black, gaRed, /* dataeditbg, dataeditfg, dataedituser */
312
	White, Black         /* editorbg, editorfg                 */
313
};
8081 ripley 314
 
9860 ripley 315
extern int R_HistorySize;  /* from Defn.h */
316
 
9212 ripley 317
ConsoleData
11588 ripley 318
newconsoledata(font f, int rows, int cols, int bufbytes, int buflines,
51948 murdoch 319
	       rgb *guiColors, int kind, int buffered, int cursor_blink)
4394 ripley 320
{
321
    ConsoleData p;
322
 
323
    initapp(0, 0);
11169 ripley 324
    p = (ConsoleData) malloc(sizeof(struct structConsoleData));
4394 ripley 325
    if (!p)
326
	return NULL;
327
    p->kind = kind;
56884 ripley 328
    /* PR#14624 claimed this was needed, with no example */
56888 ripley 329
    p->chbrk = p->modbrk = '\0';
4394 ripley 330
    if (kind == CONSOLE) {
11588 ripley 331
	p->lbuf = newxbuf(bufbytes, buflines, SLBUF);
4394 ripley 332
	if (!p->lbuf) {
11169 ripley 333
	    free(p);
4394 ripley 334
	    return NULL;
335
	}
45067 ripley 336
	p->kbuf = malloc(NKEYS * sizeof(wchar_t));
4394 ripley 337
	if (!p->kbuf) {
338
	    xbufdel(p->lbuf);
11169 ripley 339
	    free(p);
4394 ripley 340
	    return NULL;
341
	}
342
    } else {
343
	p->lbuf = NULL;
344
	p->kbuf = NULL;
345
    }
346
    p->bm = NULL;
347
    p->rows = rows;
348
    p->cols = cols;
46784 murdoch 349
    for (int i=0; i<numGuiColors; i++)
46826 ripley 350
	p->guiColors[i] = guiColors[i];
4394 ripley 351
    p->f = f;
352
    FH = fontheight(f);
353
    FW = fontwidth(f);
354
    WIDTH = (COLS + 1) * FW;
8707 ripley 355
    HEIGHT = (ROWS + 1) * FH + 1; /* +1 avoids size problems in MDI */
4394 ripley 356
    FV = FC = 0;
357
    p->newfv = p->newfc = 0;
358
    p->firstkey = p->numkeys = 0;
359
    p->clp = NULL;
360
    p->r = -1;
9862 ripley 361
    p->overwrite = 0;
4394 ripley 362
    p->lazyupdate = 1;
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);
432
    gfillrect(p->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';
45067 ripley 466
	    gdrawwcs(p->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';
477
	    gdrawwcs(p->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 */
554
    if ((p->r >= 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);
565
	    	gfillrect(p->bm, highlight, r);
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);
586
	    	gfillrect(p->bm, highlight, r);
587
	    	gdrawwcs(p->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);
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);
41381 ripley 911
    int i, alen, alen2, max_show = 10, cursor_position = p->c - 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
 
40959 ripley 950
    /* FIXME: need to escape quotes properly */
55057 murdoch 951
    wchar_t *pline = (wchar_t*) R_alloc(wcslen(partial_line) + 1, sizeof(wchar_t));
43882 ripley 952
    wcscpy(pline, partial_line);
40927 ripley 953
    /* poor attempt at escaping quotes that sort of works */
43882 ripley 954
    alen = wcslen(pline);
40927 ripley 955
    for (i = 0; i < alen; i++)
45067 ripley 956
	if (pline[i] == '"') pline[i] = L'\'';
40959 ripley 957
 
62583 ripley 958
    size_t len = wcslen(pline) + 100; 
959
    char cmd[len];
960
    snprintf(cmd, len, "utils:::.win32consoleCompletion(\"%ls\", %d)",
961
	     pline, cursor_position);
40927 ripley 962
    PROTECT(cmdSexp = mkString(cmd));
963
    cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
964
    if (status != PARSE_OK) {
965
	UNPROTECT(2);
966
	/* Uncomment next line to debug */
967
	/* Rprintf("failed: %s \n", cmd); */
968
	/* otherwise pretend that nothing happened and return */
969
	return;
970
    }
971
    /* Loop is needed here as EXPSEXP will be of length > 1 */
972
    for(i = 0; i < length(cmdexpr); i++)
973
	ans = eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
974
    UNPROTECT(2);
975
 
976
    /* ans has the form list(addition, possible), where 'addition' is
977
       unique additional text if any, and 'possible' is a character
978
       vector holding possible completions if any (already formatted
979
       for linewise printing in the current implementation).  If
980
       'possible' has any content, we want to print those (or show in
981
       status bar or whatever).  Otherwise add the 'additional' text
982
       at the cursor */
983
 
984
#define ADDITION 0
985
#define POSSIBLE 1
986
 
987
    alen = length(VECTOR_ELT(ans, POSSIBLE));
41381 ripley 988
    additional_text = CHAR(STRING_ELT( VECTOR_ELT(ans, ADDITION), 0 ));
989
    alen2 = strlen(additional_text);
40927 ripley 990
    if (alen) {
45067 ripley 991
	/* make a copy of the current string first */
43882 ripley 992
	wchar_t *p1 = LINE(NUMLINES - 1);
41381 ripley 993
	checkpointpos(p->lbuf, 1);
62583 ripley 994
	size_t len = MB_CUR_MAX * wcslen(p1) + 1; 
995
	char buf1[len];
996
	snprintf(buf1, len, "%ls\n", p1);
40959 ripley 997
	consolewrites(c, buf1);
998
 
40927 ripley 999
	for (i = 0; i < min(alen, max_show); i++) {
1000
	    consolewrites(c, CHAR(STRING_ELT(VECTOR_ELT(ans, POSSIBLE), i)));
1001
	    consolewrites(c, "\n");
1002
	}
1003
	if (alen > max_show)
1004
	    consolewrites(c, "\n[...truncated]\n");
41381 ripley 1005
	p->wipe_completion = 1;
40927 ripley 1006
    }
41381 ripley 1007
 
45067 ripley 1008
    if (alen2)
41381 ripley 1009
	for (i = 0; i < alen2; i++) storekey(c, additional_text[i]);
40927 ripley 1010
    return;
1011
}
1012
 
41380 murdoch 1013
/* deletes that part of the selection which is on the input line */
1014
static void deleteselected(ConsoleData p)
1015
{
1016
    if (p->sel) {
45067 ripley 1017
	int s0, s1;
1018
	wchar_t *cur_line;
1019
	if (intersect_input(p, 1)) {
41380 murdoch 1020
	    /* convert to bytes after the prompt */
43882 ripley 1021
	    s0 = col_to_pos(p, p->mx0);
1022
	    s1 = col_to_pos(p, p->mx1);
41380 murdoch 1023
	    cur_line = LINE(CURROW) + prompt_len;
43882 ripley 1024
	    for(int i = s0; i <= max_pos; i++)
41380 murdoch 1025
		cur_line[i] = cur_line[i + s1 - s0 + 1];
43882 ripley 1026
	    max_pos -= s1 - s0 + 1;
1027
	    cur_line[max_pos] = L'\0';
45067 ripley 1028
	    if (cur_pos > s0)
43882 ripley 1029
		cur_pos = cur_pos > s1 ? cur_pos - (s1 - s0 + 1) : s0;
41380 murdoch 1030
	    setCURCOL(p);
1031
	    p->needredraw = 1;
1032
	}
1033
    }
1034
}
1035
 
46677 ripley 1036
/* cmd is in native encoding */
43795 ripley 1037
void consolecmd(control c, const char *cmd)
32605 ripley 1038
{
1039
    ConsoleData p = getdata(c);
1040
 
4394 ripley 1041
    int i;
4605 ripley 1042
    if (p->sel) {
45067 ripley 1043
	deleteselected(p);
4605 ripley 1044
	p->sel = 0;
4645 ripley 1045
	p->needredraw = 1;
4605 ripley 1046
	REDRAW;
1047
    }
4394 ripley 1048
    storekey(c, BEGINLINE);
1049
    storekey(c, KILLRESTOFLINE);
46826 ripley 1050
    if(isUnicodeWindow(c)) {
46677 ripley 1051
	size_t sz = (strlen(cmd) + 1) * sizeof(wchar_t);
55057 murdoch 1052
	wchar_t *wcs = (wchar_t*) R_alloc(strlen(cmd) + 1, sizeof(wchar_t));
46677 ripley 1053
	memset(wcs, 0, sz);
1054
	mbstowcs(wcs, cmd, sz-1);
1055
	for(i = 0; wcs[i]; i++) storekey(c, wcs[i]);
46826 ripley 1056
    } else {
1057
	const char *ch;
1058
	for (ch = cmd; *ch; ch++) storekey(c, (unsigned char) *ch);
46677 ripley 1059
    }
4394 ripley 1060
    storekey(c, '\n');
46826 ripley 1061
/* if we are editing we save the actual line
1062
   FIXME: not right if Unicode */
4394 ripley 1063
    if (p->r > -1) {
43882 ripley 1064
	char buf[2000], *cp; /* maximum 2 bytes/char */
1065
	wchar_t *wc = &(p->lbuf->s[p->lbuf->ns - 1][prompt_len]);
1066
	memset(buf, 0, 2000);
1067
	wcstombs(buf, wc, 1000);
1068
	for (cp = buf; *cp; cp++) storekey(c, *cp);
1069
	for (i = max_pos; i > cur_pos; i--) storekey(c, CHARLEFT);
4394 ripley 1070
    }
32605 ripley 1071
}
4394 ripley 1072
 
43882 ripley 1073
static int CleanTranscript(wchar_t *tscpt, wchar_t *cmds)
28544 murdoch 1074
{
1075
    /*
1076
     * Filter R commands out of a string that contains
1077
     * prompts, commands, and output.
1078
     * Uses a simple algorithm that just looks for '>'
41313 murdoch 1079
     * prompts and '+' continuation following a simple prompt prefix.
28544 murdoch 1080
     * Always return the length of the string required
1081
     * to hold the filtered commands.
1082
     * If cmds is a non-null pointer, write the commands
1083
     * to cmds & terminate with null.
1084
     */
1085
    int incommand = 0, startofline = 1, len = 0;
43882 ripley 1086
    wchar_t nonprefix[] = L">+ \t\n\r";
28544 murdoch 1087
    while (*tscpt) {
1088
	if (startofline) {
41313 murdoch 1089
	    /* skip initial whitespace */
43882 ripley 1090
	    while (*tscpt == L' ' || *tscpt == L'\t') tscpt++;
41313 murdoch 1091
	    /* skip over the prompt prefix */
43882 ripley 1092
	    while (*tscpt && !wcschr(nonprefix, *tscpt)) tscpt++;
1093
	    if (*tscpt == L'>' || (incommand && *tscpt == L'+')) {
28544 murdoch 1094
		tscpt++;
43882 ripley 1095
		if (*tscpt == L' ' || *tscpt == L'\t') tscpt++;
28544 murdoch 1096
		incommand = 1;
1097
	    } else {
1098
		incommand = 0;
1099
	    }
1100
	    startofline = 0;
1101
	} else {
1102
	    if (incommand) {
43882 ripley 1103
		if (cmds) *(cmds++) = *tscpt;
28544 murdoch 1104
		len++;
1105
	    }
43882 ripley 1106
	    if (*tscpt == L'\n') startofline = 1;
28544 murdoch 1107
	    tscpt++;
1108
	}
1109
    }
1110
    if (cmds) {
1111
	/* seem to have to terminate with two nulls, otherwise
1112
	   pasting empty commands doesn't work correctly (e.g.,
1113
	   when clipboard contains 'XXX') */
1114
	*cmds = '\0';
1115
	*(cmds+1) = '\0';
1116
    }
1117
    return(len+2);
1118
}
1119
 
28991 murdoch 1120
/* Send a single newline to the console */
1121
void consolenewline(control c)
1122
{
1123
    storekey(c, '\n');
1124
}
1125
 
28544 murdoch 1126
/* the following four routines are system dependent */
4394 ripley 1127
void consolepaste(control c)
32605 ripley 1128
{
1129
    ConsoleData p = getdata(c);
1130
 
4394 ripley 1131
    HGLOBAL hglb;
43882 ripley 1132
    wchar_t *pc, *new = NULL;
4605 ripley 1133
    if (p->sel) {
45067 ripley 1134
	deleteselected(p);
4605 ripley 1135
	p->sel = 0;
4645 ripley 1136
	p->needredraw = 1;
4605 ripley 1137
	REDRAW;
1138
     }
45491 ripley 1139
    if (p->kind == PAGER) return;
4394 ripley 1140
    if ( OpenClipboard(NULL) &&
45067 ripley 1141
	 (hglb = GetClipboardData(CF_UNICODETEXT)) &&
1142
	 (pc = (wchar_t *) GlobalLock(hglb)))
4394 ripley 1143
    {
45067 ripley 1144
	if (p->clp) {
1145
	   new = realloc((void *)p->clp,
43882 ripley 1146
			 (wcslen(p->clp) + wcslen(pc) + 1) * sizeof(wchar_t));
45067 ripley 1147
	}
1148
	else {
1149
	   new = malloc((wcslen(pc) + 1) * sizeof(wchar_t)) ;
1150
	   if (new) new[0] = L'\0';
1151
	   p->already = p->numkeys;
1152
	   p->pclp = 0;
1153
	}
1154
	if (new) {
45213 ripley 1155
	   int i;
45067 ripley 1156
	   p->clp = new;
45213 ripley 1157
	   /* Surrogate Pairs Block */
45214 ripley 1158
	   for (i = 0; i < wcslen(pc); i++)
1159
	       if (IsSurrogatePairsHi(pc[i]) && i+1 < wcslen(pc) &&
1160
		    IsSurrogatePairsLo(pc[i+1]) ) {
1161
		   pc[i] = L'?';
1162
		   pc[i+1] = L'?';
1163
		   i++;
1164
	       }
45067 ripley 1165
	   wcscat(p->clp, pc);
1166
	}
1167
	else {
1168
	   R_ShowMessage(G_("Not enough memory"));
1169
	}
1170
	GlobalUnlock(hglb);
4394 ripley 1171
    }
1172
    CloseClipboard();
32605 ripley 1173
}
4394 ripley 1174
 
28544 murdoch 1175
void consolepastecmds(control c)
32605 ripley 1176
{
1177
    ConsoleData p = getdata(c);
1178
 
28544 murdoch 1179
    HGLOBAL hglb;
43882 ripley 1180
    wchar_t *pc, *new = NULL;
28544 murdoch 1181
    if (p->sel) {
45067 ripley 1182
	deleteselected(p);
28544 murdoch 1183
	p->sel = 0;
1184
	p->needredraw = 1;
1185
	REDRAW;
1186
     }
32606 ripley 1187
    if (p->kind == PAGER) return;;
28544 murdoch 1188
    if ( OpenClipboard(NULL) &&
45067 ripley 1189
	 (hglb = GetClipboardData(CF_UNICODETEXT)) &&
1190
	 (pc = (wchar_t *) GlobalLock(hglb)))
28544 murdoch 1191
    {
45067 ripley 1192
	if (p->clp) {
1193
	    new = realloc((void *)p->clp,
1194
			  (wcslen(p->clp) + CleanTranscript(pc, 0))
43882 ripley 1195
			  * sizeof(wchar_t));
45067 ripley 1196
	}
1197
	else {
43882 ripley 1198
	    new = malloc(CleanTranscript(pc, 0) * sizeof(wchar_t));
28544 murdoch 1199
	    if (new) new[0] = '\0';
1200
	    p->already = p->numkeys;
1201
	    p->pclp = 0;
45067 ripley 1202
	}
1203
	if (new) {
1204
	    p->clp = new;
28544 murdoch 1205
	    /* copy just the commands from the clipboard */
1206
	    for (; *new; ++new); /* append to the end of 'new' */
1207
	    CleanTranscript(pc, new);
45067 ripley 1208
	}
1209
	else {
33001 ripley 1210
	    R_ShowMessage(G_("Not enough memory"));
45067 ripley 1211
	}
1212
	GlobalUnlock(hglb);
28544 murdoch 1213
    }
1214
    CloseClipboard();
32605 ripley 1215
}
28544 murdoch 1216
 
32605 ripley 1217
/* This works with columns, not chars or bytes */
4394 ripley 1218
static void consoletoclipboardHelper(control c, int x0, int y0, int x1, int y1)
32605 ripley 1219
{
1220
    ConsoleData p = getdata(c);
1221
 
4394 ripley 1222
    HGLOBAL hglb;
1223
    int ll, i, j;
43882 ripley 1224
    wchar_t *s;
4394 ripley 1225
 
32976 ripley 1226
    if(mbcslocale) {
43882 ripley 1227
	int w0, x00 = x0, x11=100000;
1228
	i = y0; ll = 1; /* terminator */
32976 ripley 1229
	while (i <= y1) {
43882 ripley 1230
	    wchar_t *P = LINE(i);
1231
	    for (w0 = 0; w0 < x00 && *P; P++) w0 += Ri18n_wcwidth(*P);
32976 ripley 1232
	    x00 = 0;
1233
	    if(i == y1) x11 = x1+1; /* cols are 0-based */
1234
	    while (w0 < x11 && *P) {
43882 ripley 1235
		ll++;
1236
		w0 += Ri18n_wcwidth(*P++);
32976 ripley 1237
	    }
1238
	    if(w0 < x11) ll += 2;  /* \r\n */
1239
	    i++;
32616 ripley 1240
	}
37676 ripley 1241
    } else {
32976 ripley 1242
	i = y0; j = x0; ll = 1; /* terminator */
1243
	while ((i < y1) || ((i == y1) && (j <= x1))) {
1244
	    if (LINE(i)[j]) {
1245
		ll++;
1246
		j++;
43882 ripley 1247
	    } else {
32976 ripley 1248
		ll += 2;
1249
		i++;
1250
		j = 0;
1251
	    }
32616 ripley 1252
	}
1253
    }
32976 ripley 1254
 
45067 ripley 1255
 
43882 ripley 1256
    if (!(hglb = GlobalAlloc(GHND, ll * sizeof(wchar_t)))){
45067 ripley 1257
	R_ShowMessage(G_("Insufficient memory: text not copied to the clipboard"));
1258
	return;
4394 ripley 1259
    }
43882 ripley 1260
    if (!(s = (wchar_t *)GlobalLock(hglb))){
45067 ripley 1261
	R_ShowMessage(G_("Insufficient memory: text not copied to the clipboard"));
1262
	return;
4394 ripley 1263
    }
32976 ripley 1264
    if(mbcslocale) {
43882 ripley 1265
	int w0, x00 = x0, x11=100000;
1266
	wchar_t *P;
1267
	i = y0;
32976 ripley 1268
	while (i <= y1) {
1269
	    P = LINE(i);
43882 ripley 1270
	    for (w0 = 0; w0 < x00 && *P; P++) w0 += Ri18n_wcwidth(*P);
32976 ripley 1271
	    x00 = 0;
1272
	    if(i == y1) x11 = x1+1;
1273
	    while (w0 < x11 && *P) {
43882 ripley 1274
		w0 += Ri18n_wcwidth(*P);
1275
		*s++ = *P++;
32976 ripley 1276
	    }
43882 ripley 1277
	    if(w0 < x11) *s++ = L'\r'; *s++ = L'\n';
32976 ripley 1278
	    i++;
32616 ripley 1279
	}
37676 ripley 1280
    } else {
32976 ripley 1281
	i = y0; j = x0;
1282
	while ((i < y1) || ((i == y1) && (j <= x1))) {
43882 ripley 1283
	    wchar_t ch = LINE(i)[j];
32976 ripley 1284
	    if (ch) {
1285
		*s++ = ch;
1286
		j++;
1287
	    } else {
43882 ripley 1288
		*s++ = L'\r'; *s++ = L'\n';
32976 ripley 1289
		i++;
1290
		j = 0;
1291
	    }
32616 ripley 1292
	}
1293
    }
43882 ripley 1294
    *s = L'\0';
4394 ripley 1295
    GlobalUnlock(hglb);
1296
    if (!OpenClipboard(NULL) || !EmptyClipboard()) {
45067 ripley 1297
	R_ShowMessage(G_("Unable to open the clipboard"));
1298
	GlobalFree(hglb);
1299
	return;;
4394 ripley 1300
    }
43882 ripley 1301
    SetClipboardData(CF_UNICODETEXT, hglb);
4394 ripley 1302
    CloseClipboard();
32605 ripley 1303
}
4394 ripley 1304
 
1305
/* end of system dependent part */
1306
 
9212 ripley 1307
int consolecanpaste(control c)
1308
{
1309
    return clipboardhastext();
1310
}
1311
 
1312
 
4394 ripley 1313
int consolecancopy(control c)
32605 ripley 1314
{
1315
    ConsoleData p = getdata(c);
1316
    return p->sel;
1317
}
4394 ripley 1318
 
32605 ripley 1319
 
4394 ripley 1320
void consolecopy(control c)
32605 ripley 1321
{
1322
    ConsoleData p = getdata(c);
1323
 
4394 ripley 1324
    if (p->sel) {
1325
	int len, c1, c2, c3;
1326
	int x0, y0, x1, y1;
1327
	if (p->my0 >= NUMLINES) p->my0 = NUMLINES - 1;
1328
	if (p->my0 < 0) p->my0 = 0;
43882 ripley 1329
	len = wcswidth(LINE(p->my0));
4394 ripley 1330
	if (p->mx0 >= len) p->mx0 = len - 1;
1331
	if (p->mx0 < 0) p->mx0 = 0;
1332
	if (p->my1 >= NUMLINES) p->my1 = NUMLINES - 1;
1333
	if (p->my1 < 0) p->my1 = 0;
43882 ripley 1334
	len = wcswidth(LINE(p->my1));
10976 ripley 1335
	if (p->mx1 >= len) p->mx1 = len/* - 1*/;
4394 ripley 1336
	if (p->mx1 < 0) p->mx1 = 0;
1337
	c1 = (p->my0 < p->my1);
1338
	c2 = (p->my0 == p->my1);
1339
	c3 = (p->mx0 < p->mx1);
1340
	if (c1 || (c2 && c3)) {
1341
	   x0 = p->mx0; y0 = p->my0;
1342
	   x1 = p->mx1; y1 = p->my1;
1343
	}
1344
	else {
1345
	   x0 = p->mx1; y0 = p->my1;
1346
	   x1 = p->mx0; y1 = p->my0;
1347
	}
1348
	consoletoclipboardHelper(c, x0, y0, x1, y1);
1349
	REDRAW;
1350
    }
32605 ripley 1351
}
4394 ripley 1352
 
1353
void consoleselectall(control c)
32605 ripley 1354
{
1355
    ConsoleData p = getdata(c);
1356
 
4394 ripley 1357
   if (NUMLINES) {
1358
       p->sel = 1;
1359
       p->my0 = p->mx0 = 0;
1360
       p->my1 = NUMLINES - 1;
43882 ripley 1361
       p->mx1 = wcslen(LINE(p->my1));
4394 ripley 1362
       REDRAW;
1363
    }
32605 ripley 1364
}
4394 ripley 1365
 
45067 ripley 1366
/*
43882 ripley 1367
   This works in CJK as the IME puts CJK characters in the
1368
   input buffer as 2 bytes, and they are retrieved successively
1369
*/
9212 ripley 1370
void console_normalkeyin(control c, int k)
32605 ripley 1371
{
1372
    ConsoleData p = getdata(c);
1373
 
4394 ripley 1374
    int st;
1375
 
6994 pd 1376
    st = ggetkeystate();
4394 ripley 1377
    if ((p->chbrk) && (k == p->chbrk) &&
1378
	((!p->modbrk) || ((p->modbrk) && (st == p->modbrk)))) {
1379
	p->fbrk(c);
1380
	return;
1381
    }
1382
    if (st == CtrlKey)
1383
	switch (k + 'A' - 1) {
9862 ripley 1384
	    /* most are stored as themselves */
4394 ripley 1385
	case 'C':
1386
	    consolecopy(c);
1387
	    st = -1;
1388
	    break;
1389
	case 'V':
1390
	case 'Y':
10217 pd 1391
	    if(p->kind == PAGER) {
24308 ripley 1392
		consolecopy(c);
10345 ripley 1393
		if (CharacterMode == RGui) consolepaste(RConsole);
10217 pd 1394
	    }
1395
	    else consolepaste(c);
4394 ripley 1396
	    st = -1;
1397
	    break;
1398
	case 'X':
1399
	    consolecopy(c);
1400
	    consolepaste(c);
1401
	    st = -1;
1402
	    break;
1403
	case 'W':
1404
	    consoletogglelazy(c);
1405
	    st = -1;
1406
	    break;
9862 ripley 1407
	case 'L':
10227 ripley 1408
	    consoleclear(c);
9862 ripley 1409
	    st = -1;
1410
	    break;
1411
	case 'O':
1412
	    p->overwrite = !p->overwrite;
41380 murdoch 1413
	    p->needredraw = 1;
9862 ripley 1414
	    st = -1;
1415
	    break;
4394 ripley 1416
	}
1417
    if (p->sel) {
45067 ripley 1418
	if (st != -1) deleteselected(p);
41380 murdoch 1419
	p->needredraw = 1;
4394 ripley 1420
	p->sel = 0;
1421
    }
41380 murdoch 1422
    if (p->needredraw) REDRAW;
4394 ripley 1423
    if (st == -1) return;
21959 ripley 1424
    if (p->kind == PAGER) {
1425
	if(k == 'q' || k == 'Q') pagerbclose(c);
23057 ripley 1426
	if(k == ' ') setfirstvisible(c, NEWFV + ROWS);
1427
	if(k == '-') setfirstvisible(c, NEWFV - ROWS);
1428
	if(k == 'F' - 'A' + 1) setfirstvisible(c, NEWFV + ROWS);
1429
	if(k == 'B' - 'A' + 1) setfirstvisible(c, NEWFV - ROWS);
23414 ripley 1430
	if(k == 1) consoleselectall(c);
21959 ripley 1431
	return;
1432
    }
9862 ripley 1433
    storekey(c, k);
32605 ripley 1434
}
4394 ripley 1435
 
9212 ripley 1436
void console_ctrlkeyin(control c, int key)
32605 ripley 1437
{
1438
    ConsoleData p = getdata(c);
1439
 
4605 ripley 1440
    int st;
4394 ripley 1441
 
6994 pd 1442
    st = ggetkeystate();
4394 ripley 1443
    if ((p->chbrk) && (key == p->chbrk) &&
1444
	((!p->modbrk) || ((p->modbrk) && (st == p->modbrk)))) {
1445
	p->fbrk(c);
1446
	return;
1447
    }
1448
    switch (key) {
4605 ripley 1449
     case PGUP: setfirstvisible(c, NEWFV - ROWS); break;
1450
     case PGDN: setfirstvisible(c, NEWFV + ROWS); break;
4394 ripley 1451
     case HOME:
1452
	 if (st == CtrlKey)
1453
	     setfirstvisible(c, 0);
1454
	 else
1455
	     if (p->kind == PAGER)
1456
		 setfirstcol(c, 0);
1457
	     else
1458
		 storekey(c, BEGINLINE);
1459
	 break;
1460
     case END:
1461
	 if (st == CtrlKey)
1462
	     setfirstvisible(c, NUMLINES);
1463
	 else
1464
	     storekey(c, ENDLINE);
1465
	 break;
1466
     case UP:
1467
	 if ((st == CtrlKey) || (p->kind == PAGER))
1468
	     setfirstvisible(c, NEWFV - 1);
1469
	 else
1470
	     storekey(c, PREVHISTORY);
1471
	 break;
1472
     case DOWN:
1473
	 if ((st == CtrlKey) || (p->kind == PAGER))
1474
	     setfirstvisible(c, NEWFV + 1);
1475
	 else
1476
	     storekey(c, NEXTHISTORY);
1477
	 break;
1478
     case LEFT:
1479
	 if ((st == CtrlKey) || (p->kind == PAGER))
1480
	     setfirstcol(c, FC - 5);
1481
	 else
1482
	     storekey(c, CHARLEFT);
1483
	 break;
1484
     case RIGHT:
1485
	 if ((st == CtrlKey) || (p->kind == PAGER))
1486
	     setfirstcol(c, FC + 5);
1487
	 else
1488
	     storekey(c, CHARRIGHT);
1489
	 break;
1490
     case DEL:
45067 ripley 1491
	 if (p->sel) {
1492
	     if (st == ShiftKey) consolecopy(c);
1493
	     deleteselected(p);
1494
	     p->sel = 0;
1495
	 } else  if (st == CtrlKey)
4394 ripley 1496
	     storekey(c, KILLRESTOFLINE);
1497
	 else
1498
	     storekey(c, DELETECHAR);
1499
	 break;
1500
     case ENTER:
45067 ripley 1501
	 deleteselected(p);
4394 ripley 1502
	 storekey(c, '\n');
1503
	 break;
1504
     case INS:
41380 murdoch 1505
	 if (st == ShiftKey) {
1506
	     deleteselected(p);
4394 ripley 1507
	     consolepaste(c);
41380 murdoch 1508
	 } else {
1509
	     p->overwrite = !p->overwrite;
1510
	     p->needredraw = 1;
1511
	 }
4394 ripley 1512
	 break;
1513
    }
4605 ripley 1514
    if (p->sel) {
4394 ripley 1515
	p->sel = 0;
4645 ripley 1516
	p->needredraw = 1;
4394 ripley 1517
    }
41380 murdoch 1518
    if (p->needredraw) REDRAW;
32605 ripley 1519
}
4394 ripley 1520
 
12256 pd 1521
static Rboolean incomplete = FALSE;
41771 ripley 1522
int consolewrites(control c, const char *s)
32605 ripley 1523
{
1524
    ConsoleData p = getdata(c);
1525
 
43882 ripley 1526
    wchar_t buf[1001];
11075 ripley 1527
    if(p->input) {
45067 ripley 1528
	int i, len = wcslen(LINE(NUMLINES - 1));
11075 ripley 1529
	/* save the input line */
43882 ripley 1530
	wcsncpy(buf, LINE(NUMLINES - 1), 1000);
1531
	buf[1000] = L'\0';
11075 ripley 1532
	/* now zap it */
43882 ripley 1533
	for(i = 0; i < len; i++) xbufaddxc(p->lbuf, L'\b');
12256 pd 1534
	if (incomplete) {
1535
	    p->lbuf->ns--;
1536
	    p->lbuf->free--;
1537
	    p->lbuf->av++;
1538
	}
11075 ripley 1539
	USER(NUMLINES - 1) = -1;
1540
    }
4394 ripley 1541
    xbufadds(p->lbuf, s, 0);
1542
    FC = 0;
12256 pd 1543
    if(p->input) {
1544
	incomplete = (s[strlen(s) - 1] != '\n');
45067 ripley 1545
	if (incomplete) xbufaddxc(p->lbuf, L'\n');
43882 ripley 1546
	xbufaddxs(p->lbuf, buf, 1);
12256 pd 1547
    }
4394 ripley 1548
    if (strchr(s, '\n')) p->needredraw = 1;
1549
    if (!p->lazyupdate || (p->r >= 0))
45067 ripley 1550
	setfirstvisible(c, NUMLINES - ROWS);
4394 ripley 1551
    else {
45067 ripley 1552
	p->newfv = NUMLINES - ROWS;
1553
	if (p->newfv < 0) p->newfv = 0;
4394 ripley 1554
    }
12256 pd 1555
    if(p->input) REDRAW;
32605 ripley 1556
    return 0;
1557
}
4394 ripley 1558
 
9212 ripley 1559
void freeConsoleData(ConsoleData p)
4394 ripley 1560
{
1561
    if (!p) return;
1562
    if (p->bm) del(p->bm);
1563
    if (p->kind == CONSOLE) {
45067 ripley 1564
	if (p->lbuf) xbufdel(p->lbuf);
11169 ripley 1565
	if (p->kbuf) free(p->kbuf);
4394 ripley 1566
    }
11169 ripley 1567
    free(p);
4394 ripley 1568
}
1569
 
1570
static void delconsole(control c)
1571
{
1572
    freeConsoleData(getdata(c));
1573
}
1574
 
1575
/* console readline (coded looking to the GNUPLOT 3.5 readline)*/
43882 ripley 1576
static wchar_t consolegetc(control c)
4394 ripley 1577
{
1578
    ConsoleData p;
43882 ripley 1579
    wchar_t ch;
4394 ripley 1580
 
1581
    p = getdata(c);
1582
    while((p->numkeys == 0) && (!p->clp))
1583
    {
1584
	if (!peekevent()) WaitMessage();
7824 ripley 1585
	R_ProcessEvents();
4394 ripley 1586
    }
4605 ripley 1587
    if (p->sel) {
45067 ripley 1588
	deleteselected(p);
4605 ripley 1589
	p->sel = 0;
4645 ripley 1590
	p->needredraw = 1;
32605 ripley 1591
	setCURCOL(p); /* Needed? */
4605 ripley 1592
	REDRAW;
4645 ripley 1593
    }
43882 ripley 1594
    if (!p->already && p->clp) {
4605 ripley 1595
	ch = p->clp[p->pclp++];
1596
	if (!(p->clp[p->pclp])) {
11169 ripley 1597
	    free(p->clp);
4394 ripley 1598
	    p->clp = NULL;
1599
	}
11075 ripley 1600
    } else {
45252 ripley 1601
	if(isUnicodeWindow(c)) {
1602
	    ch = p->kbuf[p->firstkey];
1603
	    p->firstkey = (p->firstkey + 1) % NKEYS;
1604
	    p->numkeys--;
1605
	    if (p->already) p->already--;
1606
	} else {
1607
	    if(mbcslocale) {
1608
		/* Possibly multiple 'keys' for a single keystroke */
1609
		char tmp[20];
1610
		unsigned int used, i;
1611
 
1612
		for(i = 0; i < MB_CUR_MAX; i++)
1613
		    tmp[i] = p->kbuf[(p->firstkey + i) % NKEYS];
1614
		used = mbrtowc(&ch, tmp, MB_CUR_MAX, NULL);
1615
		p->firstkey = (p->firstkey + used) % NKEYS;
1616
		p->numkeys -= used;
1617
		if (p->already) p->already -= used;
1618
	    } else {
1619
		ch = (unsigned char) p->kbuf[p->firstkey];
1620
		if(ch >=128) {
1621
		    char tmp[2] = " ";
1622
		    tmp[0] = ch;
1623
		    mbrtowc(&ch, tmp, 2, NULL);
1624
		}
1625
		p->firstkey = (p->firstkey + 1) % NKEYS;
1626
		p->numkeys--;
1627
		if (p->already) p->already--;
1628
	    }
45225 ripley 1629
	}
4394 ripley 1630
    }
1631
    return ch;
1632
}
1633
 
1634
static void consoleunputc(control c)
32605 ripley 1635
{
1636
    ConsoleData p = getdata(c);
1637
 
38975 ripley 1638
    if(p->clp) p->pclp--;
1639
    else {
1640
	p->numkeys += 1;
1641
	if (p->firstkey > 0) p->firstkey -= 1;
1642
	else p->firstkey = NKEYS - 1;
1643
    }
32605 ripley 1644
}
4394 ripley 1645
 
32605 ripley 1646
/* This scrolls as far left as possible */
4394 ripley 1647
static void checkvisible(control c)
32605 ripley 1648
{
1649
    ConsoleData p = getdata(c);
1650
 
4394 ripley 1651
    int newfc;
1652
 
1653
    setfirstvisible(c, NUMLINES-ROWS);
1654
    newfc = 0;
32605 ripley 1655
    while ((CURCOL <= newfc) || (CURCOL > newfc+COLS-2)) newfc += 5;
4394 ripley 1656
    if (newfc != FC) setfirstcol(c, newfc);
32605 ripley 1657
}
4394 ripley 1658
 
1659
static void draweditline(control c)
32605 ripley 1660
{
1661
    ConsoleData p = getdata(c);
1662
    setCURCOL(p);
4394 ripley 1663
    checkvisible(c);
4605 ripley 1664
    if (p->needredraw) {
45067 ripley 1665
	REDRAW;
11075 ripley 1666
    } else {
1667
	WRITELINE(NUMLINES - 1, p->r);
1668
	RSHOW(RLINE(p->r));
4605 ripley 1669
    }
32605 ripley 1670
}
4394 ripley 1671
 
45491 ripley 1672
/* This needs to convert the nul-terminated wchar_t string 'in' to a
1673
   sensible strinf in buf[len].  It must not be empty, as R will
1674
   interpret that as EOF, and it should end in \n, as 'in' should do.
1675
 
1676
   Our strategy is to convert character by character to the current
1677
   Windows locale, using \uxxxx escapes for invalid characters.
1678
*/
1679
 
1680
static void wcstobuf(char *buf, int len, const wchar_t *in)
1681
{
1682
    int used, tot = 0;
1683
    char *p = buf, tmp[7];
1684
    const wchar_t *wc = in;
46677 ripley 1685
 
45491 ripley 1686
    for(; wc; wc++, p+=used, tot+=used) {
1687
	if(tot >= len - 2) break;
1688
	used = wctomb(p, *wc);
1689
	if (used < 0) {
1690
	    snprintf(tmp, 7, "\\u%x", *wc);
1691
	    used = strlen(tmp);
1692
	    memcpy(p, tmp, used);
1693
	}
1694
    }
1695
    *p++ = '\n'; *p = '\0';
1696
}
1697
 
45067 ripley 1698
int consolereads(control c, const char *prompt, char *buf, int len,
43767 ripley 1699
		 int addtohistory)
32605 ripley 1700
{
1701
    ConsoleData p = getdata(c);
1702
 
43882 ripley 1703
    wchar_t *cur_line, *P;
1704
    wchar_t *aLine;
37599 ripley 1705
    int ns0 = NUMLINES, w0 = 0, pre_prompt_len;
4394 ripley 1706
 
43882 ripley 1707
    pre_prompt_len = wcslen(LINE(NUMLINES - 1));
4394 ripley 1708
    /* print the prompt */
1709
    xbufadds(p->lbuf, prompt, 1);
32605 ripley 1710
    if (!xbufmakeroom(p->lbuf, len + 1)) return 1;
37591 ripley 1711
    P = aLine = LINE(NUMLINES - 1);
43882 ripley 1712
    prompt_len = wcslen(aLine);
1713
    for (; P < aLine + pre_prompt_len; P++)
45067 ripley 1714
	if(*P == L'\r') w0 = 0;
43882 ripley 1715
	else w0 += mbcslocale ? Ri18n_wcwidth(*P) : 1;
1716
    USER(NUMLINES - 1) = w0;
1717
    prompt_wid = wcswidth(aLine);
4394 ripley 1718
    if (NUMLINES > ROWS) {
1719
	p->r = ROWS - 1;
1720
	p->newfv = NUMLINES - ROWS;
11075 ripley 1721
    } else {
4394 ripley 1722
	p->r = NUMLINES - 1;
1723
	p->newfv = 0;
1724
    }
32605 ripley 1725
    CURCOL = prompt_wid;
4394 ripley 1726
    p->fc = 0;
43882 ripley 1727
    cur_pos = 0;
1728
    max_pos = 0;
4394 ripley 1729
    cur_line = &aLine[prompt_len];
43882 ripley 1730
    cur_line[0] = L'\0';
52012 ripley 1731
    showcaret(c, 1);
4394 ripley 1732
    REDRAW;
1733
    for(;;) {
43882 ripley 1734
	wchar_t cur_char;
1735
	char chtype; /* boolean */
11075 ripley 1736
	p->input = 1;
4394 ripley 1737
	cur_char = consolegetc(c);
11075 ripley 1738
	p->input = 0;
43908 ripley 1739
	chtype = ((unsigned int) cur_char > 0x1f);
11075 ripley 1740
	if(NUMLINES != ns0) { /* we scrolled, e.g. cleared screen */
45067 ripley 1741
	    cur_line = LINE(NUMLINES - 1) + prompt_len;
11075 ripley 1742
	    ns0 = NUMLINES;
1743
	    if (NUMLINES > ROWS) {
1744
		p->r = ROWS - 1;
1745
		p->newfv = NUMLINES - ROWS;
1746
	    } else {
1747
		p->r = NUMLINES - 1;
1748
		p->newfv = 0;
1749
	    }
37599 ripley 1750
	    USER(NUMLINES - 1) = prompt_wid;
11075 ripley 1751
	    p->needredraw = 1;
10227 ripley 1752
	}
45067 ripley 1753
	if(chtype && (max_pos <= len - 2)) {
38975 ripley 1754
	    /* not a control char: we need to fit in the char\n\0 */
4394 ripley 1755
	    int i;
43882 ripley 1756
	    if(!p->overwrite) {
1757
		for(i = max_pos; i > cur_pos; i--)
1758
		    cur_line[i] = cur_line[i - 1];
4394 ripley 1759
	    }
43882 ripley 1760
	    cur_line[cur_pos] = cur_char;
1761
	    if(!p->overwrite || cur_pos == max_pos) {
1762
		max_pos += 1;
1763
		cur_line[max_pos] = L'\0';
1764
	    }
1765
	    cur_pos++;
32605 ripley 1766
	} else { /* a control char */
4394 ripley 1767
	    /* do normal editing commands */
1768
	    int i;
1769
	    switch(cur_char) {
1770
	    case BEGINLINE:
43882 ripley 1771
		cur_pos = 0;
4394 ripley 1772
		break;
1773
	    case CHARLEFT:
43882 ripley 1774
		if(cur_pos > 0) cur_pos--;
4394 ripley 1775
		break;
1776
	    case ENDLINE:
43882 ripley 1777
		cur_pos = max_pos;
4394 ripley 1778
		break;
1779
	    case CHARRIGHT:
43882 ripley 1780
		if(cur_pos < max_pos) cur_pos ++;
4394 ripley 1781
		break;
1782
	    case KILLRESTOFLINE:
43882 ripley 1783
		max_pos = cur_pos;
1784
		cur_line[max_pos] = L'\0';
4394 ripley 1785
		break;
1786
	    case KILLLINE:
43882 ripley 1787
		max_pos = cur_pos = 0;
1788
		cur_line[max_pos] = L'\0';
4394 ripley 1789
		break;
1790
	    case PREVHISTORY:
43882 ripley 1791
		P = wgl_hist_prev();
1792
		xbufmakeroom(p->lbuf, wcslen(P) + 1);
1793
		wcscpy(cur_line, P);
1794
		cur_pos = max_pos = wcslen(cur_line);
4394 ripley 1795
		break;
1796
	    case NEXTHISTORY:
43882 ripley 1797
		P = wgl_hist_next();
1798
		xbufmakeroom(p->lbuf, wcslen(P) + 1);
1799
		wcscpy(cur_line, P);
1800
		cur_pos = max_pos = wcslen(cur_line);
4394 ripley 1801
		break;
1802
	    case BACKCHAR:
43882 ripley 1803
		if(cur_pos > 0) {
1804
		    cur_pos--;
1805
		    for(i = cur_pos; i <= max_pos - 1; i++)
45067 ripley 1806
			cur_line[i] = cur_line[i + 1];
43882 ripley 1807
		    max_pos--;
4394 ripley 1808
		}
1809
		break;
1810
	    case DELETECHAR:
43882 ripley 1811
		if(max_pos == 0) break;
1812
		if(cur_pos < max_pos) {
1813
		    for(i = cur_pos; i <= max_pos - 1; i++)
1814
			cur_line[i] = cur_line[i + 1];
1815
		    max_pos--;
4394 ripley 1816
		}
1817
		break;
9862 ripley 1818
	    case CHARTRANS:
43882 ripley 1819
		if(cur_pos < 1) break;
1820
		if(cur_pos >= max_pos) break;
45067 ripley 1821
		cur_char = cur_line[cur_pos];
43882 ripley 1822
		cur_line[cur_pos] = cur_line[cur_pos-1];
1823
		cur_line[cur_pos-1] = cur_char;
9862 ripley 1824
		break;
38975 ripley 1825
	    default:   /* Another control char, or overflow */
43882 ripley 1826
		if (chtype || (cur_char == L'\n') || (cur_char == EOFKEY)) {
4394 ripley 1827
		    if (chtype) {
43882 ripley 1828
			if (cur_pos == max_pos) {
4394 ripley 1829
			    consoleunputc(c);
1830
			} else {
1831
			    gabeep();
1832
			    break;
1833
			}
1834
		    }
43882 ripley 1835
		    if((cur_char == L'\n') || (cur_char == EOFKEY)) {
1836
			cur_line[max_pos] = L'\n';
1837
			cur_line[max_pos + 1] = L'\0';
1838
		    } else
1839
			cur_line[max_pos] = L'\0';
45491 ripley 1840
		    wcstobuf(buf, len, cur_line);
1841
		    //sprintf(buf, "%ls", cur_line);
1842
		    //if(strlen(buf) == 0) strcpy(buf, "invalid input\n");
4394 ripley 1843
		    p->r = -1;
43882 ripley 1844
		    cur_line[max_pos] = L'\0';
1845
		    if (max_pos && addtohistory) wgl_histadd(cur_line);
4394 ripley 1846
		    xbuffixl(p->lbuf);
1847
		    consolewrites(c, "\n");
52012 ripley 1848
		    showcaret(c, 0);
4394 ripley 1849
		    REDRAW;
32605 ripley 1850
		    return cur_char == EOFKEY;
4394 ripley 1851
		}
1852
		break;
1853
	    }
1854
	}
32605 ripley 1855
	draweditline(c);
4394 ripley 1856
    }
32605 ripley 1857
}
4394 ripley 1858
 
9212 ripley 1859
void console_sbf(control c, int pos)
32605 ripley 1860
{
1861
    ConsoleData p = getdata(c);
1862
 
7454 ripley 1863
    if (pos < 0) {
1864
	pos = -pos - 1 ;
1865
	if (FC != pos) setfirstcol(c, pos);
24308 ripley 1866
    } else
45067 ripley 1867
	if (FV != pos) setfirstvisible(c, pos);
32605 ripley 1868
}
4394 ripley 1869
 
43908 ripley 1870
void console_im(control c, font *f, point *pt)
1871
{
1872
  ConsoleData p = getdata(c);
1873
  pt->x = BORDERX + CURCOL * FW;
1874
  pt->y = BORDERY + CURROW * FH;
1875
  *f = consolefn;
1876
}
1877
 
4405 ripley 1878
void Rconsolesetwidth(int);
1879
int setWidthOnResize = 0;
4394 ripley 1880
 
4405 ripley 1881
int consolecols(console c)
1882
{
1883
    ConsoleData p = getdata(c);
4645 ripley 1884
 
4405 ripley 1885
    return p->cols;
1886
}
1887
 
9212 ripley 1888
void consoleresize(console c, rect r)
32605 ripley 1889
{
1890
    ConsoleData p = getdata(c);
1891
 
4394 ripley 1892
    int rr, pcols = COLS;
1893
 
1894
    if (((WIDTH  == r.width) &&
1895
	 (HEIGHT == r.height)) ||
1896
	(r.width == 0) || (r.height == 0) ) /* minimize */
45067 ripley 1897
	return;;
4394 ripley 1898
/*
1899
 *  set first visible to keep the bottom line on a console,
1900
 *  the middle line on a pager
1901
 */
1902
    if (p->kind == CONSOLE) rr = FV + ROWS;
1903
    else rr = FV + ROWS/2;
1904
    ROWS = r.height/FH - 1;
1905
    if (p->kind == CONSOLE) rr -= ROWS;
1906
    else rr -= ROWS/2;
1907
    COLS = r.width/FW - 1;
1908
    WIDTH = r.width;
1909
    HEIGHT = r.height;
1910
    BORDERX = (WIDTH - COLS*FW) / 2;
1911
    BORDERY = (HEIGHT - ROWS*FH) / 2;
1912
    del(BM);
1913
    BM = newbitmap(r.width, r.height, 2);
1914
    if (!BM) {
33001 ripley 1915
       R_ShowMessage(G_("Insufficient memory. Please close the console"));
4394 ripley 1916
       return ;
1917
    }
32606 ripley 1918
    if(!p->lbuf) return;;    /* don't implement resize if no content
6098 pd 1919
				   yet in pager */
4394 ripley 1920
    if (p->r >= 0) {
45067 ripley 1921
	if (NUMLINES > ROWS) {
4394 ripley 1922
	    p->r = ROWS - 1;
45067 ripley 1923
	} else
4394 ripley 1924
	    p->r = NUMLINES - 1;
1925
    }
1926
    clear(c);
1927
    p->needredraw = 1;
1928
    setfirstvisible(c, rr);
4405 ripley 1929
    if (setWidthOnResize && p->kind == CONSOLE && COLS != pcols)
45067 ripley 1930
	Rconsolesetwidth(COLS);
32605 ripley 1931
}
4394 ripley 1932
 
1933
void consolesetbrk(console c, actionfn fn, char ch, char mod)
32605 ripley 1934
{
1935
    ConsoleData p = getdata(c);
1936
 
4394 ripley 1937
    p->chbrk = ch;
1938
    p->modbrk = mod;
1939
    p->fbrk = fn;
32605 ripley 1940
}
4394 ripley 1941
 
9212 ripley 1942
font consolefn = NULL;
49486 murdoch 1943
char fontname[LF_FACESIZE+4];
9212 ripley 1944
int fontsty, pointsize;
24577 ripley 1945
int consoler = 25, consolec = 80, consolex = 0, consoley = 0;
9212 ripley 1946
int pagerrow = 25, pagercol = 80;
1947
int pagerMultiple = 1, haveusedapager = 0;
37860 ripley 1948
int consolebufb = DIMLBUF, consolebufl = MLBUF, consolebuffered = 1;
61660 ripley 1949
static int consoleblink = 1;
4394 ripley 1950
 
1951
void
43795 ripley 1952
setconsoleoptions(const char *fnname,int fnsty, int fnpoints,
45067 ripley 1953
		  int rows, int cols, int consx, int consy,
46784 murdoch 1954
		  rgb *nguiColors,
11588 ripley 1955
		  int pgr, int pgc, int multiplewindows, int widthonresize,
51948 murdoch 1956
		  int bufbytes, int buflines, int buffered, int cursor_blink)
4394 ripley 1957
{
4448 guido 1958
    char msg[LF_FACESIZE + 128];
4394 ripley 1959
    strncpy(fontname, fnname, LF_FACESIZE);
43882 ripley 1960
    fontname[LF_FACESIZE] = L'\0';
4394 ripley 1961
    fontsty =   fnsty;
1962
    pointsize = fnpoints;
1963
    if (consolefn) del(consolefn);
1964
    consolefn = NULL;
36950 ripley 1965
    if (strcmp(fontname, "FixedFont")) {
44575 ripley 1966
	consolefn = gnewfont(NULL, fnname, fnsty | FixedWidth, fnpoints, 0.0, 1);
36950 ripley 1967
	if (!consolefn) {
1968
	    /* This is unlikely to happen: it will find some match */
62583 ripley 1969
	    snprintf(msg, LF_FACESIZE + 128,
1970
		     G_("Font %s-%d-%d  not found.\nUsing system fixed font"),
1971
		     fontname, fontsty | FixedWidth, pointsize);
36950 ripley 1972
	    R_ShowMessage(msg);
1973
	    consolefn = FixedFont;
1974
	}
4448 guido 1975
    }
32589 ripley 1976
/*    if (!ghasfixedwidth(consolefn)) {
4645 ripley 1977
       sprintf(msg,
4605 ripley 1978
	       "Font %s-%d-%d has variable width.\nUsing system fixed font.",
45067 ripley 1979
	       fontname, fontsty, pointsize);
4649 ripley 1980
       R_ShowMessage(msg);
4394 ripley 1981
       consolefn = FixedFont;
32589 ripley 1982
       } */
4394 ripley 1983
    consoler = rows;
1984
    consolec = cols;
24577 ripley 1985
    consolex = consx;
1986
    consoley = consy;
46784 murdoch 1987
    for (int i=0; i<numGuiColors; i++)
46826 ripley 1988
	guiColors[i] = nguiColors[i];
4394 ripley 1989
    pagerrow = pgr;
1990
    pagercol = pgc;
1991
    pagerMultiple = multiplewindows;
1992
    setWidthOnResize = widthonresize;
11588 ripley 1993
    consolebufb = bufbytes;
1994
    consolebufl = buflines;
37860 ripley 1995
    consolebuffered = buffered;
51948 murdoch 1996
    consoleblink = cursor_blink;
4394 ripley 1997
}
1998
 
1999
void consoleprint(console c)
32605 ripley 2000
{
2001
    ConsoleData p = getdata(c);
24308 ripley 2002
 
32605 ripley 2003
 
10227 ripley 2004
    printer lpr;
2005
    int cc, rr, fh, cl, cp, clinp, i;
2006
    int top, left;
2007
    int x0, y0, x1, y1;
2008
    font f;
43882 ripley 2009
    wchar_t *s = L"";
2010
    char msg[LF_FACESIZE + 128], title[60];
2011
    wchar_t buf[1024];
10227 ripley 2012
    cursor cur;
32606 ripley 2013
    if (!(lpr = newprinter(0.0, 0.0, ""))) return;;
10227 ripley 2014
    show(c);
4394 ripley 2015
/*
2016
 * If possible, we avoid to use FixedFont for printer since it hasn't the
2017
 * right size
10227 ripley 2018
 */
2019
    f = gnewfont(lpr, strcmp(fontname, "FixedFont") ? fontname : "Courier New",
44575 ripley 2020
		 fontsty, pointsize, 0.0, 1);
10227 ripley 2021
    if (!f) {
2022
	/* Should not happen but....*/
62583 ripley 2023
	snprintf(msg, LF_FACESIZE + 128,
2024
		 G_("Font %s-%d-%d  not found.\nUsing system fixed font"),
2025
		 strcmp(fontname, "FixedFont") ? fontname : "Courier New",
2026
		 fontsty, pointsize);
10227 ripley 2027
	R_ShowMessage(msg);
2028
	f = FixedFont;
2029
    }
2030
    top = devicepixelsy(lpr) / 5;
2031
    left = devicepixelsx(lpr) / 5;
2032
    fh = fontheight(f);
2033
    rr = getheight(lpr) - top;
2034
    cc = getwidth(lpr) - 2*left;
39274 ripley 2035
    strncpy(title, GA_gettext(c), 59);
2036
    if (strlen(GA_gettext(c)) > 59) strcpy(&title[56], "...");
10227 ripley 2037
    cur = currentcursor();
2038
    setcursor(WatchCursor);
2039
 
2040
    /* Look for a selection */
2041
    if (p->sel) {
2042
	int len, c1, c2, c3;
2043
	if (p->my0 >= NUMLINES) p->my0 = NUMLINES - 1;
2044
	if (p->my0 < 0) p->my0 = 0;
43882 ripley 2045
	len = wcslen(LINE(p->my0));
10227 ripley 2046
	if (p->mx0 >= len) p->mx0 = len - 1;
2047
	if (p->mx0 < 0) p->mx0 = 0;
2048
	if (p->my1 >= NUMLINES) p->my1 = NUMLINES - 1;
2049
	if (p->my1 < 0) p->my1 = 0;
43882 ripley 2050
	len = wcslen(LINE(p->my1));
10227 ripley 2051
	if (p->mx1 >= len) p->mx1 = len - 1;
2052
	if (p->mx1 < 0) p->mx1 = 0;
2053
	c1 = (p->my0 < p->my1);
2054
	c2 = (p->my0 == p->my1);
2055
	c3 = (p->mx0 < p->mx1);
2056
	if (c1 || (c2 && c3)) {
2057
	    x0 = p->mx0; y0 = p->my0;
2058
	    x1 = p->mx1; y1 = p->my1;
2059
	}
2060
	else {
2061
	    x0 = p->mx1; y0 = p->my1;
2062
	    x1 = p->mx0; y1 = p->my0;
2063
	}
2064
    } else {
2065
	x0 = y0 = 0;
2066
	y1 = NUMLINES - 1;
43882 ripley 2067
	x1 = wcslen(LINE(y1));
10227 ripley 2068
    }
2069
 
2070
    cl = y0; /* current line */
2071
    clinp = rr;
2072
    cp = 1; /* current page */
2073
 
2074
    /* s is possible continuation line */
2075
    while ((cl <= y1) || (*s)) {
2076
	if (clinp + fh >= rr) {
2077
	    if (cp > 1) nextpage(lpr);
2078
	    gdrawstr(lpr, f, Black, pt(left, top), title);
62583 ripley 2079
	    snprintf(msg, LF_FACESIZE + 128, "Page %d", cp++);
24308 ripley 2080
	    gdrawstr(lpr, f, Black,
45067 ripley 2081
		     pt(cc - gstrwidth(lpr, f, msg) - 1, top),
10227 ripley 2082
		     msg);
2083
	    clinp = top + 2 * fh;
2084
	}
2085
	if (!*s) {
45067 ripley 2086
	    if (cl == y0) s = LINE(cl++) + x0;
10227 ripley 2087
	    else if (cl < y1) s = LINE(cl++);
2088
	    else if (cl == y1) {
43882 ripley 2089
		s = wcsncpy(buf, LINE(cl++), 1023);
2090
		s[min(x1, 1023) + 1] = L'\0';
10227 ripley 2091
	    } else break;
2092
	}
2093
	if (!*s) {
2094
	    clinp += fh;
2095
	} else {
43882 ripley 2096
	    wchar_t lc = L'\0';
2097
	    for (i = wcslen(s); i > 0; i--) {
10227 ripley 2098
		lc = s[i];
43882 ripley 2099
		s[i] = L'\0';
2100
		if (gwcswidth(lpr, f, s) < cc) break;
10227 ripley 2101
		s[i] = lc;
2102
	    }
43882 ripley 2103
	    gdrawwcs(lpr, f, Black, pt(left, clinp), s);
10227 ripley 2104
	    clinp += fh;
2105
	    s[i] = lc;
2106
	    s = s + i;
2107
	}
2108
    }
2109
 
2110
    if (f != FixedFont) del(f);
2111
    del(lpr);
2112
    setcursor(cur);
32605 ripley 2113
}
4394 ripley 2114
 
46842 ripley 2115
FILE *R_wfopen(const wchar_t *filename, const wchar_t *mode);
2116
 
17261 ripley 2117
void consolesavefile(console c, int pager)
32605 ripley 2118
{
2119
    ConsoleData p = getdata(c);
2120
 
46842 ripley 2121
    wchar_t *fn;
10976 ripley 2122
    cursor cur;
2123
    FILE *fp;
2124
    int x0, y0, x1, y1, cl;
43882 ripley 2125
    wchar_t *s, buf[1024];
10976 ripley 2126
 
48428 murdoch 2127
    setuserfilterW(L"Text files (*.txt)\0*.txt\0All files (*.*)\0*.*\0\0");
24308 ripley 2128
    if(p->sel)
46842 ripley 2129
	fn = askfilesaveW(G_("Save selection to"), "lastsave.txt");
10976 ripley 2130
    else
46842 ripley 2131
	fn = askfilesaveW(G_("Save console contents to"), "lastsave.txt");
10976 ripley 2132
    show(c);
2133
    if (fn) {
46842 ripley 2134
	fp = R_wfopen(fn, L"wt");
10976 ripley 2135
	if (!fp) return;
2136
	cur = currentcursor();
2137
	setcursor(WatchCursor);
2138
 
2139
	/* Look for a selection */
2140
	if (p->sel) {
2141
	    int len, c1, c2, c3;
2142
	    if (p->my0 >= NUMLINES) p->my0 = NUMLINES - 1;
2143
	    if (p->my0 < 0) p->my0 = 0;
43882 ripley 2144
	    len = wcslen(LINE(p->my0));
10976 ripley 2145
	    if (p->mx0 >= len) p->mx0 = len - 1;
2146
	    if (p->mx0 < 0) p->mx0 = 0;
2147
	    if (p->my1 >= NUMLINES) p->my1 = NUMLINES - 1;
2148
	    if (p->my1 < 0) p->my1 = 0;
43882 ripley 2149
	    len = wcslen(LINE(p->my1));
10976 ripley 2150
	    if (p->mx1 >= len) p->mx1 = len - 1;
2151
	    if (p->mx1 < 0) p->mx1 = 0;
2152
	    c1 = (p->my0 < p->my1);
2153
	    c2 = (p->my0 == p->my1);
2154
	    c3 = (p->mx0 < p->mx1);
2155
	    if (c1 || (c2 && c3)) {
2156
		x0 = p->mx0; y0 = p->my0;
2157
		x1 = p->mx1; y1 = p->my1;
2158
	    }
2159
	    else {
2160
		x0 = p->mx1; y0 = p->my1;
2161
		x1 = p->mx0; y1 = p->my0;
2162
	    }
2163
	} else {
2164
	    x0 = y0 = 0;
2165
	    y1 = NUMLINES - 1;
43882 ripley 2166
	    x1 = wcslen(LINE(y1));
10976 ripley 2167
	}
2168
 
2169
	for (cl = y0; cl <= y1; cl++) {
2170
	    if (cl == y0) s = LINE(cl) + x0;
2171
	    else if (cl < y1) s = LINE(cl);
2172
	    else if (cl == y1) {
43882 ripley 2173
		s = wcsncpy(buf, LINE(cl), 1023);
2174
		s[min(x1, 1023) + 1] = L'\0';
10976 ripley 2175
	    } else break;
43882 ripley 2176
	    fputws(s, fp); fputc('\n', fp);
10976 ripley 2177
	}
2178
	fclose(fp);
2179
	setcursor(cur);
2180
    }
32605 ripley 2181
}
10976 ripley 2182
 
2183
 
4394 ripley 2184
console newconsole(char *name, int flags)
2185
{
2186
    console c;
2187
    ConsoleData p;
2188
 
2189
    p = newconsoledata((consolefn) ? consolefn : FixedFont,
11588 ripley 2190
		       consoler, consolec, consolebufb, consolebufl,
46784 murdoch 2191
		       guiColors,
51948 murdoch 2192
		       CONSOLE, consolebuffered, consoleblink);
4394 ripley 2193
    if (!p) return NULL;
24577 ripley 2194
    c = (console) newwindow(name, rect(consolex, consoley, WIDTH, HEIGHT),
7454 ripley 2195
			    flags | TrackMouse | VScrollbar | HScrollbar);
4394 ripley 2196
    HEIGHT = getheight(c);
2197
    WIDTH  = getwidth(c);
2198
    COLS = WIDTH / FW - 1;
2199
    ROWS = HEIGHT / FH - 1;
4605 ripley 2200
    gsetcursor(c, ArrowCursor);
4394 ripley 2201
    gchangescrollbar(c, VWINSB, 0, 0, ROWS, 1);
7454 ripley 2202
    gchangescrollbar(c, HWINSB, 0, COLS-1, COLS, 1);
4394 ripley 2203
    BORDERX = (WIDTH - COLS*FW) / 2;
2204
    BORDERY = (HEIGHT - ROWS*FH) / 2;
46784 murdoch 2205
    setbackground(c, guiColors[consolebg]);
4394 ripley 2206
    BM = newbitmap(WIDTH, HEIGHT, 2);
2207
    if (!c || !BM ) {
2208
	freeConsoleData(p);
2209
	del(c);
2210
	return NULL;
2211
    }
2212
    setdata(c, p);
9212 ripley 2213
    sethit(c, console_sbf);
4394 ripley 2214
    setresize(c, consoleresize);
2215
    setredraw(c, drawconsole);
2216
    setdel(c, delconsole);
9212 ripley 2217
    setkeyaction(c, console_ctrlkeyin);
2218
    setkeydown(c, console_normalkeyin);
2219
    setmousedrag(c, console_mousedrag);
2220
    setmouserepeat(c, console_mouserep);
2221
    setmousedown(c, console_mousedown);
43908 ripley 2222
    setim(c, console_im);
4394 ripley 2223
    return(c);
2224
}
2225
 
2226
void  consolehelp()
2227
{
2228
    char s[4096];
2229
 
33093 ripley 2230
    strcpy(s,G_("Scrolling.\n"));
2231
    strcat(s,G_("  Keyboard: PgUp, PgDown, Ctrl+Arrows, Ctrl+Home, Ctrl+End,\n"));
2232
    strcat(s,G_("  Mouse: use the scrollbar(s).\n\n"));
2233
    strcat(s,G_("Editing.\n"));
2234
    strcat(s,G_("  Moving the cursor: \n"));
2235
    strcat(s,G_("     Left arrow or Ctrl+B: move backward one character;\n"));
2236
    strcat(s,G_("     Right arrow or Ctrl+F: move forward one character;\n"));
2237
    strcat(s,G_("     Home or Ctrl+A: go to beginning of line;\n"));
2238
    strcat(s,G_("     End or Ctrl+E: go to end of line;\n"));
2239
    strcat(s,G_("  History: Up and Down Arrows, Ctrl+P, Ctrl+N\n"));
2240
    strcat(s,G_("  Deleting:\n"));
41380 murdoch 2241
    strcat(s,G_("     Del or Ctrl+D: delete current character or selection;\n"));
33093 ripley 2242
    strcat(s,G_("     Backspace: delete preceding character;\n"));
2243
    strcat(s,G_("     Ctrl+Del or Ctrl+K: delete text from current character to end of line.\n"));
2244
    strcat(s,G_("     Ctrl+U: delete all text from current line.\n"));
2245
    strcat(s,G_("  Copy and paste.\n"));
2246
    strcat(s,G_("     Use the mouse (with the left button held down) to mark (select) text.\n"));
2247
    strcat(s,G_("     Use Shift+Del (or Ctrl+C) to copy the marked text to the clipboard and\n"));
2248
    strcat(s,G_("     Shift+Ins (or Ctrl+V or Ctrl+Y) to paste the content of the clipboard (if any)  \n"));
2249
    strcat(s,G_("     to the console, Ctrl+X first copy then paste\n"));
2250
    strcat(s,G_("  Misc:\n"));
2251
    strcat(s,G_("     Ctrl+L: Clear the console.\n"));
41380 murdoch 2252
    strcat(s,G_("     Ctrl+O or INS: Toggle overwrite mode: initially off.\n"));
33093 ripley 2253
    strcat(s,G_("     Ctrl+T: Interchange current char with one to the left.\n"));
2254
    strcat(s,G_("\nNote: Console is updated only when some input is required.\n"));
2255
    strcat(s,G_("  Use Ctrl+W to toggle this feature off/on.\n\n"));
2256
    strcat(s,G_("Use ESC to stop the interpreter.\n\n"));
41096 ripley 2257
    strcat(s,G_("TAB starts completion of the current word.\n\n"));
33093 ripley 2258
    strcat(s,G_("Standard Windows hotkeys can be used to switch to the\n"));
2259
    strcat(s,G_("graphics device (Ctrl+Tab or Ctrl+F6 in MDI, Alt+Tab in SDI)"));
4394 ripley 2260
    askok(s);
2261
}
2262
 
10227 ripley 2263
void consoleclear(control c)
32605 ripley 2264
{
2265
    ConsoleData p = getdata(c);
2266
 
10227 ripley 2267
    xbuf l = p->lbuf;
2268
    int oldshift = l->shift;
11075 ripley 2269
 
10227 ripley 2270
    l->shift = (l->ns - 1);
2271
    xbufshift(l);
2272
    l->shift = oldshift;
2273
    NEWFV = 0;
2274
    p->r = 0;
2275
    REDRAW;
32605 ripley 2276
}