1 /*
2  *  R : A Computer Language for Statistical Data Analysis
3  *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
4  *  Copyright (C) 1998--2021  The R Core Team
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, a copy is available at
18  *  https://www.R-project.org/Licenses/
19  */
20 
21 /* TODO
22    - spreadsheet copy and paste?
23  */
24 
25 /* Use of strchr here is MBCS-safe */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 #include "win-nls.h"
31 
32 #include <wchar.h>
33 #include <rlocale.h>
34 
35 #define R_USE_SIGNALS 1
36 #include "Defn.h"
37 #include <Internal.h>
38 #include "Print.h"
39 #include <Rinternals.h>
40 #include <R_ext/Parse.h>  /* parsing is used in handling escape codes */
41 
42 #include "graphapp/ga.h"
43 #include "console.h"
44 #include "consolestructs.h"
45 #include "rui.h"
46 
47 typedef enum {UNKNOWNN, NUMERIC, CHARACTER} CellType;
48 
49 /* Used to check if eventloop needs to be run */
50 static Rboolean R_de_up;
51 
52 #ifndef max
53 #define max(a, b) (((a)>(b))?(a):(b))
54 #endif
55 #ifndef min
56 #define min(a, b) (((a)<(b))?(a):(b))
57 #endif
58 #define BOXW(x) (min(((x<100 && DE->nboxchars == 0) ? DE->boxw[x] : DE->box_w), DE->p->w - DE->boxw[0] - 2*DE->bwidth - 2))
59 
60 #define FIELDWIDTH 10
61 #define BUFSIZE 200
62 
63 typedef struct {
64     dataeditor de;
65     ConsoleData p;
66     int box_w;                       /* width of a box */
67     int boxw[100];                   /* widths of cells */
68     int box_h;                       /* height of a box */
69     int windowWidth;                 /* current width of the window */
70     int windowHeight;                /* current height of the window */
71     int currentexp;                  /* boolean: whether an cell is active */
72     int crow;                        /* current row */
73     int ccol;                        /* current column */
74     int nwide, nhigh;
75     int colmax, colmin, rowmax, rowmin;
76     int ndecimal;                    /* count decimal points */
77     int ne;                          /* count exponents */
78     int nneg;			     /* indicate whether its a negative */
79     int clength;                     /* number of characters currently entered */
80     int inSpecial;
81     char buf[BUFSIZE];
82     char *bufp;
83     int bwidth;			/* width of the border */
84     int hwidth;			/* width of header  */
85     int text_xoffset, text_yoffset;
86     field celledit;
87     Rboolean CellModified, CellEditable;
88     int xmaxused, ymaxused;
89     int oldWIDTH, oldHEIGHT;
90     int nboxchars;
91     char labform[6];
92     int xScrollbarScale, yScrollbarScale;
93     SEXP work, names, lens;
94     PROTECT_INDEX wpi, npi, lpi;
95     menuitem de_mvw;
96     SEXP ssNA_STRING;
97     Rboolean isEditor;
98 } destruct, *DEstruct;
99 
100 /* Local Function Definitions */
101 
102 static void advancerect(DEstruct, int);
103 static void bell(void);
104 static void cleararea(DEstruct, int, int, int, int, rgb);
105 static void clearrect(DEstruct);
106 static void closerect(DEstruct);
107 static void clearwindow(DEstruct);
108 static void de_closewin(DEstruct);
109 static void copyarea(DEstruct, int, int, int, int);
110 static void copyH(DEstruct, int, int, int);
111 static void deredraw(DEstruct);
112 static void downlightrect(DEstruct);
113 static void drawwindow(DEstruct);
114 static void drawcol(DEstruct, int);
115 /* static void de_drawline(int, int, int, int);*/
116 static void de_drawtext(DEstruct, int, int, const char *);
117 static void drawrectangle(DEstruct, int, int, int, int, int, int);
118 static void drawrow(DEstruct, int);
119 static void find_coords(DEstruct, int, int, int*, int*);
120 static void handlechar(DEstruct, const char *);
121 static void highlightrect(DEstruct);
122 static Rboolean initwin(DEstruct, const char *);
123 static void jumppage(DEstruct, int);
124 static void jumpwin(DEstruct, int, int);
125 static void de_popupmenu(DEstruct, int, int, int);
126 static void printlabs(DEstruct);
127 static void printrect(DEstruct, int, int);
128 static void printstring(DEstruct, const char *, int, int, int, int);
129 static void printelt(DEstruct, SEXP, int, int, int);
130 static void setcellwidths(DEstruct);
131 
132 static dataeditor newdataeditor(DEstruct, const char *);
133 static void de_copy(control c);
134 static void de_paste(control c);
135 static void de_delete(control c);
136 
137 
138 #define WIN32_LEAN_AND_MEAN 1
139 #include <windows.h> /* for Sleep */
140 
mb_char_len(const char * buf,int clength)141 int mb_char_len(const char *buf, int clength)
142 {
143     int i, mb_len = 0;
144 
145     for(i = 0; i <= clength; i += mb_len)
146 	mb_len = mbrtowc(NULL, buf+i, R_MB_CUR_MAX, NULL);
147     return mb_len;
148 }
149 
moveback(DEstruct DE)150 static void moveback(DEstruct DE)
151 {
152     int mb_len;
153 
154     if (DE->clength > 0) {
155 	mb_len = mb_char_len(DE->buf, DE->clength-1);
156 	DE->clength -= mb_len;
157 	DE->bufp -= mb_len;
158 	printstring(DE, DE->buf, DE->clength, DE->crow, DE->ccol, 1);
159     } else bell();
160 }
161 
162 
163  /*
164   Underlying assumptions (for this version R >= 1.8.0)
165 
166   The data are stored in a list `work', with unused columns having
167   NULL entries.  The names for the list are in `names', which should
168   have a name for all displayable columns (up to xmaxused).
169   The *used* lengths of the columns are in `lens': this needs only be
170   set for non-NULL columns.
171 
172   If the list was originally length(0), that should work with
173   0 pre-defined cols.  (It used to have 1 pre-defined numeric column.)
174 
175   All row and col numbers are 1-based.
176 
177   BDR May 2003
178  */
179 
180 /*
181    ssNewVector is just an interface to allocVector but it lets us set
182    the fields to NA. We need to have a special NA for strings so that
183    we can differentiate between uninitialized elements in the vectors
184    and user supplied NA's; hence ssNA_STRING
185  */
186 
ssNewVector(DEstruct DE,SEXPTYPE type,int vlen)187 static SEXP ssNewVector(DEstruct DE, SEXPTYPE type, int vlen)
188 {
189     SEXP tvec;
190     int j;
191 
192     tvec = allocVector(type, vlen);
193     for (j = 0; j < vlen; j++)
194 	if (type == REALSXP)
195 	    REAL(tvec)[j] = NA_REAL;
196 	else if (type == STRSXP)
197 	    SET_STRING_ELT(tvec, j, DE->ssNA_STRING);
198     return (tvec);
199 }
200 
de_closewin_cend(void * DE)201 static void de_closewin_cend(void *DE)
202 {
203     de_closewin((DEstruct) DE);
204 }
205 
Win_dataentry(SEXP args)206 SEXP Win_dataentry(SEXP args)
207 {
208     SEXP colmodes, tnames, tvec, tvec2, work2;
209     SEXPTYPE type;
210     int i, j, cnt, len, nprotect;
211     RCNTXT cntxt;
212     char clab[25];
213     destruct DE1;
214     DEstruct DE = &DE1;
215 
216     DE->isEditor = TRUE;
217     nprotect = 0;/* count the PROTECT()s */
218     PROTECT_WITH_INDEX(DE->work = duplicate(CAR(args)), &DE->wpi); nprotect++;
219     colmodes = CADR(args);
220     tnames = getAttrib(DE->work, R_NamesSymbol);
221 
222     if (TYPEOF(DE->work) != VECSXP || TYPEOF(colmodes) != VECSXP)
223 	error(G_("invalid argument"));
224 
225     /* initialize the constants */
226 
227     DE->bufp = DE->buf;
228     DE->ne = 0;
229     DE->currentexp = 0;
230     DE->nneg = 0;
231     DE->ndecimal = 0;
232     DE->clength = 0;
233     DE->inSpecial = 0;
234     DE->ccol = 1;
235     DE->crow = 1;
236     DE->colmin = 1;
237     DE->rowmin = 1;
238     PROTECT(DE->ssNA_STRING = duplicate(NA_STRING));
239     nprotect++;
240     DE->bwidth = 0;
241     DE->hwidth = 5;
242 
243     /* setup work, names, lens  */
244     DE->xmaxused = length(DE->work); DE->ymaxused = 0;
245     PROTECT_WITH_INDEX(DE->lens = allocVector(INTSXP, DE->xmaxused), &DE->lpi);
246     nprotect++;
247 
248     if (isNull(tnames)) {
249 	PROTECT_WITH_INDEX(DE->names = allocVector(STRSXP, DE->xmaxused),
250 			   &DE->npi);
251 	for(i = 0; i < DE->xmaxused; i++) {
252 	    snprintf(clab, 25, "var%d", i);
253 	    SET_STRING_ELT(DE->names, i, mkChar(clab));
254 	}
255     } else
256 	PROTECT_WITH_INDEX(DE->names = duplicate(tnames), &DE->npi);
257     nprotect++;
258     for (i = 0; i < DE->xmaxused; i++) {
259 	int len = LENGTH(VECTOR_ELT(DE->work, i));
260 	INTEGER(DE->lens)[i] = len;
261 	DE->ymaxused = max(len, DE->ymaxused);
262 	type = TYPEOF(VECTOR_ELT(DE->work, i));
263 	if (LENGTH(colmodes) > 0 && !isNull(VECTOR_ELT(colmodes, i)))
264 	    type = str2type(CHAR(STRING_ELT(VECTOR_ELT(colmodes, i), 0)));
265 	if (type != STRSXP) type = REALSXP;
266 	if (isNull(VECTOR_ELT(DE->work, i))) {
267 	    if (type == NILSXP) type = REALSXP;
268 	    SET_VECTOR_ELT(DE->work, i, ssNewVector(DE, type, 100));
269 	} else if (!isVector(VECTOR_ELT(DE->work, i)))
270 	    error(G_("invalid type for value"));
271 	else {
272 	    if (TYPEOF(VECTOR_ELT(DE->work, i)) != type)
273 		SET_VECTOR_ELT(DE->work, i,
274 			       coerceVector(VECTOR_ELT(DE->work, i), type));
275 	}
276     }
277 
278     DE->xScrollbarScale = DE->yScrollbarScale = 1;
279 
280     /* start up the window, more initializing in here */
281     if (initwin(DE, G_("Data Editor")))
282 	error("unable to start data editor");
283     R_de_up = TRUE;
284 
285     /* set up a context which will close the window if there is an error */
286     begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
287 		 R_NilValue, R_NilValue);
288     cntxt.cend = &de_closewin_cend;
289     cntxt.cenddata = (void *)DE;
290 
291     highlightrect(DE);
292 
293     while (R_de_up) {
294 	/* avoid consuming 100% CPU time here */
295 	R_WaitEvent();
296 	R_ProcessEvents();
297     }
298 
299     endcontext(&cntxt);
300 
301     /* drop out unused columns */
302     for(i = 0, cnt = 0; i < DE->xmaxused; i++)
303 	if(!isNull(VECTOR_ELT(DE->work, i))) cnt++;
304     if (cnt < DE->xmaxused) {
305 	PROTECT(work2 = allocVector(VECSXP, cnt)); nprotect++;
306 	for(i = 0, j = 0; i < DE->xmaxused; i++) {
307 	    if(!isNull(VECTOR_ELT(DE->work, i))) {
308 		SET_VECTOR_ELT(work2, j, VECTOR_ELT(DE->work, i));
309 		INTEGER(DE->lens)[j] = INTEGER(DE->lens)[i];
310 		SET_STRING_ELT(DE->names, j, STRING_ELT(DE->names, i));
311 		j++;
312 	    }
313 	}
314 	REPROTECT(DE->names = lengthgets(DE->names, cnt), DE->npi);
315     } else work2 = DE->work;
316 
317     for (i = 0; i < LENGTH(work2); i++) {
318 	len = INTEGER(DE->lens)[i];
319 	tvec = VECTOR_ELT(work2, i);
320 	if (LENGTH(tvec) != len) {
321 	    tvec2 = ssNewVector(DE, TYPEOF(tvec), len);
322 	    for (j = 0; j < len; j++) {
323 		if (TYPEOF(tvec) == REALSXP) {
324 			REAL(tvec2)[j] = REAL(tvec)[j];
325 		} else if (TYPEOF(tvec) == STRSXP) {
326 		    if (STRING_ELT(tvec, j) != DE->ssNA_STRING)
327 			SET_STRING_ELT(tvec2, j, STRING_ELT(tvec, j));
328 		    else
329 			SET_STRING_ELT(tvec2, j, NA_STRING);
330 		} else
331 		    error(G_("dataentry: internal memory problem"));
332 	    }
333 	    SET_VECTOR_ELT(work2, i, tvec2);
334 	}
335     }
336 
337     setAttrib(work2, R_NamesSymbol, DE->names);
338     UNPROTECT(nprotect);
339     return work2;
340 }
341 
342 /* Window Drawing Routines */
343 
344 static rgb bbg;
345 
setcellwidths(DEstruct DE)346 static void setcellwidths(DEstruct DE)
347 {
348     int i, w, dw;
349 
350     DE->windowWidth = w = 2*DE->bwidth + DE->boxw[0] + BOXW(DE->colmin);
351     DE->nwide = 2;
352     for (i = 2; i < 100; i++) { /* 100 on-screen columns cannot occur */
353 	dw = BOXW(i + DE->colmin - 1);
354 	if((w += dw) > DE->p->w ||
355 	   (!DE->isEditor && i > DE->xmaxused - DE->colmin + 1)
356 	    ) {
357 	    DE->nwide = i;
358 	    DE->windowWidth = w - dw;
359 	    break;
360 	}
361     }
362 }
363 
364 
drawwindow(DEstruct DE)365 static void drawwindow(DEstruct DE)
366 {
367     /* might have resized */
368     setcellwidths(DE);
369     DE->nhigh = (DE->p->h - 2 * DE->bwidth - DE->hwidth - 3) / DE->box_h;
370     if(!DE->isEditor && DE->nhigh > DE->ymaxused+1) DE->nhigh = DE->ymaxused+1;
371     DE->windowHeight = DE->nhigh * DE->box_h + 2 * DE->bwidth + DE->hwidth;
372     DE->oldWIDTH = DE->p->w;
373     DE->oldHEIGHT = DE->p->h;
374 
375     clearwindow(DE);
376     deredraw(DE);
377     /* row/col 1 = pos 0 */
378     gchangescrollbar(DE->de, VWINSB, (DE->rowmin - 1)/DE->yScrollbarScale,
379 		     DE->ymaxused/DE->yScrollbarScale,
380 		     max(DE->nhigh/DE->yScrollbarScale, 1), 0);
381     gchangescrollbar(DE->de, HWINSB, (DE->colmin - 1)/DE->xScrollbarScale,
382 		     DE->xmaxused/DE->xScrollbarScale,
383 		     max(DE->nwide/DE->xScrollbarScale, 1), 0);
384 }
385 
doHscroll(DEstruct DE,int oldcol)386 static void doHscroll(DEstruct DE, int oldcol)
387 {
388     int i, dw;
389     int oldnwide = DE->nwide, oldwindowWidth = DE->windowWidth;
390 
391     /* horizontal re-position */
392     setcellwidths(DE);
393     DE->colmax = DE->colmin + (DE->nwide - 2);
394     if (oldcol < DE->colmin) { /* drop oldcol...colmin - 1 */
395 	dw = DE->boxw[0];
396 	for (i = oldcol; i < DE->colmin; i++) dw += BOXW(i);
397 	copyH(DE, dw, DE->boxw[0], oldwindowWidth - dw + 1);
398 	dw = oldwindowWidth - BOXW(oldcol) + 1;
399 	cleararea(DE, dw, DE->hwidth, DE->p->w-dw, DE->p->h, DE->p->guiColors[dataeditbg]);
400 	/* oldnwide includes the row labels */
401 	for (i = oldcol+oldnwide-1; i <= DE->colmax; i++) drawcol(DE, i);
402     } else {
403 	/* move one or more cols left */
404 	dw = BOXW(DE->colmin);
405 	copyH(DE, DE->boxw[0], DE->boxw[0] + dw, DE->windowWidth - dw + 1);
406 	dw = DE->windowWidth + 1;
407 	cleararea(DE, dw, DE->hwidth, DE->p->w-dw, DE->p->h, DE->p->guiColors[dataeditbg]);
408 	drawcol(DE, DE->colmin);
409     }
410     gchangescrollbar(DE->de, HWINSB, (DE->colmin - 1)/DE->xScrollbarScale,
411 		     DE->xmaxused/DE->xScrollbarScale,
412 		     max(DE->nwide/DE->xScrollbarScale, 1), 0);
413     highlightrect(DE);
414 }
415 
416 /* find_coords finds the coordinates of the upper left corner of the
417    given cell on the screen: row and col are on-screen coords */
418 
find_coords(DEstruct DE,int row,int col,int * xcoord,int * ycoord)419 static void find_coords(DEstruct DE,
420 			int row, int col, int *xcoord, int *ycoord)
421 {
422     int i, w;
423     w = DE->bwidth;
424     if (col > 0) w += DE->boxw[0];
425     for(i = 1; i < col; i ++) w += BOXW(i + DE->colmin - 1);
426     *xcoord = w;
427     *ycoord = DE->bwidth + DE->hwidth + DE->box_h * row;
428 }
429 
430 /* draw the window with the top left box at column wcol and row wrow */
431 
jumpwin(DEstruct DE,int wcol,int wrow)432 static void jumpwin(DEstruct DE, int wcol, int wrow)
433 {
434     if (wcol < 0 || wrow < 0) {
435 	bell();
436 	return;
437     }
438     closerect(DE);
439     if (DE->colmin != wcol || DE->rowmin != wrow) {
440 	DE->colmin = wcol;
441 	DE->rowmin = wrow;
442 	deredraw(DE);
443 	gchangescrollbar(DE->de, VWINSB, (DE->rowmin - 1)/DE->yScrollbarScale,
444 			 DE->ymaxused/DE->yScrollbarScale,
445 			 max(DE->nhigh/DE->yScrollbarScale, 1), 0);
446 	gchangescrollbar(DE->de, HWINSB, (DE->colmin - 1)/DE->xScrollbarScale,
447 			 DE->xmaxused/DE->xScrollbarScale,
448 			 max(DE->nwide/DE->xScrollbarScale, 1), 0);
449     } else highlightrect(DE);
450 }
451 
advancerect(DEstruct DE,int which)452 static void advancerect(DEstruct DE, int which)
453 {
454 
455     /* if we are in the header, changing a name then only down is
456        allowed */
457     if (DE->crow < 1 && which != DOWN) {
458 	bell();
459 	return;
460     }
461 
462     closerect(DE);
463 
464     switch (which) {
465     case UP:
466 	if (DE->crow == 1) {
467 	    if (DE->rowmin == 1)
468 		bell();
469 	    else
470 		jumppage(DE, UP);
471 	} else
472 	    DE->crow--;
473 	break;
474     case DOWN:
475 	if (DE->crow == (DE->nhigh - 1))
476 	    jumppage(DE, DOWN);
477 	else
478 	    DE->crow++;
479 	break;
480     case RIGHT:
481 	if (DE->ccol == (DE->nwide - 1))
482 	    jumppage(DE, RIGHT);
483 	else
484 	    DE->ccol++;
485 	break;
486     case LEFT:
487 	if (DE->ccol == 1) {
488 	    if (DE->colmin == 1)
489 		bell();
490 	    else
491 		jumppage(DE, LEFT);
492 	} else
493 	    DE->ccol--;
494 	break;
495     default:
496 	UNIMPLEMENTED("advancerect");
497     }
498 
499     highlightrect(DE);
500 }
501 
get_col_name(DEstruct DE,int col)502 static const char *get_col_name(DEstruct DE, int col)
503 {
504     static char clab[25];
505     if (col <= DE->xmaxused) {
506 	/* don't use NA labels */
507 	SEXP tmp = STRING_ELT(DE->names, col - 1);
508 	if(tmp != NA_STRING) return(CHAR(tmp));
509     }
510     snprintf(clab, 25, "var%d", col);
511     return clab;
512 }
513 
get_col_width(DEstruct DE,int col)514 static int get_col_width(DEstruct DE, int col)
515 {
516     int i, w = 0, w1, fw = FIELDWIDTH;
517     const char *strp;
518     SEXP tmp, lab;
519 
520     if (DE->nboxchars > 0) return DE->nboxchars;
521     if (col <= DE->xmaxused) {
522 	tmp = VECTOR_ELT(DE->work, col - 1);
523 	if (isNull(tmp)) return fw;
524 	/* don't use NA labels */
525 	lab = STRING_ELT(DE->names, col - 1);
526 	if(lab != NA_STRING) w = strlen(CHAR(lab)); else w = fw;
527 	PrintDefaults();
528 	for (i = 0; i < INTEGER(DE->lens)[col - 1]; i++) {
529 	    strp = EncodeElement(tmp, i, 0, '.');
530 	    w1 = strlen(strp);
531 	    if (w1 > w) w = w1;
532 	}
533 	if(w < 5) w = 5;
534 	if(w < 8) w++;
535 	if(w > 50) w = 50;
536 	return w;
537     }
538     return fw;
539 }
540 
get_col_type(DEstruct DE,int col)541 static CellType get_col_type(DEstruct DE, int col)
542 {
543     SEXP tmp;
544     CellType res = UNKNOWNN;
545 
546     if (col <= DE->xmaxused) {
547 	tmp = VECTOR_ELT(DE->work, col - 1);
548 	if(TYPEOF(tmp) == REALSXP) res = NUMERIC;
549 	if(TYPEOF(tmp) == STRSXP) res = CHARACTER;
550     }
551     return res;
552 }
553 
554 
555 /* whichcol is absolute col no, col is position on screen */
drawcol(DEstruct DE,int whichcol)556 static void drawcol(DEstruct DE, int whichcol)
557 {
558     int i, src_x, src_y, len, col = whichcol - DE->colmin + 1,
559 	bw = BOXW(whichcol);
560     const char *clab;
561     SEXP tmp;
562 
563     find_coords(DE, 0, col, &src_x, &src_y);
564     cleararea(DE, src_x, src_y, bw, DE->windowHeight, DE->p->guiColors[dataeditbg]);
565     cleararea(DE, src_x, src_y, bw, DE->box_h, bbg);
566     for (i = 0; i < DE->nhigh; i++)
567 	drawrectangle(DE, src_x, DE->hwidth + i * DE->box_h, bw, DE->box_h, 1, 1);
568 
569     /* now fill it in if it is active */
570     clab = get_col_name(DE, whichcol);
571     printstring(DE, clab, strlen(clab), 0, col, 0);
572 
573     if (DE->xmaxused >= whichcol) {
574 	tmp = VECTOR_ELT(DE->work, whichcol - 1);
575 	if (!isNull(tmp)) {
576 	    len = min(DE->rowmax, INTEGER(DE->lens)[whichcol - 1]);
577 	    for (i = (DE->rowmin - 1); i < len; i++)
578 		printelt(DE, tmp, i, i - DE->rowmin + 2, col);
579 	}
580     }
581 }
582 
583 
584 /* whichrow is absolute row no */
drawrow(DEstruct DE,int whichrow)585 static void drawrow(DEstruct DE, int whichrow)
586 {
587     int i, src_x, src_y, row = whichrow - DE->rowmin + 1, w;
588     char rlab[15];
589     SEXP tvec;
590 
591     find_coords(DE, row, 0, &src_x, &src_y);
592     cleararea(DE, src_x, src_y, DE->windowWidth, DE->box_h,
593 	      (whichrow > 0) ? DE->p->guiColors[dataeditbg] : bbg);
594     drawrectangle(DE, src_x, src_y, DE->boxw[0], DE->box_h, 1, 1);
595 
596     snprintf(rlab, 15, DE->labform, whichrow);
597     printstring(DE, rlab, strlen(rlab), row, 0, 0);
598 
599     w = DE->bwidth + DE->boxw[0];
600     for (i = DE->colmin; i <= DE->colmax; i++) {
601 	drawrectangle(DE, w, src_y, BOXW(i), DE->box_h, 1, 1);
602 	w += BOXW(i);
603     }
604 
605     for (i = DE->colmin; i <= DE->colmax; i++) {
606 	if (i > DE->xmaxused) break;
607 	if (!isNull(tvec = VECTOR_ELT(DE->work, i - 1)))
608 	    if (whichrow <= INTEGER(DE->lens)[i - 1])
609 		printelt(DE, tvec, whichrow - 1, row, i - DE->colmin + 1);
610     }
611 }
612 
613 /* printelt: print the correct value from vector[vrow] into the
614    spreadsheet in row ssrow and col sscol */
615 
616 /* WARNING: This has no check that you're not beyond the end of the
617    vector. Caller must check. */
618 
printelt(DEstruct DE,SEXP invec,int vrow,int ssrow,int sscol)619 static void printelt(DEstruct DE, SEXP invec, int vrow, int ssrow, int sscol)
620 {
621     const char *strp;
622     PrintDefaults();
623     if (TYPEOF(invec) == REALSXP) {
624 	strp = EncodeElement(invec, vrow, 0, '.');
625 	printstring(DE, strp, strlen(strp), ssrow, sscol, 0);
626     }
627     else if (TYPEOF(invec) == STRSXP) {
628 	if (STRING_ELT(invec, vrow) != DE->ssNA_STRING) {
629 	    strp = EncodeElement(invec, vrow, 0, '.');
630 	    printstring(DE, strp, strlen(strp), ssrow, sscol, 0);
631 	}
632     }
633     else
634 	error(G_("dataentry: internal memory error"));
635 }
636 
637 
drawelt(DEstruct DE,int whichrow,int whichcol)638 static void drawelt(DEstruct DE, int whichrow, int whichcol)
639 {
640     int i;
641     const char *clab;
642     SEXP tmp;
643 
644     if (whichrow == 0) {
645 	clab = get_col_name(DE, whichcol + DE->colmin - 1);
646 	printstring(DE, clab, strlen(clab), 0, whichcol, 0);
647     } else {
648 	if (DE->xmaxused >= whichcol + DE->colmin - 1) {
649 	    tmp = VECTOR_ELT(DE->work, whichcol + DE->colmin - 2);
650 	    if (!isNull(tmp) && (i = DE->rowmin + whichrow - 2) <
651 		INTEGER(DE->lens)[whichcol + DE->colmin - 2] )
652 		printelt(DE, tmp, i, whichrow, whichcol);
653 	} else
654 	    printstring(DE, "", 0, whichrow,  whichcol, 0);
655     }
656 }
657 
jumppage(DEstruct DE,int dir)658 static void jumppage(DEstruct DE, int dir)
659 {
660     int i, w, oldcol, wcol;
661 
662     switch (dir) {
663     case UP:
664 	DE->rowmin--;
665 	DE->rowmax--;
666 	copyarea(DE, 0, DE->hwidth + DE->box_h, 0, DE->hwidth + 2 * DE->box_h);
667 	drawrow(DE, DE->rowmin);
668 	gchangescrollbar(DE->de, VWINSB, (DE->rowmin - 1)/DE->yScrollbarScale,
669 			 DE->ymaxused/DE->yScrollbarScale,
670 			 max(DE->nhigh/DE->yScrollbarScale, 1), 0);
671 	break;
672     case DOWN:
673 	// if (DE->rowmax >= 65535) return;
674 	DE->rowmin++;
675 	DE->rowmax++;
676 	copyarea(DE, 0, DE->hwidth + 2 * DE->box_h, 0, DE->hwidth + DE->box_h);
677 	drawrow(DE, DE->rowmax);
678 	gchangescrollbar(DE->de, VWINSB, (DE->rowmin - 1)/DE->yScrollbarScale,
679 			 DE->ymaxused/DE->yScrollbarScale,
680 			 max(DE->nhigh/DE->yScrollbarScale, 1), 0);
681 	break;
682     case LEFT:
683 	DE->colmin--;
684 	doHscroll(DE, DE->colmin + 1);
685 	break;
686     case RIGHT:
687 	oldcol = DE->colmin;
688 	wcol = DE->colmin + DE->ccol + 1; /* column to be selected */
689 	/* There may not be room to fit the next column in */
690 	w = DE->p->w - DE->boxw[0] - BOXW(DE->colmax + 1);
691 	for (i = DE->colmax; i >= oldcol; i--) {
692 	    w -= BOXW(i);
693 	    if(w < 0) {
694 		DE->colmin = i + 1;
695 		break;
696 	    }
697 	}
698 	DE->ccol = wcol - DE->colmin;
699 	doHscroll(DE, oldcol);
700 	break;
701     }
702 }
703 /* draw a rectangle, used to highlight/downlight the current box */
704 
printrect(DEstruct DE,int lwd,int fore)705 static void printrect(DEstruct DE, int lwd, int fore)
706 {
707     int x, y;
708     find_coords(DE, DE->crow, DE->ccol, &x, &y);
709     drawrectangle(DE, x + lwd - 1, y + lwd - 1,
710 		  BOXW(DE->ccol+DE->colmin-1) - lwd + 1,
711 		  DE->box_h - lwd + 1, lwd, fore);
712 }
713 
downlightrect(DEstruct DE)714 static void downlightrect(DEstruct DE)
715 {
716     printrect(DE, 2, 0);
717     printrect(DE, 1, 1);
718 }
719 
highlightrect(DEstruct DE)720 static void highlightrect(DEstruct DE)
721 {
722     if(DE->isEditor)
723 	printrect(DE, 2, 1);
724     else
725 	printrect(DE, 1, 1);
726 }
727 
728 
getccol(DEstruct DE)729 static Rboolean getccol(DEstruct DE)
730 {
731     SEXP tmp, tmp2;
732     int i, len, newlen, wcol, wrow;
733     SEXPTYPE type;
734     char clab[25];
735     Rboolean newcol = FALSE;
736 
737     wcol = DE->ccol + DE->colmin - 1;
738     wrow = DE->crow + DE->rowmin - 1;
739     if (wcol > DE->xmaxused) {
740 	/* extend work, names and lens */
741 	REPROTECT(DE->work = lengthgets(DE->work, wcol), DE->wpi);
742 	REPROTECT(DE->names = lengthgets(DE->names, wcol), DE->npi);
743 	for (i = DE->xmaxused; i < wcol; i++) {
744 	    snprintf(clab, 25, "var%d", i + 1);
745 	    SET_STRING_ELT(DE->names, i, mkChar(clab));
746 	}
747 	REPROTECT(DE->lens = lengthgets(DE->lens, wcol), DE->lpi);
748 	DE->xmaxused = wcol;
749     }
750     if (isNull(VECTOR_ELT(DE->work, wcol - 1))) {
751 	newcol = TRUE;
752 	SET_VECTOR_ELT(DE->work, wcol - 1,
753 		       ssNewVector(DE, REALSXP, max(100, wrow)));
754 	INTEGER(DE->lens)[wcol - 1] = 0;
755     }
756     if (!isVector(tmp = VECTOR_ELT(DE->work, wcol - 1)))
757 	error(G_("internal type error in dataentry"));
758     len = INTEGER(DE->lens)[wcol - 1];
759     type = TYPEOF(tmp);
760     if (len < wrow) {
761 	for (newlen = max(len * 2, 10) ; newlen < wrow ; newlen *= 2)
762 	    ;
763 	tmp2 = ssNewVector(DE, type, newlen);
764 	for (i = 0; i < len; i++)
765 	    if (type == REALSXP)
766 		REAL(tmp2)[i] = REAL(tmp)[i];
767 	    else if (type == STRSXP)
768 		SET_STRING_ELT(tmp2, i, STRING_ELT(tmp, i));
769 	    else
770 		error(G_("internal type error in dataentry"));
771 	SET_VECTOR_ELT(DE->work, wcol - 1, tmp2);
772     }
773     return newcol;
774 }
775 
processEscapes(SEXP x)776 static SEXP processEscapes(SEXP x)
777 {
778     SEXP newval, pattern, replacement, expr;
779     ParseStatus status;
780 
781     /* We process escape sequences in a scalar string by escaping
782        unescaped quotes, then quoting the whole thing and parsing it.  This
783        is supposed to be equivalent to the R code
784 
785        newval <- gsub(perl=TRUE, "(?<!\\\\)((\\\\\\\\)*)\"", "\\1\\\\\"", x)
786        newval <- sub('(^.*$)', '"\1"', newval)
787        newval <- eval(parse(text=newval))
788 
789        We do it this way to avoid extracting the escape handling
790        code from the parser.  We need it in C code because this may be executed
791        numerous times from C in dataentry.c */
792 
793     PROTECT( pattern = mkString("(?<!\\\\)((\\\\\\\\)*)\"") );
794     PROTECT( replacement = mkString("\\1\\\\\"") );
795     SEXP s_gsub = install("gsub");
796     PROTECT( expr = lang5(s_gsub, ScalarLogical(1), pattern, replacement, x) );
797     SET_TAG( CDR(expr), install("perl") );
798 
799     PROTECT( newval = eval(expr, R_BaseEnv) );
800     PROTECT( pattern = mkString("(^.*$)") );
801     PROTECT( replacement = mkString("\"\\1\"") );
802     PROTECT( expr = lang4(install("sub"), pattern, replacement, newval) );
803     PROTECT( newval = eval(expr, R_BaseEnv) );
804     PROTECT( expr = R_ParseVector( newval, 1, &status, R_NilValue) );
805 
806     /* We only handle the first entry. If this were available more generally,
807        we'd probably want to loop over all of expr */
808 
809     if (status == PARSE_OK && length(expr))
810 	PROTECT( newval = eval(VECTOR_ELT(expr, 0), R_BaseEnv) );
811     else
812 	PROTECT( newval = R_NilValue );  /* protect just so the count doesn't change */
813     UNPROTECT(10);
814     return newval;
815 }
816 
817 /* close up the entry to a cell, put the value that has been entered
818    into the correct place and as the correct type */
819 
closerect(DEstruct DE)820 static void closerect(DEstruct DE)
821 {
822     SEXP cvec;
823     int wcol = DE->ccol + DE->colmin - 1, wrow = DE->rowmin + DE->crow - 1,
824 	wrow0;
825     Rboolean newcol;
826 
827     *(DE->bufp) = '\0';
828 
829     if (DE->CellModified || DE->CellEditable) {
830 	if (DE->CellEditable) {
831 	    strncpy(DE->buf, GA_gettext(DE->celledit), BUFSIZE-1);
832 	    DE->clength = strlen(DE->buf);
833 	    hide(DE->celledit);
834 	    del(DE->celledit);
835 	}
836 	newcol = getccol(DE);
837 	cvec = VECTOR_ELT(DE->work, wcol - 1);
838 	wrow0 = INTEGER(DE->lens)[wcol - 1];
839 	if (wrow > wrow0) INTEGER(DE->lens)[wcol - 1] = wrow;
840 	DE->ymaxused = max(DE->ymaxused, wrow);
841 	if (DE->clength != 0) {
842 	    /* do it this way to ensure NA, Inf, ...  can get set */
843 	    char *endp;
844 	    double new = R_strtod(DE->buf, &endp);
845 	    int warn = !isBlankString(endp);
846 	    if (TYPEOF(cvec) == STRSXP) {
847 	    	SEXP newval;
848 	    	PROTECT( newval = mkString(DE->buf) );
849 	    	PROTECT( newval = processEscapes(newval) );
850 	    	if (TYPEOF(newval) == STRSXP && length(newval) == 1)
851 		    SET_STRING_ELT(cvec, wrow - 1, STRING_ELT(newval, 0));
852 		else
853 		    warning(G_("dataentry: parse error on string"));
854 		UNPROTECT(2);
855 	    } else
856 		REAL(cvec)[wrow - 1] = new;
857 	    if (newcol && warn) {
858 		/* change mode to character */
859 		SEXP tmp = coerceVector(cvec, STRSXP);
860 		SET_STRING_ELT(tmp, wrow - 1, mkChar(DE->buf));
861 		SET_VECTOR_ELT(DE->work, wcol - 1, tmp);
862 	    }
863 	} else {
864 	    if (TYPEOF(cvec) == STRSXP)
865 		SET_STRING_ELT(cvec, wrow - 1, NA_STRING);
866 	    else
867 		REAL(cvec)[wrow - 1] = NA_REAL;
868 	}
869 	drawelt(DE, DE->crow, DE->ccol);  /* to get the cell scrolling right */
870 	if(wrow > wrow0) drawcol(DE, wcol); /* to fill in NAs */
871     }
872     DE->CellEditable = DE->CellModified = FALSE;
873 
874     downlightrect(DE);
875     gsetcursor(DE->de, ArrowCursor);
876 
877     DE->ndecimal = 0;
878     DE->nneg = 0;
879     DE->ne = 0;
880     DE->currentexp = 0;
881     DE->clength = 0;
882     DE->inSpecial = 0;
883     DE->bufp = DE->buf;
884 }
885 
886 /* print a null terminated string, check to see if it is longer than
887    the print area and print it, left adjusted if necessary; clear the
888    area of previous text; */
889 
890 /* This version will only display BUFSIZE chars, but the maximum col width
891    will not allow that many */
printstring(DEstruct DE,const char * ibuf,int buflen,int row,int col,int left)892 static void printstring(DEstruct DE, const char *ibuf, int buflen,
893 			int row, int col, int left)
894 {
895     int x_pos, y_pos, bw, fw, bufw;
896     char buf[BUFSIZE+1];
897 
898     find_coords(DE, row, col, &x_pos, &y_pos);
899     if (col == 0) bw = DE->boxw[0]; else bw = BOXW(col+DE->colmin-1);
900     cleararea(DE, x_pos + 1, y_pos + 1, bw - 1, DE->box_h - 1,
901 	      (row==0 || col==0) ? bbg:DE->p->guiColors[dataeditbg]);
902     fw = min(BUFSIZE, (bw - 8)/(DE->p->fw));
903     bufw = min(fw, buflen);
904     strncpy(buf, ibuf, bufw);
905     buf[bufw] = '\0';
906     if (buflen > fw) {
907 	if(left) {
908 	    strncpy(buf, ibuf + buflen - fw, fw);
909 	    buf[fw] = '\0';
910 	    *buf = '<';
911 	} else {
912 	    *(buf + fw - 1) = '>';
913 	    *(buf + fw) = '\0';
914 	}
915     }
916     de_drawtext(DE, x_pos + DE->text_xoffset, y_pos - DE->text_yoffset, buf);
917 }
918 
clearrect(DEstruct DE)919 static void clearrect(DEstruct DE)
920 {
921     int x_pos, y_pos;
922 
923     find_coords(DE, DE->crow, DE->ccol, &x_pos, &y_pos);
924     cleararea(DE, x_pos, y_pos, BOXW(DE->ccol+DE->colmin-1),
925 	      DE->box_h, DE->p->guiColors[dataeditbg]);
926 }
927 
928 /* handlechar has to be able to parse decimal numbers and strings,
929    depending on the current column type, only printing characters
930    should get this far */
931 
932 /* --- Not true! E.g. ESC ends up in here... */
933 
handlechar(DEstruct DE,const char * text)934 static void handlechar(DEstruct DE, const char *text)
935 {
936     int c = text[0];
937 
938     if ( c == '\033' ) {
939 	DE->CellModified = FALSE;
940 	DE->clength = 0;
941 	drawelt(DE, DE->crow, DE->ccol);
942 	gsetcursor(DE->de, ArrowCursor);
943 	return;
944     } else {
945 	DE->CellModified = TRUE;
946 	gsetcursor(DE->de, TextCursor);
947     }
948 
949     if (DE->clength == 0) {
950 	switch(get_col_type(DE, DE->ccol + DE->colmin - 1)) {
951 	case NUMERIC:
952 	    DE->currentexp = 1;
953 	    break;
954 	default:
955 	    DE->currentexp = 2;
956 	}
957 	clearrect(DE);
958 	highlightrect(DE);
959     }
960 
961     if (DE->currentexp == 1)	/* we are parsing a number */
962 	switch (c) {
963 	case '-':
964 	    if (DE->nneg == 0)
965 		DE->nneg++;
966 	    else
967 		goto donehc;
968 	    break;
969 	case '.':
970 	    if (DE->ndecimal == 0)
971 		DE->ndecimal++;
972 	    else
973 		goto donehc;
974 	    break;
975 	case 'e':
976 	case 'E':
977 	    if (DE->ne == 0) {
978 		DE->nneg = DE->ndecimal = 0;	/* might have decimal in exponent */
979 		DE->ne++;
980 	    }
981 	    else
982 		goto donehc;
983 	    break;
984 	case 'N':
985 	    if(DE->nneg) goto donehc;
986 	case 'I':
987 	    DE->inSpecial++;
988 	    break;
989 	default:
990 	    if (!DE->inSpecial && !isdigit((int)text[0]))
991 		goto donehc;
992 	    break;
993 	}
994 
995     if (DE->clength++ > 199) {
996 	warning(G_("dataentry: expression too long"));
997 	DE->clength--;
998 	goto donehc;
999     }
1000 
1001     *(DE->bufp)++ = text[0];
1002     printstring(DE, DE->buf, DE->clength, DE->crow, DE->ccol, 1);
1003     return;
1004 
1005 donehc:
1006     bell();
1007 }
1008 
printlabs(DEstruct DE)1009 static void printlabs(DEstruct DE)
1010 {
1011     char clab[15];
1012     const char *p;
1013     int i;
1014 
1015     for (i = DE->colmin; i <= DE->colmax; i++) {
1016 	p = get_col_name(DE, i);
1017 	printstring(DE, p, strlen(p), 0, i - DE->colmin + 1, 0);
1018     }
1019     for (i = DE->rowmin; i <= DE->rowmax; i++) {
1020 	snprintf(clab, 15, DE->labform, i);
1021 	printstring(DE, clab, strlen(clab), i - DE->rowmin + 1, 0, 0);
1022     }
1023 }
1024 
1025 	      /* ================ GraphApp-specific ================ */
1026 
bell(void)1027 static void bell(void)
1028 {
1029     gabeep();
1030 }
1031 
cleararea(DEstruct DE,int xpos,int ypos,int width,int height,rgb col)1032 static void cleararea(DEstruct DE,
1033 		      int xpos, int ypos, int width, int height, rgb col)
1034 {
1035     gfillrect(DE->de, col, rect(xpos, ypos, width, height));
1036 }
1037 
clearwindow(DEstruct DE)1038 static void clearwindow(DEstruct DE)
1039 {
1040     gfillrect(DE->de, DE->p->guiColors[dataeditbg], rect(0, 0, DE->p->w, DE->p->h));
1041 }
1042 
1043 
drawrectangle(DEstruct DE,int xpos,int ypos,int width,int height,int lwd,int fore)1044 static void drawrectangle(DEstruct DE,
1045 			  int xpos, int ypos, int width, int height,
1046 			  int lwd, int fore)
1047 {
1048     /* only used on screen, so always fast */
1049     gdrawrect(DE->de, lwd, 0, (fore==1)? DE->p->guiColors[dataedituser]: DE->p->guiColors[dataeditbg],
1050 	      rect(xpos, ypos, width, height), 1, PS_ENDCAP_SQUARE,
1051 	      PS_JOIN_BEVEL, 10);
1052 }
1053 
de_drawtext(DEstruct DE,int xpos,int ypos,const char * text)1054 static void de_drawtext(DEstruct DE, int xpos, int ypos, const char *text)
1055 {
1056     gdrawstr(DE->de, DE->p->f, DE->p->guiColors[dataeditfg], pt(xpos, ypos), text);
1057 }
1058 
1059 /* Keypress callbacks */
1060 
de_normalkeyin(control c,int k)1061 static void de_normalkeyin(control c, int k)
1062 {
1063     int i, st;
1064     char text[1];
1065     DEstruct DE = getdata(c);
1066 
1067     st = ggetkeystate();
1068     if ((DE->p->chbrk) && (k == DE->p->chbrk) &&
1069 	((!DE->p->modbrk) || ((DE->p->modbrk) && (st == DE->p->modbrk)))) {
1070 	DE->p->fbrk(c);
1071 	return;
1072     }
1073     if (st & CtrlKey) {
1074 	switch (k + 'A' - 1) {
1075 	case 'B':
1076 	    i = DE->rowmin - DE->nhigh + 2;
1077 	    jumpwin(DE, DE->colmin, max(i, 1));
1078 	    break;
1079 	case 'F':
1080 	    jumpwin(DE, DE->colmin, DE->rowmax);
1081 	    break;
1082 	case 'H':
1083 	    moveback(DE);
1084 	    break;
1085 	case 'I':
1086 	    if (st & ShiftKey) advancerect(DE, LEFT);
1087 	    else advancerect(DE, RIGHT);
1088 	    break;
1089 	case 'N':
1090 	case 'J':
1091 	    advancerect(DE, DOWN);
1092 	    break;
1093 	case 'C':
1094 	    de_copy(DE->de);
1095 	    break;
1096 	case 'V':
1097 	    de_paste(DE->de);
1098 	    break;
1099 	case 'L':
1100 	    for (i = DE->colmin; i < DE->colmax; i++)
1101 		if (i < 100)
1102 		    DE->boxw[i] = get_col_width(DE, i)*(DE->p->fw) + 8;
1103 	    drawwindow(DE);
1104 	    break;
1105 	default:
1106 	    bell();
1107 	}
1108     } else if(k == '\b') {
1109 	moveback(DE);
1110     } else if(k == '\n' || k == '\r') {
1111 	advancerect(DE, DOWN);
1112     } else if(k == '\t') {
1113 	if (st & ShiftKey) advancerect(DE, LEFT);
1114 	else advancerect(DE, RIGHT);
1115     } else {
1116 	text[0] = k;
1117 	handlechar(DE, text);
1118     }
1119 
1120 }
1121 
de_im(control c,font * f,point * pt)1122 static void de_im(control c, font *f, point *pt)
1123 {
1124     DEstruct DE = getdata(c);
1125     int x, y;
1126 
1127     drawelt(DE, DE->crow, DE->ccol);
1128     gsetcursor(DE->de, ArrowCursor);
1129     find_coords(DE, DE->crow, DE->ccol, &x, &y);
1130     pt->x = x + DE->text_xoffset + DE->clength * fontwidth(consolefn);
1131     pt->y = y - DE->text_yoffset - 1;
1132     *f = consolefn;
1133 }
1134 
de_ctrlkeyin(control c,int key)1135 static void de_ctrlkeyin(control c, int key)
1136 {
1137     int st, i;
1138     DEstruct DE = getdata(c);
1139 
1140     st = ggetkeystate();
1141     if ((DE->p->chbrk) && (key == DE->p->chbrk) &&
1142 	((!DE->p->modbrk) || ((DE->p->modbrk) && (st == DE->p->modbrk)))) {
1143 	DE->p->fbrk(c);
1144 	return;
1145     }
1146     switch (key) {
1147     case HOME:
1148 	jumpwin(DE, 1, 1);
1149 	downlightrect(DE);
1150 	DE->crow = DE->ccol = 1;
1151 	highlightrect(DE);
1152 	break;
1153     case END:
1154 	i = DE->ymaxused - DE->nhigh + 2;
1155 	if(DE->isEditor)
1156 	    jumpwin(DE, DE->xmaxused, max(i, 1));
1157 	else {
1158 	    /* Try to work out which cols we can fit in */
1159 	    int j, w = 0;
1160 	    for(j = DE->xmaxused;j >= 0; j--) {
1161 		w += BOXW(j);
1162 		if(w > DE->p->w) break;
1163 	    }
1164 	    jumpwin(DE, min(j+2, DE->xmaxused), max(i, 1));
1165 	}
1166 	downlightrect(DE);
1167 	DE->crow = DE->ymaxused - DE->rowmin + 1;
1168 	DE->ccol = 1;
1169 	highlightrect(DE);
1170 	break;
1171     case PGUP:
1172 	i = DE->rowmin - DE->nhigh + 2;
1173 	jumpwin(DE, DE->colmin, max(i, 1));
1174 	break;
1175     case PGDN:
1176 	i = DE->ymaxused - DE->nhigh + 2;
1177 	if(DE->isEditor)
1178 	    jumpwin(DE, DE->colmin, DE->rowmax);
1179 	else
1180 	    jumpwin(DE, DE->colmin, min(i, DE->rowmax));
1181 	break;
1182     case LEFT:
1183 	advancerect(DE, LEFT);
1184 	break;
1185     case RIGHT:
1186 	advancerect(DE, RIGHT);
1187 	break;
1188     case UP:
1189 	advancerect(DE, UP);
1190 	break;
1191     case DOWN:
1192 	advancerect(DE, DOWN);
1193 	break;
1194     case DEL:
1195 	moveback(DE);
1196 	break;
1197      case ENTER:
1198 	 advancerect(DE, DOWN);
1199 	 break;
1200     default:
1201 	;
1202     }
1203 }
1204 
1205 /* mouse callbacks */
1206 
get_cell_text(DEstruct DE)1207 static const char *get_cell_text(DEstruct DE)
1208 {
1209     int  wrow = DE->rowmin + DE->crow - 2, wcol = DE->colmin + DE->ccol - 1;
1210     const char *prev = "";
1211     SEXP tvec;
1212 
1213     if (wcol <= DE->xmaxused) {
1214 	tvec = VECTOR_ELT(DE->work, wcol - 1);
1215 	if (!isNull(tvec) && wrow < INTEGER(DE->lens)[wcol - 1]) {
1216 	    PrintDefaults();
1217 	    if (TYPEOF(tvec) == REALSXP) {
1218 		prev = EncodeElement(tvec, wrow, 0, '.');
1219 	    } else if (TYPEOF(tvec) == STRSXP) {
1220 		if (STRING_ELT(tvec, wrow) != DE->ssNA_STRING)
1221 		    prev = EncodeElement(tvec, wrow, 0, '.');
1222 	    } else error(G_("dataentry: internal memory error"));
1223 	}
1224     }
1225     return prev;
1226 }
1227 
1228 static int online, clickline;
1229 
de_mousedown(control c,int buttons,point xy)1230 static void de_mousedown(control c, int buttons, point xy)
1231 {
1232     int xw, yw, wcol=0, wrow, i, w;
1233     DEstruct DE;
1234 
1235     if (buttons & LeftButton) {
1236 	DE = getdata(c);
1237 	xw = xy.x;
1238 	yw = xy.y;
1239 
1240 	closerect(DE);
1241 
1242 	/* check to see if the click was in the header */
1243 
1244 	if (yw < DE->hwidth + DE->bwidth) {
1245 	    /* too high */
1246 	    return;
1247 	}
1248 	/* translate to box coordinates */
1249 
1250 	wrow = (yw - DE->bwidth - DE->hwidth) / DE->box_h;
1251 
1252 	/* see if it is in the row labels */
1253 	if (xw < DE->bwidth + DE->boxw[0]) {
1254 	    bell();
1255 	    highlightrect(DE);
1256 	    return;
1257 	}
1258 	w = DE->bwidth + DE->boxw[0];
1259 	for (i = 1; i <= DE->nwide; i++)
1260 	    if((w += BOXW(i+DE->colmin-1)) > xw) {
1261 		wcol = i;
1262 		break;
1263 	    }
1264 
1265 	/* see if we selected a line */
1266 	w = DE->bwidth;
1267 	online = 0;
1268 	for (i = 0; i <= DE->nwide; i++) {
1269 	    if(i == 0) w += DE->boxw[0]; else w += BOXW(i+DE->colmin-1);
1270 	    if (abs(w - xw) <= 2) {
1271 		online = 1;
1272 		clickline = i; /* between cols i and i+1 */
1273 		highlightrect(DE);
1274 		gsetcursor(DE->de, HandCursor);
1275 		return;
1276 	    }
1277 	}
1278 
1279 	/* next check to see if it is in the column labels */
1280 
1281 	if (yw < DE->hwidth + DE->bwidth + DE->box_h) {
1282 	    if (xw > DE->bwidth + DE->boxw[0]) {
1283 		highlightrect(DE);
1284 		de_popupmenu(DE, xw, yw, wcol);
1285 		return;
1286 	    } else {
1287 		/* in 0th column */
1288 		highlightrect(DE);
1289 		bell();
1290 	    }
1291 	} else if (wrow > DE->nhigh - 1 || wcol > DE->nwide - 1) {
1292 	    /* off the grid */
1293 	    highlightrect(DE);
1294 	    bell();
1295 	    return;
1296 	} else if (buttons & DblClick) {
1297 	    int x, y, bw;
1298 	    const char *prev;
1299 	    rect rr;
1300 
1301 	    DE->ccol = wcol;
1302 	    DE->crow = wrow;
1303 	    highlightrect(DE);
1304 	    find_coords(DE, DE->crow, DE->ccol, &x, &y);
1305 	    bw = BOXW(DE->ccol + DE->colmin - 1);
1306 	    rr = rect(x + DE->text_xoffset, y - DE->text_yoffset - 1,
1307 		      bw - DE->text_xoffset - 2,
1308 		      DE->box_h - DE->text_yoffset - 2);
1309 	    prev = get_cell_text(DE);
1310 	    if (strlen(prev) * (DE->p->fw) > bw)
1311 		rr.width = (strlen(prev) + 2) * (DE->p->fw);
1312 	    addto(DE->de);
1313 	    DE->celledit = newfield_no_border(prev, rr);
1314 	    settextfont(DE->celledit, DE->p->f);
1315 	    setbackground(DE->celledit, DE->p->guiColors[dataeditbg]);
1316 	    setforeground(DE->celledit, DE->p->guiColors[dataedituser]);
1317 
1318 	    show(DE->celledit);
1319 	    DE->CellEditable = TRUE;
1320 	} else if (buttons & LeftButton) {
1321 	    DE->ccol = wcol;
1322 	    DE->crow = wrow;
1323 	}
1324 	highlightrect(DE);
1325 	return;
1326     }
1327 }
1328 
de_mouseup(control c,int buttons,point xy)1329 static void de_mouseup(control c, int buttons, point xy)
1330 {
1331     int xw, bw, i, w;
1332     DEstruct DE;
1333 
1334     if (online) {
1335 	DE = getdata(c);
1336 	xw = xy.x;
1337 	w = DE->bwidth + DE->boxw[0];
1338 	for(i = 1; i < clickline; i++) w+= BOXW(i+DE->colmin-1);
1339 	bw = xw - w;
1340 	if (bw < (DE->p->fw)*4 + 8) bw = (DE->p->fw)*4 + 8;
1341 	if (bw > (DE->p->fw)*50) bw = (DE->p->fw)*50;
1342 	if(clickline < 100) DE->boxw[clickline] = bw;
1343 	gsetcursor(DE->de, ArrowCursor);
1344 	deredraw(DE);
1345     }
1346 }
1347 
de_redraw(control c,rect r)1348 static void de_redraw(control c, rect r)
1349 {
1350     DEstruct DE = getdata(c);
1351     if (DE->p->w != DE->oldWIDTH || DE->p->h != DE->oldHEIGHT) drawwindow(DE);
1352     else deredraw(DE);
1353 }
1354 
deredraw(DEstruct DE)1355 static void deredraw(DEstruct DE)
1356 {
1357     int i;
1358 
1359     setcellwidths(DE);
1360 
1361     if(DE->hwidth > 0)
1362 	gfillrect(DE->de, bbg, rect(0, 0, DE->p->w, DE->hwidth));
1363     gfillrect(DE->de, bbg, rect(0, 0, DE->boxw[0], DE->windowHeight));
1364 
1365     for (i = 1; i < DE->nhigh; i++)
1366 	drawrectangle(DE, 0, DE->hwidth + i * DE->box_h, DE->boxw[0],
1367 		      DE->box_h, 1, 1);
1368     if(DE->isEditor) {
1369 	DE->colmax = DE->colmin + (DE->nwide - 2);
1370 	DE->rowmax = DE->rowmin + (DE->nhigh - 2);
1371     } else {
1372 	DE->colmax = min(DE->xmaxused, DE->colmin + (DE->nwide - 2));
1373 	DE->rowmax = min(DE->ymaxused, DE->rowmin + (DE->nhigh - 2));
1374     }
1375     printlabs(DE);
1376     for (i = DE->colmin; i <= DE->colmax; i++) drawcol(DE,i);
1377     gfillrect(DE->de, DE->p->guiColors[dataeditbg], rect(DE->windowWidth+1, DE->hwidth,
1378 				      DE->p->w - DE->windowWidth-1,
1379 				      DE->p->h - DE->hwidth));
1380     highlightrect(DE);
1381 }
1382 
de_closewin(DEstruct DE)1383 static void de_closewin(DEstruct DE)
1384 {
1385     closerect(DE);
1386     hide(DE->de);
1387     del(DE->de);
1388 }
1389 
copyarea(DEstruct DE,int src_x,int src_y,int dest_x,int dest_y)1390 static void copyarea(DEstruct DE, int src_x, int src_y, int dest_x, int dest_y)
1391 {
1392     int mx = max(src_x, dest_x), my = max(src_y, dest_y);
1393     copyrect(DE->de, pt(dest_x, dest_y),
1394 	     rect(src_x, src_y, DE->windowWidth - mx, DE->windowHeight - my));
1395 }
1396 
copyH(DEstruct DE,int src_x,int dest_x,int width)1397 static void copyH(DEstruct DE, int src_x, int dest_x, int width)
1398 {
1399     copyrect(DE->de, pt(dest_x, DE->hwidth),
1400 	     rect(src_x, DE->hwidth, width, DE->windowHeight - DE->hwidth));
1401 }
1402 
initwin(DEstruct DE,const char * title)1403 static Rboolean initwin(DEstruct DE, const char *title)
1404 {
1405     int i, labdigs;
1406     rect r;
1407 
1408     DE->de = newdataeditor(DE, title);
1409     if(!DE->de) return TRUE;
1410     DE->oldWIDTH = DE->oldHEIGHT = 0;
1411     DE->nboxchars = 5;
1412 
1413     DE->nboxchars = asInteger(GetOption1(install("de.cellwidth")));
1414     if (DE->nboxchars == NA_INTEGER || DE->nboxchars < 0) DE->nboxchars = 0;
1415     if (DE->nboxchars > 0) check(DE->de_mvw);
1416     DE->box_w = ((DE->nboxchars >0)?DE->nboxchars:FIELDWIDTH)*(DE->p->fw) + 8;
1417     /* this used to presume 4 chars sufficed for row numbering */
1418     labdigs = max(3, 1+floor(log10((double)DE->ymaxused)));
1419     DE->boxw[0] = (1+labdigs)*(DE->p->fw) + 8;
1420     snprintf(DE->labform, 6, "%%%dd", labdigs);
1421     for(i = 1; i < 100; i++)
1422 	DE->boxw[i] = get_col_width(DE, i) * (DE->p->fw) + 8;
1423     DE->box_h = (DE->p->fh) + 4;
1424     DE->text_xoffset = 5;
1425     DE->text_yoffset = -3;
1426     setcellwidths(DE);
1427     DE->nhigh = (DE->p->h - 2 * DE->bwidth - DE->hwidth - 3) / DE->box_h;
1428     if(!DE->isEditor && DE->nhigh > DE->ymaxused+1) DE->nhigh = DE->ymaxused+1;
1429     DE->windowHeight = DE->nhigh * DE->box_h + 2 * DE->bwidth + DE->hwidth;
1430     r = getrect(DE->de);
1431     r.width = DE->windowWidth + 3;
1432     r.height = DE->windowHeight + 3;
1433     resize(DE->de, r);
1434 
1435     DE->CellModified = DE->CellEditable = FALSE;
1436     bbg = dialog_bg();
1437     /* set the active cell to be the upper left one */
1438     DE->crow = 1;
1439     DE->ccol = 1;
1440     /* drawwindow(); done as repaint but
1441        decide if we need scrollbars here to avoid flashing*/
1442     DE->nhigh = (DE->p->h - 2 * DE->bwidth - DE->hwidth) / DE->box_h;
1443     gchangescrollbar(DE->de, VWINSB, 0, DE->ymaxused/DE->yScrollbarScale,
1444 		     max(DE->nhigh/DE->yScrollbarScale, 1), 0);
1445     setcellwidths(DE);
1446     gchangescrollbar(DE->de, HWINSB, 0, DE->xmaxused/DE->xScrollbarScale,
1447 		     max(DE->nwide/DE->xScrollbarScale, 1), 0);
1448     show(DE->de);
1449     show(DE->de); /* a precaution, as PD reports transparent windows */
1450     BringToTop(DE->de, 0);
1451     DE->buf[BUFSIZE-1] = '\0';
1452     return FALSE;
1453 }
1454 
1455 /* Menus */
1456 
1457 static window wconf, devw;
1458 static radiobutton rb_num, rb_char;
1459 static label lwhat, lrb;
1460 static field varname;
1461 static int isnumeric, popupcol;
1462 
popupclose(control c)1463 static void popupclose(control c)
1464 {
1465     SEXP tvec;
1466     char buf[BUFSIZE], clab[25];
1467     int i;
1468     DEstruct DE = getdata(c);
1469 
1470     buf[BUFSIZE-1] = '\0';
1471     strncpy(buf, GA_gettext(varname), BUFSIZE-1);
1472     if(!strlen(buf)) {
1473 	askok(G_("column names cannot be blank"));
1474 	return;
1475     }
1476     if (popupcol > DE->xmaxused) {
1477 	/* extend work, names and lens */
1478 	REPROTECT(DE->work = lengthgets(DE->work, popupcol), DE->wpi);
1479 	REPROTECT(DE->names = lengthgets(DE->names, popupcol), DE->npi);
1480 	/* Last col name is set later */
1481 	for (i = DE->xmaxused+1; i < popupcol - 1; i++) {
1482 	    snprintf(clab, 25, "var%d", i + 1);
1483 	    SET_STRING_ELT(DE->names, i, mkChar(clab));
1484 	}
1485 	REPROTECT(DE->lens = lengthgets(DE->lens, popupcol), DE->lpi);
1486 	DE->xmaxused = popupcol;
1487     }
1488     tvec = VECTOR_ELT(DE->work, popupcol - 1);
1489     if(ischecked(rb_num) && !isnumeric) {
1490 	if (isNull(tvec))
1491 	    SET_VECTOR_ELT(DE->work, popupcol - 1,
1492 			   ssNewVector(DE, REALSXP, 100));
1493 	else
1494 	    SET_VECTOR_ELT(DE->work, popupcol - 1, coerceVector(tvec, REALSXP));
1495     } else if(ischecked(rb_char) && isnumeric) {
1496 	if (isNull(tvec))
1497 	    SET_VECTOR_ELT(DE->work, popupcol - 1,
1498 			   ssNewVector(DE, STRSXP, 100));
1499 	else
1500 	    SET_VECTOR_ELT(DE->work, popupcol - 1, coerceVector(tvec, STRSXP));
1501     }
1502     SET_STRING_ELT(DE->names, popupcol - 1, mkChar(buf));
1503     hide(wconf);
1504     del(wconf);
1505 }
1506 
nm_hit_key(window w,int key)1507 static void nm_hit_key(window w, int key)
1508 {
1509     if(key == '\n') popupclose(wconf);
1510 }
1511 
de_popupmenu(DEstruct DE,int x_pos,int y_pos,int col)1512 static void de_popupmenu(DEstruct DE, int x_pos, int y_pos, int col)
1513 {
1514     const char *blah;
1515     rect r = screen_coords(DE->de);
1516 
1517     popupcol = DE->colmin + col - 1;
1518     blah = get_col_name(DE, popupcol);
1519     wconf = newwindow(G_("Variable editor"),
1520 		      rect(x_pos + r.x-150, y_pos + r.y-50, 300, 100),
1521 		      Titlebar | Closebox | Modal);
1522     setclose(wconf, popupclose);
1523     setbackground(wconf, bbg);
1524     lwhat = newlabel(G_("variable name"), rect(10, 22, 90, 20), AlignLeft);
1525     varname = newfield(blah, rect(100, 20, 120, 20));
1526     lrb = newlabel(G_("type"), rect(50, 62, 50, 20), AlignLeft);
1527     rb_num = newradiobutton("numeric", rect(100, 60 , 80, 20), NULL);
1528     rb_char = newradiobutton("character", rect(180, 60 , 80, 20), NULL);
1529     isnumeric = (get_col_type(DE, popupcol) == NUMERIC);
1530     if (isnumeric) check(rb_num); else check(rb_char);
1531     setkeydown(wconf, nm_hit_key);
1532     setdata(wconf, DE); /* for popupclose, nm_hit_key */
1533     show(wconf);
1534 }
1535 
de_copy(control c)1536 static void de_copy(control c)
1537 {
1538     DEstruct DE = getdata(c);
1539     copystringtoclipboard(get_cell_text(DE));
1540 }
1541 
de_paste(control c)1542 static void de_paste(control c)
1543 {
1544     char *p;
1545     DEstruct DE = getdata(c);
1546 
1547     closerect(DE);
1548     if ( clipboardhastext() &&
1549 	 !getstringfromclipboard(DE->buf, BUFSIZE-1) ) {
1550 	/* set current cell to first line of clipboard */
1551 	DE->CellModified = TRUE;
1552 	if ((p = strchr(DE->buf, '\n'))) *p = '\0';
1553 	DE->clength = strlen(DE->buf);
1554 	DE->bufp = DE->buf + DE->clength;
1555 	closerect(DE);
1556     }
1557     highlightrect(DE);
1558 }
1559 
de_delete(control c)1560 static void de_delete(control c)
1561 {
1562     DEstruct DE = getdata(c);
1563     DE->CellModified = TRUE;
1564     DE->buf[0] = '\0';
1565     DE->clength = 0;
1566     DE->bufp = DE->buf + DE->clength;
1567     closerect(DE);
1568     highlightrect(DE);
1569 }
1570 
de_autosize(control c)1571 static void de_autosize(control c)
1572 {
1573     DEstruct DE = getdata(c);
1574     int col = DE->ccol + DE->colmin - 1;
1575 
1576     closerect(DE);
1577     if(col < 100) {
1578 	DE->boxw[col] = get_col_width(DE,col)*(DE->p->fw) + 8;
1579 	deredraw(DE);
1580     }
1581 }
1582 
de_stayontop(control c)1583 static void de_stayontop(control c)
1584 {
1585     DEstruct DE = getdata(c);
1586     BringToTop(DE->de, 2);
1587 }
1588 
de_sbf(control c,int pos)1589 static void de_sbf(control c, int pos)
1590 {
1591     DEstruct DE = getdata(c);
1592     if (pos < 0) { /* horizontal */
1593 	DE->colmin = min(DE->xmaxused, -pos*DE->xScrollbarScale);
1594     } else {
1595 	DE->rowmin = 1 + pos*DE->yScrollbarScale;
1596 	if(DE->rowmin > DE->ymaxused - DE->nhigh + 2)
1597 	    DE->rowmin = max(1,DE->ymaxused - DE->nhigh + 2);
1598 /*	printf("pos %d, rowmin %d, scale %d\n", pos, DE->rowmin, DE->yScrollbarScale); */
1599     }
1600     drawwindow(DE);
1601 }
1602 
1603 static checkbox varwidths;
1604 
vw_close(control c)1605 static void vw_close(control c)
1606 {
1607     int x;
1608     DEstruct DE = getdata(c);
1609     if (ischecked(varwidths)) x = 0;
1610     else x = atoi(GA_gettext(varname)); /* 0 if error */
1611     x = min(x, 50);
1612     if (x != DE->nboxchars) {
1613 	DE->nboxchars = x;
1614 	DE->box_w = ((DE->nboxchars >0)?DE->nboxchars:FIELDWIDTH)*(DE->p->fw) + 8;
1615 	deredraw(DE);
1616     }
1617     hide(devw);
1618     del(devw);
1619     if (DE->nboxchars > 0) check(DE->de_mvw);
1620     else uncheck(DE->de_mvw);
1621     addto(DE->de);
1622 }
1623 
vw_hit_key(window w,int key)1624 static void vw_hit_key(window w, int key)
1625 {
1626     if(key == '\n') vw_close(w);
1627 }
1628 
vw_callback(control c)1629 static void vw_callback(control c)
1630 {
1631     if (ischecked(varwidths)) disable(varname);
1632     else enable(varname);
1633 }
1634 
1635 
de_popup_vw(DEstruct DE)1636 static void de_popup_vw(DEstruct DE)
1637 {
1638     char blah[25];
1639 
1640     devw = newwindow(G_("Cell width(s)"),
1641 		     rect(0, 0, 250, 60),
1642 		     Titlebar | Centered | Closebox | Modal);
1643     setdata(devw, DE);
1644     setclose(devw, vw_close);
1645     setbackground(devw, bbg);
1646     lwhat = newlabel(G_("Cell width"), rect(10, 20, 70, 20), AlignLeft);
1647     snprintf(blah, 25, "%d", DE->nboxchars);
1648     varname = newfield(blah, rect(80, 20, 40, 20));
1649     varwidths = newcheckbox(G_("variable"), rect(150, 20, 80, 20), vw_callback);
1650     if (DE->nboxchars == 0) {
1651 	check(varwidths);
1652 	disable(varname);
1653     }
1654     setkeydown(devw, vw_hit_key);
1655     show(devw);
1656 }
1657 
menudecellwidth(control m)1658 static void menudecellwidth(control m)
1659 {
1660     de_popup_vw(getdata(m));
1661 }
1662 
deldataeditor(control m)1663 static void deldataeditor(control m)
1664 {
1665     DEstruct DE = getdata(m);
1666     freeConsoleData(DE->p);
1667 }
1668 
declose(control m)1669 static void declose(control m)
1670 {
1671     DEstruct DE = getdata(m);
1672 
1673     de_closewin(DE);
1674     show(RConsole);
1675     R_de_up = FALSE;
1676 }
1677 
deresize(console c,rect r)1678 static void deresize(console c, rect r)
1679 {
1680     DEstruct DE = getdata(c);
1681     if (((DE->p->w  == r.width) &&
1682 	 (DE->p->h == r.height)) ||
1683 	(r.width == 0) || (r.height == 0) ) /* minimize */
1684 	return;;
1685     DE->p->w = r.width;
1686     DE->p->h = r.height;
1687 }
1688 
1689 
menudehelp(control m)1690 static void menudehelp(control m)
1691 {
1692     char s[] = GN_("Navigation.\n  Keyboard: cursor keys move selection\n\tTab move right, Shift+Tab moves left\n\tPgDn or Ctrl+F: move down one screenful\n\tPgUp or Ctrl+B: move up one screenful\n\tHome: move to (1,1) cell\n\tEnd: show last rows of last column.\n   Mouse: left-click in a cell, use the scrollbar(s).\n\nEditing.\n  Type in the currently hightlighted cell\n  Double-click in a cell for an editable field\n\nMisc.\n  Ctrl-L redraws the screen, auto-resizing the columns\n  Ctrl-C copies selected cell\n  Ctrl-V pastes to selected cell\n  Right-click menu for copy, paste, autosize currently selected column\n\n");
1693     askok(G_(s));
1694 }
1695 
1696 
1697 static MenuItem DePopup[28] = {
1698     {GN_("Help"), menudehelp, 0},
1699     {"-", 0, 0},
1700     {GN_("Copy selected cell"), de_copy, 0},
1701     {GN_("Paste to selected cell"), de_paste, 0},
1702     {GN_("Autosize column"), de_autosize, 0},
1703     {"-", 0, 0},
1704     {GN_("Stay on top"), de_stayontop, 0},
1705     {"-", 0, 0},
1706     {GN_("Close"), declose, 0},
1707     LASTMENUITEM
1708 };
1709 
demenuact(control m)1710 static void demenuact(control m)
1711 {
1712     /* use this to customize the menu */
1713 }
1714 
depopupact(control m)1715 static void depopupact(control m)
1716 {
1717     DEstruct DE = getdata(m);
1718     /* use this to customize the menu */
1719 
1720     if (ismdi())
1721 	disable(DePopup[6].m);
1722     else {
1723 	if (isTopmost(DE->de))
1724 	    check(DePopup[6].m);
1725 	else
1726 	    uncheck(DePopup[6].m);
1727     }
1728 }
1729 
1730 
1731 #define MCHECK(a) if (!(a)) {del(c);return NULL;}
1732 
1733 RECT *RgetMDIsize(void); /* in rui.c */
1734 
newdataeditor(DEstruct DE,const char * title)1735 static dataeditor newdataeditor(DEstruct DE, const char *title)
1736 {
1737     int w, h, x, y;
1738     dataeditor c;
1739     menuitem m;
1740 
1741     DE->p = newconsoledata((consolefn) ? consolefn : FixedFont,
1742 			   pagerrow, pagercol, 0, 0,
1743 			   guiColors,
1744 			   DATAEDITOR, 0, 0);
1745     if (!DE->p) return NULL;
1746 
1747     w = DE->p->w ;
1748     h = DE->p->h;
1749     if (ismdi()) {
1750 	RECT *pR = RgetMDIsize();
1751 	x = (pR->right - w) / 3; x = x > 20 ? x:20;
1752 	y = (pR->bottom - h) / 3; y = y > 20 ? y:20;
1753     } else {
1754 	x = (devicewidth(NULL) - w) / 3;
1755 	y = (deviceheight(NULL) - h) / 3 ;
1756     }
1757     c = (dataeditor) newwindow(title, rect(x, y, w, h),
1758 			       Document | StandardWindow | Menubar |
1759 			       VScrollbar | HScrollbar | TrackMouse);
1760     if (!c) {
1761 	freeConsoleData(DE->p);
1762 	return NULL;
1763     }
1764     setdata(c, DE);
1765     if(h == 0) DE->p->h = getheight(c);
1766     if(w == 0) DE->p->w  = getwidth(c);
1767     (DE->p->cols) = DE->p->w / (DE->p->fw) - 1;
1768     (DE->p->rows) = DE->p->h / (DE->p->fh) - 1;
1769     DE->p->right = (DE->p->w - (DE->p->cols)*(DE->p->fw)) / 2;
1770     DE->p->top = (DE->p->h - (DE->p->rows)*(DE->p->fh)) / 2;
1771     gsetcursor(c, ArrowCursor);
1772     setbackground(c, guiColors[dataeditbg]);
1773     if (ismdi() && (RguiMDI & RW_TOOLBAR)) {
1774 	/* blank toolbar to stop windows jumping around */
1775 	int btsize = 24;
1776 	control tb;
1777 	addto(c);
1778 	MCHECK(tb = newtoolbar(btsize + 4));
1779 	gsetcursor(tb, ArrowCursor);
1780     }
1781     if(DE->isEditor) {
1782 	MCHECK(m = gpopup(depopupact, DePopup));
1783 	setdata(m, DE);
1784 	setdata(DePopup[2].m, DE);
1785 	setdata(DePopup[3].m, DE);
1786 	setdata(DePopup[4].m, DE);
1787 	setdata(DePopup[6].m, DE);
1788 	setdata(DePopup[8].m, DE);
1789     }
1790     MCHECK(m = newmenubar(demenuact));
1791     MCHECK(newmenu(G_("File")));
1792 /*    MCHECK(m = newmenuitem("-", 0, NULL));*/
1793     MCHECK(m = newmenuitem(G_("Close"), 0, declose));
1794     setdata(m, DE);
1795     if(DE->isEditor) {
1796 	newmdimenu();
1797 	MCHECK(newmenu(G_("Edit")));
1798 	MCHECK(m = newmenuitem(G_("Copy  \tCTRL+C"), 0, de_copy));
1799 	setdata(m, DE);
1800 	MCHECK(m = newmenuitem(G_("Paste \tCTRL+V"), 0, de_paste));
1801 	setdata(m, DE);
1802 	MCHECK(m = newmenuitem(G_("Delete\tDEL"), 0, de_delete));
1803 	setdata(m, DE);
1804 	MCHECK(m = newmenuitem("-", 0, NULL));
1805 	MCHECK(m = DE->de_mvw = newmenuitem(G_("Cell widths ..."), 0,
1806 					    menudecellwidth));
1807 	setdata(m, DE);
1808 	MCHECK(m = newmenu(G_("Help")));
1809 	MCHECK(newmenuitem(G_("Data editor"), 0, menudehelp));
1810     }
1811 
1812     setdata(c, DE); /* Why the repeat? */
1813     setresize(c, deresize);
1814     setredraw(c, de_redraw);
1815     setdel(c, deldataeditor);
1816     setclose(c, declose);
1817     sethit(c, de_sbf);
1818     setkeyaction(c, de_ctrlkeyin);
1819     if(DE->isEditor) {
1820 	setkeydown(c, de_normalkeyin);
1821 	setim(c, de_im);
1822 	setmousedown(c, de_mousedown);
1823 	setmouseup(c, de_mouseup);
1824     }
1825     return(c);
1826 }
1827 
dv_closewin_cend(void * data)1828 static void dv_closewin_cend(void *data)
1829 {
1830     DEstruct DE = (DEstruct) data;
1831     R_ReleaseObject(DE->lens);
1832     R_ReleaseObject(DE->work);
1833     de_closewin(DE);
1834     free(DE);
1835 }
1836 
Win_dataviewer(SEXP args)1837 SEXP Win_dataviewer(SEXP args)
1838 {
1839     SEXP stitle;
1840     SEXPTYPE type;
1841     int i, nprotect;
1842     RCNTXT cntxt;
1843     DEstruct DE = (DEstruct) malloc(sizeof(destruct));
1844 
1845     DE->isEditor = FALSE;
1846     nprotect = 0;/* count the PROTECT()s */
1847     DE->work = CAR(args);
1848     DE->names = getAttrib(DE->work, R_NamesSymbol);
1849 
1850     if (TYPEOF(DE->work) != VECSXP)
1851 	error(G_("invalid argument"));
1852     stitle = CADR(args);
1853     if (!isString(stitle) || LENGTH(stitle) != 1)
1854 	error(G_("invalid argument"));
1855 
1856     /* initialize the constants */
1857 
1858     DE->bufp = DE->buf;
1859     DE->ne = 0;
1860     DE->currentexp = 0;
1861     DE->nneg = 0;
1862     DE->ndecimal = 0;
1863     DE->clength = 0;
1864     DE->inSpecial = 0;
1865     DE->ccol = 1;
1866     DE->crow = 1;
1867     DE->colmin = 1;
1868     DE->rowmin = 1;
1869     PROTECT(DE->ssNA_STRING = duplicate(NA_STRING));
1870     nprotect++;
1871     DE->bwidth = 0;
1872     DE->hwidth = 5;
1873 
1874     /* setup lens  */
1875     DE->xmaxused = length(DE->work); DE->ymaxused = 0;
1876     PROTECT_WITH_INDEX(DE->lens = allocVector(INTSXP, DE->xmaxused), &DE->lpi);
1877     nprotect++;
1878 
1879     for (i = 0; i < DE->xmaxused; i++) {
1880 	int len = LENGTH(VECTOR_ELT(DE->work, i));
1881 	INTEGER(DE->lens)[i] = len;
1882 	DE->ymaxused = max(len, DE->ymaxused);
1883 	type = TYPEOF(VECTOR_ELT(DE->work, i));
1884 	if (type != STRSXP && type != REALSXP)
1885 	    error(G_("invalid argument"));
1886     }
1887 
1888     DE->xScrollbarScale = DE->yScrollbarScale = 1;
1889 
1890     /* start up the window, more initializing in here */
1891     if (initwin(DE, CHAR(STRING_ELT(stitle, 0))))
1892 	error("unable to start data viewer");
1893 
1894     /* set up a context which will close the window if there is an error */
1895     begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
1896 		 R_NilValue, R_NilValue);
1897     cntxt.cend = &dv_closewin_cend;
1898     cntxt.cenddata = (void *)DE;
1899 
1900     R_PreserveObject(DE->work); /* also preserves names */
1901     R_PreserveObject(DE->lens);
1902     UNPROTECT(nprotect);
1903     return R_NilValue;
1904 }
1905