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