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