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