The R Project SVN R

Rev

Details | Last modification | View Log | RSS feed

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