The R Project SVN R

Rev

Rev 73256 | Rev 78446 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
13974 hornik 1
/*
73256 maechler 2
 *  R : A Computer Language for Statistical Data Analysis
13974 hornik 3
 *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
72512 maechler 4
 *  Copyright (C) 1998--2015  The R Core Team
13974 hornik 5
 *
6
 *  This program is free software; you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation; either version 2 of the License, or
9
 *  (at your option) any later version.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
42307 ripley 17
 *  along with this program; if not, a copy is available at
68949 ripley 18
 *  https://www.R-project.org/Licenses/
13974 hornik 19
 */
20
 
34639 ripley 21
/* <UTF8>
32555 ripley 22
   Used XTextWidth and XDrawText, so need to use fontsets
32422 ripley 23
 
32555 ripley 24
   Also needed input context.
32432 ripley 25
*/
26
 
34639 ripley 27
/* The version for R 2.1.0 is partly based on patches by
35930 ripley 28
   Ei-ji Nakama <nakama@ki.rim.or.jp> for use with Japanese fonts. */
32429 ripley 29
 
34639 ripley 30
#define DPRINTS(x) printf(#x "=[%s]\n", x)
31
#define DPRINTX(x) printf(#x "=%x\n", x)
32
#define DPRINTD(x) printf(#x "=%d\n", x)
33
 
13974 hornik 34
#ifdef HAVE_CONFIG_H
35
#include <config.h>
36
#endif
37
 
57538 ripley 38
#define R_USE_SIGNALS 1
36498 ripley 39
#include <Defn.h>
13974 hornik 40
#include <stdlib.h>
46045 murdoch 41
#include <Rinternals.h>
42
#include <R_ext/Parse.h>  /* parsing is used in handling escape codes */
13974 hornik 43
 
46045 murdoch 44
 
29066 ripley 45
#ifndef _Xconst
46
#define _Xconst const
47
#endif
13974 hornik 48
#include <X11/X.h>
49
#include <X11/Xlib.h>
50
#include <X11/Xutil.h>
51
#include <X11/keysym.h>
52
#include <X11/cursorfont.h>
36044 ripley 53
#include <X11/Intrinsic.h>
13974 hornik 54
 
40756 ripley 55
#include <Print.h>
56
/* For the input handlers of the event loop mechanism: */
57
#include <R_ext/eventloop.h>
41831 rgentlem 58
#include <R_ext/RS.h>           /* for CallocCharBuf */
32677 ripley 59
# define USE_FONTSET 1
35844 ripley 60
/* In theory we should do this, but it works less well
61
# ifdef X_HAVE_UTF8_STRING
62
#  define HAVE_XUTF8TEXTEXTENTS 1
63
#  define HAVE_XUTF8DRAWSTRING 1
64
#  define HAVE_XUTF8DRAWIMAGESTRING 1
65
# endif */
32677 ripley 66
 
29067 ripley 67
#ifndef HAVE_KEYSYM
68
#define KeySym int
69
#endif
28840 ripley 70
 
13974 hornik 71
#define DEEvent XEvent
72
 
73
typedef enum { UP, DOWN, LEFT, RIGHT } DE_DIRECTION;
74
 
75
typedef enum {UNKNOWNN, NUMERIC, CHARACTER} CellType;
76
 
77
/* EXPORTS : */
44345 ripley 78
SEXP in_RX11_dataentry(SEXP call, SEXP op, SEXP args, SEXP rho);
13974 hornik 79
 
40759 ripley 80
/* Global variables needed for the graphics */
81
static Display *iodisplay = NULL;
82
static XContext deContext;
83
static int nView = 0; /* number of open data windows */
84
static int fdView = -1;
85
 
86
typedef struct {
87
    Window iowindow;
88
    GC iogc;
89
    XFontStruct *font_info;
90
    SEXP work, names, lens;
91
    PROTECT_INDEX wpi, npi, lpi;
92
    int box_w;                       /* width of a box */
93
    int boxw[100];                   /* widths of cells */
94
    int box_h;                       /* height of a box */
95
    int windowWidth;                 /* current width of the window */
96
    int fullwindowWidth;
97
    int windowHeight;                /* current height of the window */
98
    int fullwindowHeight;
99
    int crow;                        /* current row */
100
    int ccol;                        /* current column */
101
    int nwide, nhigh;
102
    int colmax, colmin, rowmax, rowmin;
103
    int bwidth;			/* width of the border */
104
    int hht;			/* height of header  */
105
    int text_offset;
106
    int nboxchars;
107
    int xmaxused, ymaxused;
74275 ripley 108
    char labform[15];  // increased from 6 to pacify gcc 8
40759 ripley 109
    Rboolean isEditor;
110
    Atom prot;
111
} destruct, *DEstruct;
112
 
13974 hornik 113
/* Local Function Definitions */
32424 ripley 114
 
40759 ripley 115
static void advancerect(DEstruct, DE_DIRECTION);
116
static int  CheckControl(DEEvent *);
117
static int  CheckShift(DEEvent *);
13974 hornik 118
static int  checkquit(int);
40759 ripley 119
static void clearrect(DEstruct);
120
static void closerect(DEstruct);
121
static void clearwindow(DEstruct);
122
static void closewin(DEstruct);
123
static void copycell(DEstruct);
124
static void doControl(DEstruct, DEEvent*);
125
static int  doMouseDown(DEstruct, DEEvent*);
126
static void doSpreadKey(DEstruct, int, DEEvent*);
127
static void downlightrect(DEstruct);
128
static void drawwindow(DEstruct);
129
static void drawcol(DEstruct, int);
130
static void drawrow(DEstruct, int);
131
static void eventloop(DEstruct);
132
static void find_coords(DEstruct, int, int, int*, int*);
133
static int  findcell(DEstruct);
32432 ripley 134
static char *GetCharP(DEEvent*);
13974 hornik 135
static KeySym GetKey(DEEvent*);
41835 ripley 136
static void handlechar(DEstruct, char *);
40759 ripley 137
static void highlightrect(DEstruct);
41831 rgentlem 138
static Rboolean initwin(DEstruct, const char *);
40759 ripley 139
static void jumppage(DEstruct, DE_DIRECTION);
140
static void jumpwin(DEstruct, int, int);
141
static void pastecell(DEstruct, int, int);
142
static void popdownmenu(DEstruct);
143
static void popupmenu(DEstruct, int, int, int, int);
144
static void printlabs(DEstruct);
145
static void printrect(DEstruct, int, int);
41831 rgentlem 146
static void printstring(DEstruct, const char*, int, int, int, int);
40759 ripley 147
static void printelt(DEstruct, SEXP, int, int, int);
13974 hornik 148
static void RefreshKeyboardMapping(DEEvent*);
40759 ripley 149
static void cell_cursor_init(DEstruct);
32424 ripley 150
 
13974 hornik 151
/* Functions to hide Xlib calls */
152
static void bell(void);
40759 ripley 153
static void cleararea(DEstruct, int, int, int, int);
154
static void copyH(DEstruct, int, int, int);
155
static void copyarea(DEstruct, int, int, int, int);
156
static void doConfigure(DEstruct, DEEvent *ioevent);
157
static void drawrectangle(DEstruct, int, int, int, int, int, int);
158
static void drawtext(DEstruct, int, int, char*, int);
13974 hornik 159
static void RefreshKeyboardMapping(DEEvent *ioevent);
40759 ripley 160
static void Rsync(DEstruct);
41831 rgentlem 161
static int textwidth(DEstruct, const char*, int);
13974 hornik 162
static int WhichEvent(DEEvent ioevent);
163
 
40756 ripley 164
static void R_ProcessX11Events(void *data);
165
 
166
 
41831 rgentlem 167
static const char *get_col_name(DEstruct, int col);
40759 ripley 168
static int  get_col_width(DEstruct, int col);
169
static CellType get_col_type(DEstruct, int col);
170
static void calc_pre_edit_pos(DEstruct DE);
34639 ripley 171
static int last_wchar_bytes(char *);
40756 ripley 172
static SEXP ssNewVector(SEXPTYPE, int);
24274 ripley 173
static SEXP ssNA_STRING;
13974 hornik 174
 
32424 ripley 175
 
40756 ripley 176
/* only used in the editor */
177
static Atom _XA_WM_PROTOCOLS = 0;
178
static Window menuwindow, menupanes[4];
179
static Rboolean CellModified;
180
static int box_coords[6];
181
static int currentexp;                  /* whether an cell is active */
13974 hornik 182
static int ndecimal;                    /* count decimal points */
183
static int ne;                          /* count exponents */
184
static int nneg;			/* indicate whether its a negative */
185
static int clength;                     /* number of characters currently entered */
40747 ripley 186
static int inSpecial;
34639 ripley 187
 
188
#define BOOSTED_BUF_SIZE    201
189
static char buf[BOOSTED_BUF_SIZE];	/* boosted to allow for MBCS */
13974 hornik 190
static char *bufp;
32424 ripley 191
static char copycontents[sizeof(buf)+1] ;
13974 hornik 192
 
40756 ripley 193
/* The next few and used only for the editor in MBCS locales */
34639 ripley 194
static Status           status;
46169 ripley 195
static XFontSet         font_set = NULL;
32424 ripley 196
static XFontStruct	**fs_list;
197
static int		font_set_cnt;
44345 ripley 198
static char             fontset_name[]="-*-fixed-medium-r-*-*-*-120-*-*-*-*-*-*";
32518 ripley 199
static XIM		ioim;
34639 ripley 200
static XIMStyle         ioim_style;
201
static XIMStyles        *ioim_styles;
202
 
203
/*
204
 * XIM:
205
 * OverTheSpot XIMPreeditPosition | XIMStatusArea;
206
 * OffTheSpot  XIMPreeditArea     | XIMStatusArea;
207
 * Root        XIMPreeditNothing  | XIMStatusNothing;
208
 */
40756 ripley 209
static XIMStyle preedit_styles[] = {
210
    XIMPreeditPosition,
211
    XIMPreeditArea,
212
    XIMPreeditNothing,
213
    XIMPreeditNone,
214
    (XIMStyle)NULL,
34639 ripley 215
};
40756 ripley 216
static XIMStyle status_styles[] = {
217
    XIMStatusArea,
218
    XIMStatusNothing,
219
    XIMStatusNone,
220
    (XIMStyle)NULL,
34639 ripley 221
};
46169 ripley 222
static XIC ioic = NULL;
13974 hornik 223
 
224
#ifndef max
225
#define max(a, b) (((a)>(b))?(a):(b))
226
#endif
227
#ifndef min
228
#define min(a, b) (((a)<(b))?(a):(b))
229
#endif
40759 ripley 230
#define BOXW(x) (min(((x<100 && DE->nboxchars==0)?DE->boxw[x]:DE->box_w), DE->fullwindowWidth-DE->boxw[0]-2*DE->bwidth-2))
13974 hornik 231
 
24274 ripley 232
/*
233
  Underlying assumptions (for this version R >= 1.8.0)
13974 hornik 234
 
24274 ripley 235
  The data are stored in a list `work', with unused columns having
236
  NULL entries.  The names for the list are in `names', which should
32424 ripley 237
  have a name for all displayable columns (up to xmaxused).
24274 ripley 238
  The *used* lengths of the columns are in `lens': this needs only be
239
  set for non-NULL columns.
240
 
32424 ripley 241
  If the list was originally length(0), that should work with
32432 ripley 242
 
24274 ripley 243
 
244
  All row and col numbers are 1-based.
245
 
246
  BDR May 2003
247
 */
248
 
13974 hornik 249
/*
250
   The spreadsheet function returns a list of vectors. The types of
251
   these vectors can be specified by the user as can their names. It
252
   the names are specified they are set during initialization. The
253
   user can change these via a menu interface, they can also change
254
   the type.
255
 
256
   The vectors are created too long and if they need to be increased
257
   this is done by using the next higher power of 2. They start 100
32360 ripley 258
   long.  Vectors are initialized to NA when they are created so that
40759 ripley 259
   NA is returned for any cell that was not set by the user.  We use
40747 ripley 260
   a special type of NA to distinguish this from user-supplied NAs.
13974 hornik 261
 
24733 ripley 262
   In Macintosh we needed to call the main event loop to get
13974 hornik 263
   events. This ensures that the spreadsheet interacts well with the
264
   other windows. Under X windows we let the window manager handle
265
   those sorts of details.
266
 
267
 */
268
 
269
static char *menu_label[] =
270
{
34639 ripley 271
    " Real",
272
    " Character",
273
    "Change Name ",
13974 hornik 274
};
275
 
276
/*
277
   ssNewVector is just an interface to allocVector but it lets us
278
   set the fields to NA. We need to have a special NA for reals and
279
   strings so that we can differentiate between uninitialized elements
40747 ripley 280
   in the vectors and user supplied NA's; hence ssNA_STRING
13974 hornik 281
 */
282
 
283
static SEXP ssNewVector(SEXPTYPE type, int vlen)
284
{
285
    SEXP tvec;
286
    int j;
287
 
288
    tvec = allocVector(type, vlen);
289
    for (j = 0; j < vlen; j++)
290
	if (type == REALSXP)
40747 ripley 291
	    REAL(tvec)[j] = NA_REAL;
13974 hornik 292
	else if (type == STRSXP)
40747 ripley 293
	    SET_STRING_ELT(tvec, j, ssNA_STRING);
13974 hornik 294
    return (tvec);
295
}
296
 
14561 luke 297
static void closewin_cend(void *data)
298
{
40759 ripley 299
    DEstruct DE = (DEstruct) data;
300
    closewin(DE);
14561 luke 301
}
302
 
44345 ripley 303
SEXP in_RX11_dataentry(SEXP call, SEXP op, SEXP args, SEXP rho)
13974 hornik 304
{
24274 ripley 305
    SEXP colmodes, tnames, tvec, tvec2, work2;
13974 hornik 306
    SEXPTYPE type;
24274 ripley 307
    int i, j, cnt, len, nprotect;
13974 hornik 308
    RCNTXT cntxt;
24274 ripley 309
    char clab[25];
40754 ripley 310
    char *title = "R Data Editor";
40759 ripley 311
    destruct DE1;
312
    DEstruct DE = &DE1;
13974 hornik 313
 
314
    nprotect = 0;/* count the PROTECT()s */
40759 ripley 315
    PROTECT_WITH_INDEX(DE->work = duplicate(CAR(args)), &DE->wpi); nprotect++;
24274 ripley 316
    colmodes = CADR(args);
40759 ripley 317
    tnames = getAttrib(DE->work, R_NamesSymbol);
13974 hornik 318
 
40759 ripley 319
    if (TYPEOF(DE->work) != VECSXP || TYPEOF(colmodes) != VECSXP)
13974 hornik 320
	errorcall(call, "invalid argument");
321
 
24274 ripley 322
    /* initialize the constants */
13974 hornik 323
 
324
    bufp = buf;
325
    ne = 0;
326
    currentexp = 0;
327
    nneg = 0;
328
    ndecimal = 0;
329
    clength = 0;
40747 ripley 330
    inSpecial = 0;
40759 ripley 331
    DE->ccol = 1;
332
    DE->crow = 1;
333
    DE->colmin = 1;
334
    DE->rowmin = 1;
40747 ripley 335
    PROTECT(ssNA_STRING = duplicate(NA_STRING));
336
    nprotect++;
40759 ripley 337
    DE->bwidth = 5;
338
    DE->hht = 30;
339
    DE->isEditor = TRUE;
13974 hornik 340
 
24274 ripley 341
    /* setup work, names, lens  */
40759 ripley 342
    DE->xmaxused = length(DE->work); DE->ymaxused = 0;
343
    PROTECT_WITH_INDEX(DE->lens = allocVector(INTSXP, DE->xmaxused), &DE->lpi);
24274 ripley 344
    nprotect++;
13974 hornik 345
 
24274 ripley 346
    if (isNull(tnames)) {
40759 ripley 347
	PROTECT_WITH_INDEX(DE->names = allocVector(STRSXP, DE->xmaxused),
348
			   &DE->npi);
349
	for(i = 0; i < DE->xmaxused; i++) {
24274 ripley 350
	    sprintf(clab, "var%d", i);
40759 ripley 351
	    SET_STRING_ELT(DE->names, i, mkChar(clab));
13974 hornik 352
	}
24274 ripley 353
    } else
40759 ripley 354
	PROTECT_WITH_INDEX(DE->names = duplicate(tnames), &DE->npi);
24274 ripley 355
    nprotect++;
40759 ripley 356
    for (i = 0; i < DE->xmaxused; i++) {
357
	int len = LENGTH(VECTOR_ELT(DE->work, i));
358
	INTEGER(DE->lens)[i] = len;
359
	DE->ymaxused = max(len, DE->ymaxused);
360
	type = TYPEOF(VECTOR_ELT(DE->work, i));
25365 ripley 361
	if (LENGTH(colmodes) > 0 && !isNull(VECTOR_ELT(colmodes, i)))
24274 ripley 362
	    type = str2type(CHAR(STRING_ELT(VECTOR_ELT(colmodes, i), 0)));
363
	if (type != STRSXP) type = REALSXP;
40759 ripley 364
	if (isNull(VECTOR_ELT(DE->work, i))) {
24274 ripley 365
	    if (type == NILSXP) type = REALSXP;
40759 ripley 366
	    SET_VECTOR_ELT(DE->work, i, ssNewVector(type, 100));
367
	} else if (!isVector(VECTOR_ELT(DE->work, i)))
24274 ripley 368
	    errorcall(call, "invalid type for value");
369
	else {
40759 ripley 370
	    if (TYPEOF(VECTOR_ELT(DE->work, i)) != type)
371
		SET_VECTOR_ELT(DE->work, i,
372
			       coerceVector(VECTOR_ELT(DE->work, i), type));
24274 ripley 373
	}
13974 hornik 374
    }
375
 
376
 
377
    /* start up the window, more initializing in here */
40759 ripley 378
    if (initwin(DE, title))
65368 ripley 379
	errorcall(call, "unable to start data editor");
13974 hornik 380
 
381
    /* set up a context which will close the window if there is an error */
35450 murdoch 382
    begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
23463 luke 383
		 R_NilValue, R_NilValue);
14561 luke 384
    cntxt.cend = &closewin_cend;
40759 ripley 385
    cntxt.cenddata = (void *) DE;
13974 hornik 386
 
40759 ripley 387
    highlightrect(DE);
13974 hornik 388
 
40759 ripley 389
    cell_cursor_init(DE);
34639 ripley 390
 
40759 ripley 391
    eventloop(DE);
13974 hornik 392
 
393
    endcontext(&cntxt);
40759 ripley 394
    closewin(DE);
395
    if(nView == 0) {
46169 ripley 396
	if(fdView >= 0) { /* might be open after viewers, but unlikely */
397
	    removeInputHandler(&R_InputHandlers,
398
			       getInputHandler(R_InputHandlers,fdView));
399
	    fdView = -1;
400
	}
401
	if(font_set) {
402
	    XFreeFontSet(iodisplay, font_set);
403
	    font_set = NULL;
404
	}
40759 ripley 405
	XCloseDisplay(iodisplay);
406
	iodisplay = NULL;
407
    }
13974 hornik 408
 
409
    /* drop out unused columns */
40759 ripley 410
    for(i = 0, cnt = 0; i < DE->xmaxused; i++)
411
	if(!isNull(VECTOR_ELT(DE->work, i))) cnt++;
412
    if (cnt < DE->xmaxused) {
24274 ripley 413
	PROTECT(work2 = allocVector(VECSXP, cnt)); nprotect++;
40759 ripley 414
	for(i = 0, j = 0; i < DE->xmaxused; i++) {
415
	    if(!isNull(VECTOR_ELT(DE->work, i))) {
416
		SET_VECTOR_ELT(work2, j, VECTOR_ELT(DE->work, i));
417
		INTEGER(DE->lens)[j] = INTEGER(DE->lens)[i];
418
		SET_STRING_ELT(DE->names, j, STRING_ELT(DE->names, i));
24274 ripley 419
		j++;
13974 hornik 420
	    }
421
	}
40759 ripley 422
	REPROTECT(DE->names = lengthgets(DE->names, cnt), DE->npi);
423
    } else work2 = DE->work;
13974 hornik 424
 
24274 ripley 425
    for (i = 0; i < LENGTH(work2); i++) {
40759 ripley 426
	len = INTEGER(DE->lens)[i];
24274 ripley 427
	tvec = VECTOR_ELT(work2, i);
428
	if (LENGTH(tvec) != len) {
429
	    tvec2 = ssNewVector(TYPEOF(tvec), len);
430
	    for (j = 0; j < len; j++) {
431
		if (TYPEOF(tvec) == REALSXP) {
432
			REAL(tvec2)[j] = REAL(tvec)[j];
433
		} else if (TYPEOF(tvec) == STRSXP) {
40747 ripley 434
		    if (STRING_ELT(tvec, j) != ssNA_STRING)
24274 ripley 435
			SET_STRING_ELT(tvec2, j, STRING_ELT(tvec, j));
13974 hornik 436
		    else
437
			SET_STRING_ELT(tvec2, j, NA_STRING);
438
		} else
439
		    error("dataentry: internal memory problem");
24274 ripley 440
	    }
441
	    SET_VECTOR_ELT(work2, i, tvec2);
13974 hornik 442
	}
443
    }
444
 
40759 ripley 445
    setAttrib(work2, R_NamesSymbol, DE->names);
13974 hornik 446
    UNPROTECT(nprotect);
24274 ripley 447
    return work2;
13974 hornik 448
}
449
 
40759 ripley 450
static void dv_closewin_cend(void *data)
451
{
452
    DEstruct DE = (DEstruct) data;
453
    R_ReleaseObject(DE->lens);
454
    R_ReleaseObject(DE->work);
455
    closewin(DE);
456
    free(DE);
457
    nView--;
458
}
459
 
40754 ripley 460
SEXP in_R_X11_dataviewer(SEXP call, SEXP op, SEXP args, SEXP rho)
461
{
40756 ripley 462
    SEXP stitle;
40754 ripley 463
    SEXPTYPE type;
464
    int i, nprotect;
465
    RCNTXT cntxt;
40759 ripley 466
    DEstruct DE = (DEstruct) malloc(sizeof(destruct));
13974 hornik 467
 
40759 ripley 468
    nView++;
469
 
40754 ripley 470
    nprotect = 0;/* count the PROTECT()s */
40759 ripley 471
    DE->work = CAR(args);
472
    DE->names = getAttrib(DE->work, R_NamesSymbol);
40754 ripley 473
 
40759 ripley 474
    if (TYPEOF(DE->work) != VECSXP)
40754 ripley 475
	errorcall(call, "invalid argument");
476
    stitle = CADR(args);
477
    if (!isString(stitle) || LENGTH(stitle) != 1)
478
	errorcall(call, "invalid argument");
479
 
480
    /* initialize the constants */
481
 
482
    bufp = buf;
483
    ne = 0;
484
    currentexp = 0;
485
    nneg = 0;
486
    ndecimal = 0;
487
    clength = 0;
488
    inSpecial = 0;
40759 ripley 489
    DE->ccol = 1;
490
    DE->crow = 1;
491
    DE->colmin = 1;
492
    DE->rowmin = 1;
493
    DE->bwidth = 5;
494
    DE->hht = 10;
495
    DE->isEditor = FALSE;
40754 ripley 496
 
497
    /* setup work, names, lens  */
40759 ripley 498
    DE->xmaxused = length(DE->work); DE->ymaxused = 0;
499
    PROTECT_WITH_INDEX(DE->lens = allocVector(INTSXP, DE->xmaxused), &DE->lpi);
40754 ripley 500
    nprotect++;
501
 
40759 ripley 502
    for (i = 0; i < DE->xmaxused; i++) {
503
	int len = LENGTH(VECTOR_ELT(DE->work, i));
504
	INTEGER(DE->lens)[i] = len;
505
	DE->ymaxused = max(len, DE->ymaxused);
506
	type = TYPEOF(VECTOR_ELT(DE->work, i));
40756 ripley 507
	if (type != STRSXP && type != REALSXP)
508
	    errorcall(call, "invalid argument");
40754 ripley 509
    }
510
 
511
 
512
    /* start up the window, more initializing in here */
40759 ripley 513
    if (initwin(DE, CHAR(STRING_ELT(stitle, 0))))
65368 ripley 514
	errorcall(call, "unable to start data viewer");
40754 ripley 515
 
516
    /* set up a context which will close the window if there is an error */
517
    begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
518
		 R_NilValue, R_NilValue);
40759 ripley 519
    cntxt.cend = &dv_closewin_cend;
520
    cntxt.cenddata = (void *) DE;
40754 ripley 521
 
40759 ripley 522
    highlightrect(DE);
40754 ripley 523
 
40759 ripley 524
    cell_cursor_init(DE);
40754 ripley 525
 
40759 ripley 526
    if(fdView < 0) {
527
	fdView = ConnectionNumber(iodisplay);
528
	addInputHandler(R_InputHandlers, fdView,
529
			R_ProcessX11Events, XActivity);
530
    }
40754 ripley 531
 
40759 ripley 532
    drawwindow(DE);
40756 ripley 533
 
40759 ripley 534
    R_PreserveObject(DE->work); /* also preserves names */
535
    R_PreserveObject(DE->lens);
536
    UNPROTECT(nprotect);
40754 ripley 537
    return R_NilValue;
538
}
539
 
13974 hornik 540
/* Window Drawing Routines */
541
 
40759 ripley 542
static void setcellwidths(DEstruct DE)
13974 hornik 543
{
544
    int i, w, dw;
545
 
40759 ripley 546
    DE->windowWidth = w = 2*DE->bwidth + DE->boxw[0] + BOXW(DE->colmin);
547
    DE->nwide = 2;
13974 hornik 548
    for (i = 2; i < 100; i++) { /* 100 on-screen columns cannot occur */
40759 ripley 549
	dw = BOXW(i + DE->colmin - 1);
550
	if((w += dw) > DE->fullwindowWidth ||
551
	   (!DE->isEditor && i > DE->xmaxused - DE->colmin + 1)) {
552
	    DE->nwide = i;
553
	    DE->windowWidth = w - dw;
13974 hornik 554
	    break;
555
	}
556
    }
557
}
558
 
40759 ripley 559
static void drawwindow(DEstruct DE)
13974 hornik 560
{
561
    int i, st;
562
    XWindowAttributes attribs;
563
 
564
    /* if there is an active cell enter the data in it */
34639 ripley 565
    /*
566
     * case colname input Expose not use.
567
     * closerect();
568
     */
13974 hornik 569
 
570
    /* now set up the window with the new dimensions */
40759 ripley 571
    XGetWindowAttributes(iodisplay, DE->iowindow, &attribs);
572
    DE->bwidth = attribs.border_width;
573
    DE->fullwindowWidth = attribs.width;
574
    DE->fullwindowHeight = attribs.height;
575
    setcellwidths(DE);
576
    DE->nhigh = (DE->fullwindowHeight - 2 * DE->bwidth - DE->hht) / DE->box_h;
577
    DE->windowHeight = DE->nhigh * DE->box_h + 2 * DE->bwidth;
13974 hornik 578
 
40759 ripley 579
    clearwindow(DE);
13974 hornik 580
 
581
 
40759 ripley 582
    for (i = 1; i < DE->nhigh; i++)
583
	drawrectangle(DE, 0, DE->hht + i * DE->box_h, DE->boxw[0], DE->box_h,
584
		      1, 1);
13974 hornik 585
     /* so row 0 and col 0 are reserved for labels */
40759 ripley 586
    DE->colmax = DE->colmin + (DE->nwide - 2);
587
    DE->rowmax = DE->rowmin + (DE->nhigh - 2);
588
    printlabs(DE);
589
    for (i = DE->colmin; i <= DE->colmax; i++) drawcol(DE, i);
13974 hornik 590
 
40759 ripley 591
    if(DE->isEditor) {
40755 ripley 592
	/* draw the quit etc boxes */
13974 hornik 593
 
40759 ripley 594
	i = textwidth(DE, "Quit", 4);
595
	box_coords[0] = st = DE->fullwindowWidth - 6 - DE->bwidth;
40755 ripley 596
	box_coords[1] = st - i;
40759 ripley 597
	drawrectangle(DE, st - i, 3, i + 4, DE->hht - 6, 1, 1);
598
	drawtext(DE, st + 2 - i, DE->hht - 7, "Quit", 4);
13974 hornik 599
 
40755 ripley 600
	box_coords[4] = st = st - 5*i;
40759 ripley 601
	i = textwidth(DE, "Paste", 5);
40755 ripley 602
	box_coords[5] = st - i;
40759 ripley 603
	drawrectangle(DE, st - i, 3, i + 4, DE->hht - 6, 1, 1);
604
	drawtext(DE, st + 2 - i, DE->hht - 7, "Paste", 5);
13974 hornik 605
 
40755 ripley 606
	box_coords[2] = st = st - 2*i;
40759 ripley 607
	i = textwidth(DE, "Copy", 4);
40755 ripley 608
	box_coords[3] = st - i;
40759 ripley 609
	drawrectangle(DE, st - i, 3, i + 4, DE->hht - 6, 1, 1);
610
	drawtext(DE, st + 2 - i, DE->hht - 7, "Copy", 4);
40755 ripley 611
    }
13974 hornik 612
 
40759 ripley 613
    highlightrect(DE);
13974 hornik 614
 
40759 ripley 615
    Rsync(DE);
616
 
13974 hornik 617
}
618
 
40759 ripley 619
static void doHscroll(DEstruct DE, int oldcol)
13974 hornik 620
{
621
    int i, dw;
40759 ripley 622
    int oldnwide = DE->nwide, oldwindowWidth = DE->windowWidth;
13974 hornik 623
 
624
    /* horizontal re-position */
40759 ripley 625
    setcellwidths(DE);
626
    DE->colmax = DE->colmin + (DE->nwide - 2);
627
    if (oldcol < DE->colmin) { /* drop oldcol...colmin - 1 */
628
	dw = DE->boxw[0];
629
	for (i = oldcol; i < DE->colmin; i++) dw += BOXW(i);
630
	copyH(DE, dw, DE->boxw[0], oldwindowWidth - dw + 1);
13974 hornik 631
	dw = oldwindowWidth - BOXW(oldcol) + 1;
40759 ripley 632
	cleararea(DE, dw, DE->hht, DE->fullwindowWidth-dw,
633
		  DE->fullwindowHeight);
13974 hornik 634
	/* oldnwide includes the row labels */
40759 ripley 635
	for (i = oldcol+oldnwide-1; i <= DE->colmax; i++) drawcol(DE, i);
13974 hornik 636
    } else {
637
	/* move one or more cols left */
40759 ripley 638
	dw = BOXW(DE->colmin);
639
	copyH(DE, DE->boxw[0], DE->boxw[0] + dw, DE->windowWidth - dw + 1);
640
	dw = DE->windowWidth + 1;
641
	cleararea(DE, dw, DE->hht, DE->fullwindowWidth-dw,
642
		  DE->fullwindowHeight);
643
	drawcol(DE, DE->colmin);
13974 hornik 644
    }
645
 
40759 ripley 646
    highlightrect(DE);
647
    cell_cursor_init(DE);
13974 hornik 648
 
40759 ripley 649
    Rsync(DE);
13974 hornik 650
}
651
 
652
/* find_coords finds the coordinates of the upper left corner of the
653
   given cell on the screen: row and col are on-screen coords */
654
 
40759 ripley 655
static void find_coords(DEstruct DE,
656
			int row, int col, int *xcoord, int *ycoord)
13974 hornik 657
{
658
    int i, w;
40759 ripley 659
    w = DE->bwidth;
660
    if (col > 0) w += DE->boxw[0];
661
    for(i = 1; i < col; i ++) w += BOXW(i + DE->colmin - 1);
13974 hornik 662
    *xcoord = w;
40759 ripley 663
    *ycoord = DE->bwidth + DE->hht + DE->box_h * row;
13974 hornik 664
}
665
 
666
/* draw the window with the top left box at column wcol and row wrow */
667
 
40759 ripley 668
static void jumpwin(DEstruct DE, int wcol, int wrow)
13974 hornik 669
{
670
    if (wcol < 0 || wrow < 0) {
671
	bell();
672
	return;
673
    }
40759 ripley 674
    closerect(DE);
675
    if (DE->colmin != wcol || DE->rowmin != wrow) {
676
	DE->colmin = wcol;
677
	DE->rowmin = wrow;
678
	closerect(DE);
679
	drawwindow(DE);
680
    } else highlightrect(DE);
13974 hornik 681
}
682
 
40759 ripley 683
static void advancerect(DEstruct DE, DE_DIRECTION which)
13974 hornik 684
{
685
 
686
    /* if we are in the header, changing a name then only down is
687
       allowed */
40759 ripley 688
    if (DE->crow < 1 && which != DOWN) {
13974 hornik 689
	bell();
690
	return;
691
    }
692
 
40759 ripley 693
    closerect(DE);
13974 hornik 694
 
695
    switch (which) {
696
    case UP:
40759 ripley 697
	if (DE->crow == 1) {
698
	    if (DE->rowmin == 1)
13974 hornik 699
		bell();
700
	    else
40759 ripley 701
		jumppage(DE, UP);
13974 hornik 702
	} else
40759 ripley 703
	    DE->crow--;
13974 hornik 704
	break;
705
    case DOWN:
40759 ripley 706
	if (!DE->isEditor && DE->crow+DE->rowmin > DE->ymaxused) {
40755 ripley 707
	    bell();
708
	    break;
709
	}
40759 ripley 710
	if (DE->crow == (DE->nhigh - 1))
711
	    jumppage(DE, DOWN);
13974 hornik 712
	else
40759 ripley 713
	    DE->crow++;
13974 hornik 714
	break;
715
    case RIGHT:
40759 ripley 716
	if (!DE->isEditor && DE->ccol+DE->colmin > DE->xmaxused) {
40755 ripley 717
	    bell();
718
	    break;
719
	}
40759 ripley 720
	if (DE->ccol == (DE->nwide - 1))
721
	    jumppage(DE, RIGHT);
13974 hornik 722
	else
40759 ripley 723
	    DE->ccol++;
13974 hornik 724
	break;
725
    case LEFT:
40759 ripley 726
	if (DE->ccol == 1) {
727
	    if (DE->colmin == 1)
13974 hornik 728
		bell();
729
	    else
40759 ripley 730
		jumppage(DE, LEFT);
13974 hornik 731
	} else
40759 ripley 732
	    DE->ccol--;
13974 hornik 733
	break;
734
    default:
735
	UNIMPLEMENTED("advancerect");
736
    }
737
 
40759 ripley 738
    highlightrect(DE);
34639 ripley 739
 
40759 ripley 740
    cell_cursor_init(DE);
13974 hornik 741
}
742
 
40759 ripley 743
static void cell_cursor_init(DEstruct DE)
34639 ripley 744
{
40759 ripley 745
    int i, whichrow = DE->crow + DE->rowmin - 1,
746
	whichcol = DE->ccol + DE->colmin -1;
34639 ripley 747
    SEXP tmp;
748
 
749
    memset(buf,0,sizeof(buf));
750
 
40759 ripley 751
    if (DE->crow == 0 ){
34639 ripley 752
	strncpy(buf,
40759 ripley 753
		get_col_name(DE, whichcol),
34639 ripley 754
		BOOSTED_BUF_SIZE-1);
755
    } else {
40759 ripley 756
	if (length(DE->work) >= whichcol) {
757
	    tmp = VECTOR_ELT(DE->work, whichcol - 1);
34639 ripley 758
	    if (tmp != R_NilValue &&
759
		(i = whichrow - 1) < LENGTH(tmp) ) {
54049 ripley 760
		PrintDefaults();
34639 ripley 761
		if (TYPEOF(tmp) == REALSXP) {
40747 ripley 762
		    strncpy(buf, EncodeElement(tmp, i, 0, '.'),
763
			    BOOSTED_BUF_SIZE-1);
34639 ripley 764
		} else if (TYPEOF(tmp) == STRSXP) {
40747 ripley 765
		    if (STRING_ELT(tmp, i) != ssNA_STRING)
34639 ripley 766
			strncpy(buf, EncodeElement(tmp, i, 0, '.'),
767
				BOOSTED_BUF_SIZE-1);
768
		}
769
	    }
770
	}
771
    }
772
    buf[BOOSTED_BUF_SIZE-1] = '\0';
59764 ripley 773
    clength = (int) strlen(buf);
34639 ripley 774
    bufp = buf + clength;
775
}
776
 
41831 rgentlem 777
static const char *get_col_name(DEstruct DE, int col)
13974 hornik 778
{
24274 ripley 779
    static char clab[25];
41831 rgentlem 780
    int nwrote;
40759 ripley 781
    if (col <= DE->xmaxused) {
24274 ripley 782
	/* don't use NA labels */
40759 ripley 783
	SEXP tmp = STRING_ELT(DE->names, col - 1);
24274 ripley 784
	if(tmp != NA_STRING) return(CHAR(tmp));
13974 hornik 785
    }
41831 rgentlem 786
    nwrote = snprintf(clab, 25, "var%d", col);
787
    if (nwrote >= 25)
45475 ripley 788
	error("get_col_name: column number too big to stringify");
41831 rgentlem 789
    return (const char *)clab;
13974 hornik 790
}
791
 
40759 ripley 792
static int get_col_width(DEstruct DE, int col)
13974 hornik 793
{
794
    int i, w = 0, w1;
41831 rgentlem 795
    const char *strp;
24274 ripley 796
    SEXP tmp, lab;
13974 hornik 797
 
40759 ripley 798
    if (DE->nboxchars > 0) return DE->box_w;
799
    if (col <= DE->xmaxused) {
800
	tmp = VECTOR_ELT(DE->work, col - 1);
801
	if (isNull(tmp)) return DE->box_w;
24274 ripley 802
	/* don't use NA labels */
40759 ripley 803
	lab = STRING_ELT(DE->names, col - 1);
24274 ripley 804
	if(lab != NA_STRING) strp = CHAR(lab); else strp = "var12";
54049 ripley 805
	PrintDefaults();
34639 ripley 806
 
59764 ripley 807
	w = textwidth(DE, strp, (int) strlen(strp));
40759 ripley 808
	for (i = 0; i < INTEGER(DE->lens)[col - 1]; i++) {
34294 ripley 809
	    strp = EncodeElement(tmp, i, 0, '.');
59764 ripley 810
	    w1 = textwidth(DE, strp, (int) strlen(strp));
13974 hornik 811
	    if (w1 > w) w = w1;
812
	}
59764 ripley 813
	if(w < 0.5*DE->box_w) w = (int) (0.5*DE->box_w);
814
	if(w < 0.8*DE->box_w) w+= (int) (0.1*DE->box_w);
13974 hornik 815
	if(w > 600) w = 600;
816
	return w+8;
817
    }
40759 ripley 818
    return DE->box_w;
13974 hornik 819
}
820
 
40759 ripley 821
static CellType get_col_type(DEstruct DE, int col)
13974 hornik 822
{
823
    SEXP tmp;
824
    CellType res = UNKNOWNN;
825
 
40759 ripley 826
    if (col <= DE->xmaxused) {
827
	tmp = VECTOR_ELT(DE->work, col - 1);
13974 hornik 828
	if(TYPEOF(tmp) == REALSXP) res = NUMERIC;
829
	if(TYPEOF(tmp) == STRSXP) res = CHARACTER;
830
    }
831
    return res;
832
}
833
 
834
 
835
/* whichcol is absolute col no, col is position on screen */
40759 ripley 836
static void drawcol(DEstruct DE, int whichcol)
13974 hornik 837
{
40759 ripley 838
    int i, src_x, src_y, len, col = whichcol - DE->colmin + 1,
839
	bw = BOXW(whichcol);
41831 rgentlem 840
    const char *clab;
13974 hornik 841
    SEXP tmp;
842
 
40759 ripley 843
    find_coords(DE, 0, col, &src_x, &src_y);
844
    cleararea(DE, src_x, src_y, bw, DE->windowHeight);
845
    for (i = 0; i < DE->nhigh; i++)
846
	drawrectangle(DE, src_x, DE->hht + i * DE->box_h, bw, DE->box_h, 1, 1);
13974 hornik 847
 
848
    /* now fill it in if it is active */
40759 ripley 849
    clab = get_col_name(DE, whichcol);
59764 ripley 850
    printstring(DE, clab, (int) strlen(clab), 0, col, 0);
13974 hornik 851
 
40759 ripley 852
   if (DE->xmaxused >= whichcol) {
853
	tmp = VECTOR_ELT(DE->work, whichcol - 1);
24274 ripley 854
	if (!isNull(tmp)) {
40759 ripley 855
	    len = min(DE->rowmax, INTEGER(DE->lens)[whichcol - 1]);
856
	    for (i = (DE->rowmin - 1); i < len; i++)
857
		printelt(DE, tmp, i, i - DE->rowmin + 2, col);
13974 hornik 858
	}
859
    }
40759 ripley 860
    Rsync(DE);
13974 hornik 861
}
862
 
863
 
864
/* whichrow is absolute row no */
40759 ripley 865
static void drawrow(DEstruct DE, int whichrow)
13974 hornik 866
{
40759 ripley 867
    int i, src_x, src_y, row = whichrow - DE->rowmin + 1, w;
13974 hornik 868
    char rlab[15];
869
    SEXP tvec;
870
 
40759 ripley 871
    find_coords(DE, row, 0, &src_x, &src_y);
872
    cleararea(DE, src_x, src_y, DE->windowWidth, DE->box_h);
873
    drawrectangle(DE, src_x, src_y, DE->boxw[0], DE->box_h, 1, 1);
13974 hornik 874
 
40759 ripley 875
    sprintf(rlab, DE->labform, whichrow);
59764 ripley 876
    printstring(DE, rlab, (int)strlen(rlab), row, 0, 0);
13974 hornik 877
 
40759 ripley 878
    w = DE->bwidth + DE->boxw[0];
879
    for (i = DE->colmin; i <= DE->colmax; i++) {
880
	drawrectangle(DE, w, src_y, BOXW(i), DE->box_h, 1, 1);
13974 hornik 881
	w += BOXW(i);
882
    }
883
 
40759 ripley 884
    for (i = DE->colmin; i <= DE->colmax; i++) {
885
	if (i > DE->xmaxused) break;
886
	if (!isNull(tvec = VECTOR_ELT(DE->work, i - 1)))
887
	    if (whichrow <= INTEGER(DE->lens)[i - 1])
888
		printelt(DE, tvec, whichrow - 1, row, i - DE->colmin + 1);
13974 hornik 889
    }
890
 
40759 ripley 891
    Rsync(DE);
13974 hornik 892
}
893
 
894
/* printelt: print the correct value from vector[vrow] into the
895
   spreadsheet in row ssrow and col sscol */
896
 
897
/* WARNING: This has no check that you're not beyond the end of the
898
   vector. Caller must check. */
899
 
40759 ripley 900
static void printelt(DEstruct DE, SEXP invec, int vrow, int ssrow, int sscol)
13974 hornik 901
{
41861 ripley 902
    const char *strp;
54049 ripley 903
    PrintDefaults();
13974 hornik 904
    if (TYPEOF(invec) == REALSXP) {
40747 ripley 905
	strp = EncodeElement(invec, vrow, 0, '.');
59764 ripley 906
	printstring(DE ,strp, (int) strlen(strp), ssrow, sscol, 0);
13974 hornik 907
    }
908
    else if (TYPEOF(invec) == STRSXP) {
40747 ripley 909
	if (STRING_ELT(invec, vrow) != ssNA_STRING) {
34294 ripley 910
	    strp = EncodeElement(invec, vrow, 0, '.');
59764 ripley 911
	    printstring(DE ,strp, (int) strlen(strp), ssrow, sscol, 0);
13974 hornik 912
	}
913
    }
914
    else
915
	error("dataentry: internal memory error");
916
}
917
 
918
 
40759 ripley 919
static void drawelt(DEstruct DE, int whichrow, int whichcol)
13974 hornik 920
{
921
    int i;
41831 rgentlem 922
    const char *clab;
13974 hornik 923
    SEXP tmp;
924
 
925
    if (whichrow == 0) {
40759 ripley 926
	clab = get_col_name(DE, whichcol + DE->colmin - 1);
59764 ripley 927
	printstring(DE ,clab, (int) strlen(clab), 0, whichcol, 0);
13974 hornik 928
    } else {
40759 ripley 929
	if (DE->xmaxused >= whichcol + DE->colmin - 1) {
930
	    tmp = VECTOR_ELT(DE->work, whichcol + DE->colmin - 2);
931
	    if (!isNull(tmp) && (i = DE->rowmin + whichrow - 2) <
932
		INTEGER(DE->lens)[whichcol + DE->colmin - 2] )
933
		printelt(DE, tmp, i, whichrow, whichcol);
13974 hornik 934
	} else
40759 ripley 935
	    printstring(DE, "", 0, whichrow,  whichcol, 0);
13974 hornik 936
    }
937
 
40759 ripley 938
    Rsync(DE);
13974 hornik 939
}
940
 
40759 ripley 941
static void jumppage(DEstruct DE, DE_DIRECTION dir)
13974 hornik 942
{
943
    int i, w, oldcol, wcol;
944
 
945
    switch (dir) {
946
    case UP:
40759 ripley 947
	DE->rowmin--;
948
	DE->rowmax--;
949
	copyarea(DE, 0, DE->hht + DE->box_h, 0, DE->hht + 2 * DE->box_h);
950
	drawrow(DE, DE->rowmin);
13974 hornik 951
	break;
952
    case DOWN:
40759 ripley 953
	if (DE->rowmax >= 65535) return;
954
	DE->rowmin++;
955
	DE->rowmax++;
956
	copyarea(DE, 0, DE->hht + 2 * DE->box_h, 0, DE->hht + DE->box_h);
957
	drawrow(DE, DE->rowmax);
13974 hornik 958
	break;
959
    case LEFT:
40759 ripley 960
	DE->colmin--;
961
	doHscroll(DE, DE->colmin+1);
13974 hornik 962
	break;
963
    case RIGHT:
40759 ripley 964
	oldcol = DE->colmin;
965
	wcol = DE->colmin + DE->ccol + 1; /* column to be selected */
45475 ripley 966
	/* There may not be room to fit the next column in */
40759 ripley 967
	w = DE->fullwindowWidth - DE->boxw[0] - BOXW(DE->colmax + 1);
968
	for (i = DE->colmax; i >= oldcol; i--) {
13974 hornik 969
	    w -= BOXW(i);
970
	    if(w < 0) {
40759 ripley 971
		DE->colmin = i + 1;
13974 hornik 972
		break;
973
	    }
974
	}
40759 ripley 975
	DE->ccol = wcol - DE->colmin;
976
	doHscroll(DE, oldcol);
13974 hornik 977
	break;
978
    }
979
}
980
/* draw a rectangle, used to highlight/downlight the current box */
981
 
40759 ripley 982
static void printrect(DEstruct DE, int lwd, int fore)
13974 hornik 983
{
984
    int x, y;
40759 ripley 985
    find_coords(DE, DE->crow, DE->ccol, &x, &y);
986
    drawrectangle(DE, x + lwd - 1, y + lwd - 1,
987
		  BOXW(DE->ccol+DE->colmin-1) - lwd + 1,
988
		  DE->box_h - lwd + 1, lwd, fore);
989
    Rsync(DE);
13974 hornik 990
}
991
 
40759 ripley 992
static void downlightrect(DEstruct DE)
13974 hornik 993
{
40759 ripley 994
    printrect(DE, 2, 0);
995
    printrect(DE, 1, 1);
13974 hornik 996
}
997
 
40759 ripley 998
static void highlightrect(DEstruct DE)
13974 hornik 999
{
40759 ripley 1000
    printrect(DE, 2, 1);
13974 hornik 1001
}
1002
 
24274 ripley 1003
 
40759 ripley 1004
static Rboolean getccol(DEstruct DE)
13974 hornik 1005
{
1006
    SEXP tmp, tmp2;
1007
    int i, len, newlen, wcol, wrow;
1008
    SEXPTYPE type;
24274 ripley 1009
    char clab[25];
40747 ripley 1010
    Rboolean newcol = FALSE;
13974 hornik 1011
 
40759 ripley 1012
    wcol = DE->ccol + DE->colmin - 1;
1013
    wrow = DE->crow + DE->rowmin - 1;
1014
    if (wcol > DE->xmaxused) {
24274 ripley 1015
	/* extend work, names and lens */
40759 ripley 1016
	REPROTECT(DE->work = lengthgets(DE->work, wcol), DE->wpi);
1017
	REPROTECT(DE->names = lengthgets(DE->names, wcol), DE->npi);
1018
	for (i = DE->xmaxused; i < wcol; i++) {
24274 ripley 1019
	    sprintf(clab, "var%d", i + 1);
40759 ripley 1020
	    SET_STRING_ELT(DE->names, i, mkChar(clab));
24274 ripley 1021
	}
40759 ripley 1022
	REPROTECT(DE->lens = lengthgets(DE->lens, wcol), DE->lpi);
1023
	DE->xmaxused = wcol;
24274 ripley 1024
    }
40759 ripley 1025
    if (isNull(VECTOR_ELT(DE->work, wcol - 1))) {
13974 hornik 1026
	newcol = TRUE;
40759 ripley 1027
	SET_VECTOR_ELT(DE->work, wcol - 1,
1028
		       ssNewVector(REALSXP, max(100, wrow)));
1029
	INTEGER(DE->lens)[wcol - 1] = 0;
13974 hornik 1030
    }
40759 ripley 1031
    if (!isVector(tmp = VECTOR_ELT(DE->work, wcol - 1)))
13974 hornik 1032
	error("internal type error in dataentry");
40759 ripley 1033
    len = INTEGER(DE->lens)[wcol - 1];
24274 ripley 1034
    type = TYPEOF(tmp);
13974 hornik 1035
    if (len < wrow) {
1036
	for (newlen = max(len * 2, 10) ; newlen < wrow ; newlen *= 2)
1037
	    ;
1038
	tmp2 = ssNewVector(type, newlen);
1039
	for (i = 0; i < len; i++)
1040
	    if (type == REALSXP)
24274 ripley 1041
		REAL(tmp2)[i] = REAL(tmp)[i];
13974 hornik 1042
	    else if (type == STRSXP)
24274 ripley 1043
		SET_STRING_ELT(tmp2, i, STRING_ELT(tmp, i));
13974 hornik 1044
	    else
1045
		error("internal type error in dataentry");
40759 ripley 1046
	SET_VECTOR_ELT(DE->work, wcol - 1, tmp2);
13974 hornik 1047
    }
40747 ripley 1048
    return newcol;
13974 hornik 1049
}
1050
 
46045 murdoch 1051
static SEXP processEscapes(SEXP x)
1052
{
1053
    SEXP newval, pattern, replacement, expr;
1054
    ParseStatus status;
51659 maechler 1055
 
46045 murdoch 1056
    /* We process escape sequences in a scalar string by escaping
1057
       unescaped quotes, then quoting the whole thing and parsing it.  This
1058
       is supposed to be equivalent to the R code
1059
 
1060
       newval <- gsub(perl=TRUE, "(?<!\\\\)((\\\\\\\\)*)\"", "\\1\\\\\"", x)
1061
       newval <- sub('(^.*$)', '"\1"', newval)
1062
       newval <- eval(parse(text=newval))
1063
 
1064
       We do it this way to avoid extracting the escape handling
1065
       code from the parser.  We need it in C code because this may be executed
1066
       numerous times from C in dataentry.c */
51659 maechler 1067
 
46045 murdoch 1068
    PROTECT( pattern = mkString("(?<!\\\\)((\\\\\\\\)*)\"") );
1069
    PROTECT( replacement = mkString("\\1\\\\\"") );
67099 luke 1070
    SEXP s_gsub = install("gsub");
1071
    PROTECT( expr = lang5(s_gsub, ScalarLogical(1), pattern, replacement, x) );
46045 murdoch 1072
    SET_TAG( CDR(expr), install("perl") );
1073
 
1074
    PROTECT( newval = eval(expr, R_BaseEnv) );
1075
    PROTECT( pattern = mkString("(^.*$)") );
1076
    PROTECT( replacement = mkString("\"\\1\"") );
1077
    PROTECT( expr = lang4(install("sub"), pattern, replacement, newval) );
1078
    PROTECT( newval = eval(expr, R_BaseEnv) );
1079
    PROTECT( expr = R_ParseVector( newval, 1, &status, R_NilValue) );
51659 maechler 1080
 
46045 murdoch 1081
    /* We only handle the first entry. If this were available more generally,
1082
       we'd probably want to loop over all of expr */
51659 maechler 1083
 
46045 murdoch 1084
    if (status == PARSE_OK && length(expr))
1085
	PROTECT( newval = eval(VECTOR_ELT(expr, 0), R_BaseEnv) );
1086
    else
1087
	PROTECT( newval = R_NilValue );  /* protect just so the count doesn't change */
1088
    UNPROTECT(10);
1089
    return newval;
1090
}
1091
 
13974 hornik 1092
/* close up the entry to a cell, put the value that has been entered
1093
   into the correct place and as the correct type */
1094
 
40759 ripley 1095
static void closerect(DEstruct DE)
13974 hornik 1096
{
24274 ripley 1097
    SEXP cvec;
40759 ripley 1098
    int i, wcol = DE->ccol + DE->colmin - 1,
1099
	wrow = DE->rowmin + DE->crow - 1, wrow0;
24274 ripley 1100
    char clab[25];
40747 ripley 1101
    Rboolean newcol;
13974 hornik 1102
 
1103
    *bufp = '\0';
1104
 
1105
    /* first check to see if anything has been entered */
1106
    if (CellModified) {
40759 ripley 1107
	if (DE->crow == 0) {
13974 hornik 1108
	    if (clength != 0) {
1109
		/* then we are entering a new column name */
40759 ripley 1110
		if (DE->xmaxused < wcol) {
24274 ripley 1111
		    /* extend work, names and lens */
40759 ripley 1112
		    REPROTECT(DE->work = lengthgets(DE->work, wcol), DE->wpi);
1113
		    REPROTECT(DE->names = lengthgets(DE->names, wcol),
1114
			      DE->npi);
1115
		    for (i = DE->xmaxused; i < wcol - 1; i++) {
24274 ripley 1116
			sprintf(clab, "var%d", i + 1);
40759 ripley 1117
			SET_STRING_ELT(DE->names, i, mkChar(clab));
24274 ripley 1118
		    }
40759 ripley 1119
		    REPROTECT(DE->lens = lengthgets(DE->lens, wcol), DE->lpi);
1120
		    DE->xmaxused = wcol;
24274 ripley 1121
		}
40759 ripley 1122
		SET_STRING_ELT(DE->names, wcol - 1, mkChar(buf));
59764 ripley 1123
		printstring(DE ,buf, (int) strlen(buf), 0, wcol, 0);
13974 hornik 1124
	    } else {
40759 ripley 1125
		sprintf(buf, "var%d", DE->ccol);
59764 ripley 1126
		printstring(DE ,buf, (int) strlen(buf), 0, wcol, 0);
13974 hornik 1127
	    }
1128
	} else {
40759 ripley 1129
	    newcol = getccol(DE);
1130
	    cvec = VECTOR_ELT(DE->work, wcol - 1);
1131
	    wrow0 = INTEGER(DE->lens)[wcol - 1];
1132
	    if (wrow > wrow0) INTEGER(DE->lens)[wcol - 1] = wrow;
1133
	    DE->ymaxused = max(DE->ymaxused, wrow);
24274 ripley 1134
	    if (clength != 0) {
1135
		/* do it this way to ensure NA, Inf, ...  can get set */
1136
		char *endp;
1137
		double new = R_strtod(buf, &endp);
1138
		Rboolean warn = !isBlankString(endp);
46045 murdoch 1139
		if (TYPEOF(cvec) == STRSXP) {
1140
		    SEXP newval;
1141
		    PROTECT( newval = mkString(buf) );
1142
		    PROTECT( newval = processEscapes(newval) );
1143
		    if (TYPEOF(newval) == STRSXP && length(newval) == 1)
1144
			SET_STRING_ELT(cvec, wrow - 1, STRING_ELT(newval, 0));
1145
		    else
1146
			warning("dataentry: parse error on string");
1147
		    UNPROTECT(2);
1148
		} else
24274 ripley 1149
		    REAL(cvec)[wrow - 1] = new;
40747 ripley 1150
		if (newcol && warn) {
24274 ripley 1151
		    /* change mode to character */
40747 ripley 1152
		    SEXP tmp = coerceVector(cvec, STRSXP);
67351 luke 1153
		    PROTECT(tmp);
40747 ripley 1154
		    SET_STRING_ELT(tmp, wrow - 1, mkChar(buf));
40759 ripley 1155
		    SET_VECTOR_ELT(DE->work, wcol - 1, tmp);
67351 luke 1156
		    UNPROTECT(1);
24274 ripley 1157
		}
1158
	    } else {
1159
		if (TYPEOF(cvec) == STRSXP)
1160
		    SET_STRING_ELT(cvec, wrow - 1, NA_STRING);
1161
		else
1162
		    REAL(cvec)[wrow - 1] = NA_REAL;
24255 ripley 1163
	    }
40759 ripley 1164
	    drawelt(DE, DE->crow, DE->ccol); /* to get the cell scrolling right */
1165
	    if(wrow > wrow0) drawcol(DE, wcol); /* to fill in NAs */
24255 ripley 1166
	}
13974 hornik 1167
    }
1168
    CellModified = FALSE;
1169
 
40759 ripley 1170
    downlightrect(DE);
13974 hornik 1171
 
1172
    ndecimal = 0;
1173
    nneg = 0;
1174
    ne = 0;
1175
    currentexp = 0;
1176
    clength = 0;
40747 ripley 1177
    inSpecial = 0;
13974 hornik 1178
    bufp = buf;
1179
}
1180
 
1181
/* print a null terminated string, check to see if it is longer than
1182
   the print area and print it, left adjusted if necessary; clear the
1183
   area of previous text; */
1184
 
1185
/* This version will only display 200 chars, but the maximum col width
1186
   will not allow that many */
41831 rgentlem 1187
static void printstring(DEstruct DE, const char *ibuf, int buflen, int row,
45475 ripley 1188
			int col, int left)
13974 hornik 1189
{
1190
    int i, x_pos, y_pos, bw, bufw;
34639 ripley 1191
    char pbuf[BOOSTED_BUF_SIZE];
1192
    int wcsbufw,j;
1193
    wchar_t wcspbuf[BOOSTED_BUF_SIZE], *wcspc = wcspbuf;
1194
    wchar_t wcs[BOOSTED_BUF_SIZE];
1195
    char    s[BOOSTED_BUF_SIZE];
1196
    wchar_t *w_p;
1197
    char    *p;
1198
    int cnt;
13974 hornik 1199
 
40759 ripley 1200
    find_coords(DE, row, col, &x_pos, &y_pos);
1201
    if (col == 0) bw = DE->boxw[0]; else bw = BOXW(col+DE->colmin-1);
1202
    cleararea(DE, x_pos + 2, y_pos + 2, bw - 3, DE->box_h - 3);
34639 ripley 1203
    bufw = (buflen > BOOSTED_BUF_SIZE-1) ? BOOSTED_BUF_SIZE-1 : buflen;
1204
    strncpy(pbuf, ibuf, bufw);
1205
    pbuf[bufw] = '\0';
1206
 
1207
    p = pbuf;
62295 ripley 1208
    wcsbufw = (int) mbsrtowcs(wcspbuf, (const char **)&p, bufw, NULL);
34639 ripley 1209
    wcspbuf[wcsbufw]=L'\0';
1210
    if(left) {
45475 ripley 1211
	for (i = wcsbufw; i > 1; i--) {
34639 ripley 1212
	    for(j=0;*(wcspc+j)!=L'\0';j++)wcs[j]=*(wcspc+j);
1213
	    wcs[j]=L'\0';
1214
	    w_p=wcs;
62295 ripley 1215
	    cnt = (int) wcsrtombs(s,(const wchar_t **)&w_p,sizeof(s)-1,NULL);
34639 ripley 1216
	    s[cnt]='\0';
59764 ripley 1217
	    if (textwidth(DE, s, (int) strlen(s)) < (bw - DE->text_offset)) break;
45475 ripley 1218
	    *(++wcspc) = L'<';
1219
	}
34639 ripley 1220
    } else {
45475 ripley 1221
	for (i = wcsbufw; i > 1; i--) {
34639 ripley 1222
	    for(j=0;*(wcspc+j)!=L'\0';j++)wcs[j]=*(wcspc+j);
1223
	    wcs[j]=L'\0';
1224
	    w_p=wcs;
62314 ripley 1225
	    cnt = (int) wcsrtombs(s,(const wchar_t **)&w_p,sizeof(s)-1,NULL);
34639 ripley 1226
	    s[cnt]='\0';
59764 ripley 1227
	    if (textwidth(DE, s, (int) strlen(s)) < (bw - DE->text_offset)) break;
45475 ripley 1228
	    *(wcspbuf + i - 2) = L'>';
1229
	    *(wcspbuf + i - 1) = L'\0';
1230
	}
34639 ripley 1231
    }
1232
    for(j=0;*(wcspc+j)!=L'\0';j++) wcs[j]=*(wcspc+j);
1233
    wcs[j]=L'\0';
1234
    w_p=wcs;
62295 ripley 1235
    cnt = (int) wcsrtombs(s,(const wchar_t **)&w_p,sizeof(s)-1,NULL);
34639 ripley 1236
 
40759 ripley 1237
    drawtext(DE, x_pos + DE->text_offset, y_pos + DE->box_h - DE->text_offset,
1238
	     s, cnt);
34639 ripley 1239
 
40759 ripley 1240
    Rsync(DE);
13974 hornik 1241
}
1242
 
40759 ripley 1243
static void clearrect(DEstruct DE)
13974 hornik 1244
{
1245
    int x_pos, y_pos;
1246
 
40759 ripley 1247
    find_coords(DE, DE->crow, DE->ccol, &x_pos, &y_pos);
1248
    cleararea(DE, x_pos, y_pos, BOXW(DE->ccol+DE->colmin-1), DE->box_h);
1249
    Rsync(DE);
13974 hornik 1250
}
1251
 
1252
/* handlechar has to be able to parse decimal numbers and strings,
1253
   depending on the current column type, only printing characters
1254
   should get this far */
1255
 
1256
/* --- Not true! E.g. ESC ends up in here... */
1257
 
60260 ripley 1258
#include <rlocale.h>
1259
 
34639 ripley 1260
/* <FIXME> This is not correct for stateful MBCSs, but that's hard to
32677 ripley 1261
   do as we get a char at a time */
40759 ripley 1262
static void handlechar(DEstruct DE, char *text)
13974 hornik 1263
{
44186 ripley 1264
    int c = text[0], j;
34639 ripley 1265
    wchar_t wcs[BOOSTED_BUF_SIZE];
13974 hornik 1266
 
34639 ripley 1267
    memset(wcs,0,sizeof(wcs));
1268
 
32432 ripley 1269
    if ( c == '\033' ) { /* ESC */
13974 hornik 1270
	CellModified = FALSE;
1271
	clength = 0;
45475 ripley 1272
	bufp = buf;
40759 ripley 1273
	drawelt(DE, DE->crow, DE->ccol);
1274
	cell_cursor_init(DE);
13974 hornik 1275
	return;
1276
    } else
1277
	CellModified = TRUE;
1278
 
1279
    if (clength == 0) {
1280
 
40759 ripley 1281
	if (DE->crow == 0)	                        /* variable name */
13974 hornik 1282
	    currentexp = 3;
1283
	else
40759 ripley 1284
	    switch(get_col_type(DE, DE->ccol + DE->colmin - 1)) {
13974 hornik 1285
	    case NUMERIC:
1286
		currentexp = 1;
1287
		break;
1288
	    default:
1289
		currentexp = 2;
1290
	    }
40759 ripley 1291
	clearrect(DE);
1292
	highlightrect(DE);
13974 hornik 1293
    }
1294
 
34639 ripley 1295
    /* NA number? */
40759 ripley 1296
    if (get_col_type(DE, DE->ccol + DE->colmin - 1) == NUMERIC) {
40754 ripley 1297
	/* input numeric for NA of buffer , suppress NA etc.*/
1298
	if(strcmp(buf, "NA") == 0 || strcmp(buf, "NaN") == 0 ||
1299
	   strcmp(buf, "Inf") == 0 || strcmp(buf, "-Inf") == 0) {
34639 ripley 1300
	    buf[0] = '\0';
1301
	    clength = 0;
1302
	    bufp = buf;
1303
	}
1304
    }
1305
 
32518 ripley 1306
    if (currentexp == 1) {	/* we are parsing a number */
49591 ripley 1307
	char *mbs = text;
62295 ripley 1308
	int i, cnt = (int)mbsrtowcs(wcs, (const char **)&mbs, (int) strlen(text)+1, NULL);
34639 ripley 1309
 
49591 ripley 1310
	for(i = 0; i < cnt; i++) {
1311
	    switch (wcs[i]) {
1312
	    case L'-':
1313
		if (nneg == 0) nneg++; else goto donehc;
40747 ripley 1314
	    break;
49591 ripley 1315
	    case L'.':
1316
		if (ndecimal == 0) ndecimal++; else goto donehc;
13974 hornik 1317
	    break;
49591 ripley 1318
	    case L'e':
1319
	    case L'E':
1320
		if (ne == 0) {
1321
		    nneg = ndecimal = 0;	/* might have decimal in exponent */
1322
		    ne++;
1323
		} else goto donehc;
13974 hornik 1324
	    break;
49591 ripley 1325
	    case L'N':
1326
		if(nneg) goto donehc;
1327
	    case L'I':
1328
		inSpecial++;
13974 hornik 1329
	    break;
49591 ripley 1330
	    default:
1331
		if (!inSpecial && !iswdigit(wcs[i])) goto donehc;
1332
		break;
1333
	    }
13974 hornik 1334
	}
32518 ripley 1335
    }
13974 hornik 1336
    if (currentexp == 3) {
40747 ripley 1337
	char *mbs = text;
62295 ripley 1338
	int i, cnt = (int) mbsrtowcs(wcs, (const char **)&mbs, (int) strlen(text)+1, NULL);
40747 ripley 1339
	for(i = 0; i < cnt; i++) {
1340
	    if (iswspace(wcs[i])) goto donehc;
40759 ripley 1341
	    if (clength == 0 && wcs[i] != L'.' && !iswalpha(wcs[i]))
1342
		goto donehc;
40747 ripley 1343
	    else if (wcs[i] != L'.' && !iswalnum(wcs[i])) goto donehc;
1344
	}
13974 hornik 1345
    }
1346
 
34639 ripley 1347
    if (clength+strlen(text) > BOOSTED_BUF_SIZE - MB_CUR_MAX - 1) {
13974 hornik 1348
	warning("dataentry: expression too long");
1349
	goto donehc;
1350
    }
1351
 
42804 maechler 1352
    /* as originally written, this left an undefined byte at
1353
       the end of bufp, followed by a zero byte; luckily, the storage
1354
       pointed to by bufp had already been zeroed, so the undefined
1355
       byte was in fact zero.  */
1356
    strcpy(bufp, text);
59764 ripley 1357
    bufp += (j = (int) strlen(text));
42804 maechler 1358
    clength += j;
40759 ripley 1359
    printstring(DE, buf, clength, DE->crow, DE->ccol, 1);
13974 hornik 1360
    return;
1361
 
1362
 donehc:
1363
    bell();
1364
}
1365
 
40759 ripley 1366
static void printlabs(DEstruct DE)
13974 hornik 1367
{
41831 rgentlem 1368
    char clab[15];
1369
    const char *p;
13974 hornik 1370
    int i;
1371
 
40759 ripley 1372
    for (i = DE->colmin; i <= DE->colmax; i++) {
1373
	p = get_col_name(DE, i);
59764 ripley 1374
	printstring(DE, p, (int) strlen(p), 0, i - DE->colmin + 1, 0);
13974 hornik 1375
    }
40759 ripley 1376
    for (i = DE->rowmin; i <= DE->rowmax; i++) {
1377
	sprintf(clab, DE->labform, i);
59764 ripley 1378
	printstring(DE, clab, (int) strlen(clab), i - DE->rowmin + 1, 0, 0);
13974 hornik 1379
    }
1380
}
1381
 
45475 ripley 1382
	       /* ================ X11-specific ================ */
13974 hornik 1383
 
1384
/* find out whether the button click was in the quit box */
1385
static int checkquit(int xw)
1386
{
1387
    if (xw > box_coords[1] && xw < box_coords[0]) return 1;
1388
    if (xw > box_coords[3] && xw < box_coords[2]) return 2;
1389
    if (xw > box_coords[5] && xw < box_coords[4]) return 3;
1390
    return 0;
1391
}
1392
 
1393
/* when a buttonpress event happens find the square that is being
1394
   pointed to if the pointer is in the header we need to see if the
1395
   quit button was pressed and if so quit. This is done by having
32432 ripley 1396
   findcell return an int which is one if we should quit and zero
13974 hornik 1397
   otherwise */
1398
 
40759 ripley 1399
static int findcell(DEstruct DE)
13974 hornik 1400
{
1401
 
1402
    int xw, yw, xr, yr, wcol=0, wrow, i, w;
1403
    unsigned int keys;
1404
    Window root, child;
1405
 
40759 ripley 1406
    closerect(DE);
1407
    XQueryPointer(iodisplay, DE->iowindow, &root, &child,
13974 hornik 1408
		  &xr, &yr, &xw, &yw, &keys);
1409
 
1410
    if (keys & Button1Mask) { /* left click */
1411
 
1412
	/* check to see if the click was in the header */
1413
 
40759 ripley 1414
	if (yw < DE->hht + DE->bwidth) {
13974 hornik 1415
	    i =  checkquit(xw);
1416
	    if (i == 1) return 1;
40759 ripley 1417
	    else if (i == 2) copycell(DE);
1418
	    else if (i == 3) pastecell(DE, DE->crow, DE->ccol);
13974 hornik 1419
	    return 0;
1420
	}
1421
 
32424 ripley 1422
 
13974 hornik 1423
	/* see if it is in the row labels */
40759 ripley 1424
	if (xw < DE->bwidth + DE->boxw[0]) {
13974 hornik 1425
	    bell();
40759 ripley 1426
	    highlightrect(DE);
13974 hornik 1427
	    return 0;
1428
	}
1429
	/* translate to box coordinates */
40759 ripley 1430
	wrow = (yw - DE->bwidth - DE->hht) / DE->box_h;
1431
	w = DE->bwidth + DE->boxw[0];
1432
	for (i = 1; i <= DE->nwide; i++)
1433
	    if((w += BOXW(i+DE->colmin-1)) > xw) {
13974 hornik 1434
		wcol = i;
1435
		break;
1436
	    }
1437
 
1438
	/* next check to see if it is in the column labels */
1439
 
40759 ripley 1440
	if (yw < DE->hht + DE->bwidth + DE->box_h) {
1441
	    if (xw > DE->bwidth + DE->boxw[0])
1442
		popupmenu(DE, xr, yr, wcol, wrow);
13974 hornik 1443
	    else {
40759 ripley 1444
		highlightrect(DE);
13974 hornik 1445
		bell();
1446
	    }
40759 ripley 1447
	} else if (wrow > DE->nhigh - 1 || wcol > DE->nwide -1) {
13974 hornik 1448
		/* off the grid */
40759 ripley 1449
		highlightrect(DE);
13974 hornik 1450
		bell();
40759 ripley 1451
	} else if (wcol != DE->ccol || wrow != DE->crow) {
1452
	    DE->ccol = wcol;
1453
	    DE->crow = wrow;
13974 hornik 1454
	}
1455
    }
1456
    if (keys & Button2Mask) { /* Paste */
1457
	int row, col = 0;
32424 ripley 1458
 
40759 ripley 1459
	if (yw < DE->hht + DE->bwidth || xw < DE->bwidth + DE->boxw[0])
1460
	    return 0;
13974 hornik 1461
 
1462
	/* translate to box coordinates */
40759 ripley 1463
	row = (yw - DE->bwidth - DE->hht) / DE->box_h;
1464
	w = DE->bwidth + DE->boxw[0];
1465
	for (i = 1; i <= DE->nwide; i++)
1466
	    if ((w += BOXW(i+DE->colmin-1)) > xw) {
13974 hornik 1467
		col = i;
1468
		break;
1469
	    }
40759 ripley 1470
	pastecell(DE, row, col);
13974 hornik 1471
    }
40759 ripley 1472
    highlightrect(DE);
13974 hornik 1473
    return 0;
1474
}
1475
 
1476
 
1477
/* Event Loop Functions */
45475 ripley 1478
#define mouseDown	ButtonPress
40756 ripley 1479
#define keyDown		KeyPress
1480
#define activateEvt	MapNotify
1481
#define updateEvt	Expose
13974 hornik 1482
 
40759 ripley 1483
static void eventloop(DEstruct DE)
13974 hornik 1484
{
1485
    int done;
1486
    DEEvent ioevent;
46169 ripley 1487
    caddr_t temp;
13974 hornik 1488
 
1489
    done = 0;
1490
    while (done == 0) {
45475 ripley 1491
	XNextEvent(iodisplay, &ioevent);
46169 ripley 1492
	XFindContext(iodisplay, ioevent.xany.window, deContext, &temp);
1493
	if ((DEstruct) temp != DE) { /* so a View window */
1494
	    if (WhichEvent(ioevent) == Expose)
1495
		drawwindow((DEstruct) temp);
1496
	} else {
45475 ripley 1497
	    if (XFilterEvent(&ioevent, None)){
40754 ripley 1498
		if(ioic){
1499
		    XSetICFocus(ioic);
1500
		    if (ioim_style & XIMPreeditPosition)
40759 ripley 1501
			calc_pre_edit_pos(DE);
40754 ripley 1502
		}
1503
		continue;
34639 ripley 1504
	    }
1505
 
13974 hornik 1506
	    switch (WhichEvent(ioevent)) {
34639 ripley 1507
	    case keyDown:/* KeyPress */
40759 ripley 1508
		doSpreadKey(DE, 0, &ioevent);
34639 ripley 1509
		break;
45475 ripley 1510
	    case Expose:
40759 ripley 1511
		while(XCheckTypedEvent(iodisplay, Expose, &ioevent))
1512
		    ;
40754 ripley 1513
		/*
1514
		 * XIM on  - KeyPress - Expose
1515
		 * XIM off - KeyPress - KeyRelease
1516
		 * colname change XIM on mode. type Backspace.
1517
		 */
45475 ripley 1518
		if(DE->crow == 0){
40759 ripley 1519
		    drawwindow(DE);
1520
		    printstring(DE, buf, clength, DE->crow, DE->ccol, 1);
40747 ripley 1521
		} else {
40759 ripley 1522
		    closerect(DE);
1523
		    drawwindow(DE);
1524
		    cell_cursor_init(DE);
34639 ripley 1525
		}
1526
		break;
1527
	    case activateEvt:/* MapNotify */
45475 ripley 1528
		closerect(DE);
40759 ripley 1529
		drawwindow(DE);
1530
		cell_cursor_init(DE);
45475 ripley 1531
		break;
34639 ripley 1532
	    case mouseDown:/* ButtonPress */
40759 ripley 1533
		if(DE->isEditor) {
1534
		    done  = doMouseDown(DE, &ioevent);
1535
		    cell_cursor_init(DE);
40755 ripley 1536
		}
13974 hornik 1537
		break;
1538
	    case MappingNotify:
1539
		RefreshKeyboardMapping(&ioevent);
1540
		break;
1541
	    case ConfigureNotify:
40759 ripley 1542
		while(XCheckTypedEvent(iodisplay, ConfigureNotify, &ioevent))
1543
		    ;
1544
		doConfigure(DE, &ioevent);
1545
		cell_cursor_init(DE);
13974 hornik 1546
		break;
1547
	    case ClientMessage:
1548
		if(ioevent.xclient.message_type == _XA_WM_PROTOCOLS
40759 ripley 1549
		   && ioevent.xclient.data.l[0] == DE->prot) {
13974 hornik 1550
		    /* user clicked on `close' aka `destroy' */
40754 ripley 1551
		    done = 1;
13974 hornik 1552
		}
1553
		break;
1554
	    }
1555
	}
1556
    }
1557
}
1558
 
40756 ripley 1559
static void R_ProcessX11Events(void *data)
1560
{
40759 ripley 1561
    caddr_t temp;
1562
    DEstruct DE = NULL;
40756 ripley 1563
    DEEvent ioevent;
1564
    int done = 0;
1565
 
46169 ripley 1566
    while (nView && XPending(iodisplay)) {
45475 ripley 1567
	XNextEvent(iodisplay, &ioevent);
40759 ripley 1568
	XFindContext(iodisplay, ioevent.xany.window, deContext, &temp);
1569
	DE = (DEstruct) temp;
40756 ripley 1570
	switch (WhichEvent(ioevent)) {
1571
	case keyDown:/* KeyPress */
40759 ripley 1572
	    doSpreadKey(DE, 0, &ioevent);
40756 ripley 1573
	    break;
1574
	case Expose:
40759 ripley 1575
	    while(XCheckTypedEvent(iodisplay, Expose, &ioevent))
1576
		;
1577
	    drawwindow(DE);
40756 ripley 1578
	    break;
1579
	case MappingNotify:
1580
	    RefreshKeyboardMapping(&ioevent);
1581
	    break;
1582
	case ConfigureNotify:
40759 ripley 1583
	    while(XCheckTypedEvent(iodisplay, ConfigureNotify, &ioevent))
1584
		;
1585
	    doConfigure(DE, &ioevent);
1586
	    cell_cursor_init(DE);
40756 ripley 1587
	    break;
1588
	case activateEvt:/* MapNotify */
1589
	    break;
1590
	case ClientMessage:
1591
	    if(ioevent.xclient.message_type == _XA_WM_PROTOCOLS
40759 ripley 1592
	       && ioevent.xclient.data.l[0] == DE->prot) {
40756 ripley 1593
		/* user clicked on `close' aka `destroy' */
1594
		done = 1;
1595
	    }
1596
	    break;
1597
	}
1598
    }
1599
    if(done) {
40759 ripley 1600
	R_ReleaseObject(DE->lens);
1601
	R_ReleaseObject(DE->work);
1602
	closewin(DE);
1603
	free(DE);
1604
	nView--;
1605
	if(nView == 0) {
46169 ripley 1606
	    /* NB: this is removing the handler that is currently
1607
	       being used: only OK to free here in R > 2.8.0 */
40759 ripley 1608
	    removeInputHandler(&R_InputHandlers,
1609
			       getInputHandler(R_InputHandlers,fdView));
1610
	    fdView = -1;
46169 ripley 1611
	    if(font_set) {
1612
		XFreeFontSet(iodisplay, font_set);
1613
		font_set = NULL;
1614
	    }
40759 ripley 1615
	    XCloseDisplay(iodisplay);
1616
	    iodisplay = NULL;
1617
	}
45475 ripley 1618
 
40756 ripley 1619
    }
1620
}
1621
 
40759 ripley 1622
static int doMouseDown(DEstruct DE, DEEvent * event)
13974 hornik 1623
{
40759 ripley 1624
    return findcell(DE);
13974 hornik 1625
}
1626
 
40759 ripley 1627
static void doSpreadKey(DEstruct DE, int key, DEEvent * event)
13974 hornik 1628
{
1629
    KeySym iokey;
49591 ripley 1630
    char *text = "";
13974 hornik 1631
 
1632
    iokey = GetKey(event);
48544 ripley 1633
    if(DE->isEditor) text = GetCharP(event);
13974 hornik 1634
 
1635
    if (CheckControl(event))
40759 ripley 1636
	doControl(DE, event);
32424 ripley 1637
    else if ((iokey == XK_Return)  || (iokey == XK_KP_Enter) ||
13974 hornik 1638
	     (iokey == XK_Linefeed)|| (iokey == XK_Down))
40759 ripley 1639
	advancerect(DE, DOWN);
13974 hornik 1640
    else if (iokey == XK_Left)
40759 ripley 1641
	advancerect(DE, LEFT);
13974 hornik 1642
    else if (iokey == XK_Right)
40759 ripley 1643
	advancerect(DE, RIGHT);
13974 hornik 1644
    else if (iokey == XK_Up)
40759 ripley 1645
	advancerect(DE, UP);
22718 ripley 1646
#ifdef XK_Page_Up
1647
    else if (iokey == XK_Page_Up) {
40759 ripley 1648
	int i = DE->rowmin - DE->nhigh + 2;
1649
	jumpwin(DE, DE->colmin, max(1, i));
1650
	cell_cursor_init(DE);
13974 hornik 1651
    }
22718 ripley 1652
#elif defined(XK_Prior)
1653
    else if (iokey == XK_Prior) {
40759 ripley 1654
	int i = DE->rowmin - DE->nhigh + 2;
1655
	jumpwin(DE, DE->colmin, max(1, i));
1656
	cell_cursor_init(DE);
13974 hornik 1657
    }
22718 ripley 1658
#endif
1659
#ifdef XK_Page_Down
34639 ripley 1660
    else if (iokey == XK_Page_Down) {
40759 ripley 1661
	if(DE->isEditor)
48544 ripley 1662
	    jumpwin(DE, DE->colmin, DE->rowmax);
40755 ripley 1663
	else {
40759 ripley 1664
	    int i = DE->ymaxused - DE->nhigh + 2;
51659 maechler 1665
            jumpwin(DE, DE->colmin, min(i, DE->rowmax));
40755 ripley 1666
	}
40759 ripley 1667
	cell_cursor_init(DE);
34639 ripley 1668
    }
22718 ripley 1669
#elif defined(XK_Next)
34639 ripley 1670
    else if (iokey == XK_Next) {
48544 ripley 1671
	if(DE->isEditor)
1672
	    jumpwin(DE, DE->colmin, DE->rowmax);
1673
	else {
1674
	    int i = DE->ymaxused - DE->nhigh + 2;
51659 maechler 1675
            jumpwin(DE, DE->colmin, min(i, DE->rowmax));
48544 ripley 1676
	}
40759 ripley 1677
	cell_cursor_init(DE);
34639 ripley 1678
    }
13974 hornik 1679
#endif
40759 ripley 1680
    else if (DE->isEditor && (iokey == XK_BackSpace || iokey == XK_Delete)) {
13974 hornik 1681
	if (clength > 0) {
34639 ripley 1682
	    int last_w ;
1683
	    last_w = last_wchar_bytes(NULL);
1684
	    clength -= last_w;
1685
	    bufp -= last_w;
1686
	    *bufp = '\0';
1687
	    CellModified = TRUE;
40759 ripley 1688
	    printstring(DE, buf, clength, DE->crow, DE->ccol, 1);
13974 hornik 1689
	} else bell();
1690
    }
1691
    else if (iokey == XK_Tab) {
40759 ripley 1692
	if(CheckShift(event)) advancerect(DE, LEFT);
1693
	else advancerect(DE, RIGHT);
13974 hornik 1694
    }
1695
    else if (iokey == XK_Home) {
40759 ripley 1696
	jumpwin(DE, 1, 1);
1697
	downlightrect(DE);
1698
	DE->crow = DE->ccol = 1;
1699
	highlightrect(DE);
1700
	cell_cursor_init(DE);
13974 hornik 1701
    }
1702
    else if (iokey == XK_End) {
40759 ripley 1703
	int i = DE->ymaxused - DE->nhigh + 2, j, w = 0 ;
1704
	/* Try to work out which cols we can fit in */
1705
	for(j = DE->xmaxused;j >= 0; j--) {
1706
	    w += BOXW(j);
1707
	    if(w > DE->fullwindowWidth) break;
1708
	}
44407 ripley 1709
	jumpwin(DE, min(1 + max(j, 0), DE->xmaxused), max(i, 1));
40759 ripley 1710
	downlightrect(DE);
1711
	DE->crow = DE->ymaxused - DE->rowmin + 1;
1712
	DE->ccol = DE->xmaxused - DE->colmin + 1;
1713
	highlightrect(DE);
1714
	cell_cursor_init(DE);
13974 hornik 1715
    }
1716
    else if (IsModifierKey(iokey)) {
1717
    }
40759 ripley 1718
    else if(DE->isEditor) {
1719
	handlechar(DE, text);
1720
    }
13974 hornik 1721
}
1722
 
1723
 
1724
static int WhichEvent(DEEvent ioevent)
1725
{
1726
    return ioevent.type;
1727
}
1728
 
1729
static KeySym GetKey(DEEvent * event)
1730
{
1731
    char text[1];
1732
    KeySym iokey;
1733
 
31990 ripley 1734
    XLookupString((XKeyEvent *)event, text, 1, &iokey, NULL);
13974 hornik 1735
    return iokey;
1736
}
1737
 
32432 ripley 1738
static char *GetCharP(DEEvent * event)
13974 hornik 1739
{
34639 ripley 1740
    static char text[BOOSTED_BUF_SIZE];
13974 hornik 1741
    KeySym iokey;
1742
 
34639 ripley 1743
    memset(text,0,sizeof(text));
1744
 
32677 ripley 1745
    if(mbcslocale) {
32471 ripley 1746
#ifdef HAVE_XUTF8LOOKUPSTRING
45475 ripley 1747
	if(utf8locale)
34639 ripley 1748
	    Xutf8LookupString(ioic, (XKeyEvent *)event,
1749
			      text, sizeof(text) - clength,
1750
			      &iokey, &status);
1751
	else
32471 ripley 1752
#endif
34639 ripley 1753
	    XmbLookupString(ioic, (XKeyEvent *)event,
1754
			    text, sizeof(text) - clength,
1755
			    &iokey, &status);
32432 ripley 1756
	/* FIXME check the return code */
34639 ripley 1757
	if(status == XBufferOverflow)
1758
	    warning("dataentry: expression too long");
32432 ripley 1759
    } else
34639 ripley 1760
	XLookupString((XKeyEvent *)event,
1761
		      text, sizeof(text) - clength,
1762
		      &iokey, NULL);
32432 ripley 1763
    return text;
13974 hornik 1764
}
1765
 
1766
static int CheckControl(DEEvent * event)
1767
{
1768
    return (*event).xkey.state & ControlMask;
1769
}
1770
 
1771
static int CheckShift(DEEvent * event)
1772
{
1773
    return (*event).xkey.state & ShiftMask;
1774
}
1775
 
40759 ripley 1776
static void doControl(DEstruct DE, DEEvent * event)
13974 hornik 1777
{
1778
    int i;
1779
    char text[1];
1780
    KeySym iokey;
1781
 
1782
    (*event).xkey.state = 0;
31990 ripley 1783
    XLookupString((XKeyEvent *)event, text, 1, &iokey, NULL);
13974 hornik 1784
    /* one row overlap when scrolling: top line <--> bottom line */
1785
    switch (text[0]) {
1786
	case 'b':
40759 ripley 1787
	    i = DE->rowmin - DE->nhigh + 2;
1788
	    jumpwin(DE, DE->colmin, max(1, i));
13974 hornik 1789
	    break;
1790
	case 'f':
40759 ripley 1791
	    jumpwin(DE, DE->colmin, DE->rowmax);
13974 hornik 1792
	    break;
1793
	case 'l':
40759 ripley 1794
	    closerect(DE);
1795
	    for (i = 1 ; i <= min(100, DE->xmaxused); i++)
1796
		DE->boxw[i] = get_col_width(DE, i);
1797
	    closerect(DE);
1798
	    drawwindow(DE);
13974 hornik 1799
	    break;
1800
    }
40759 ripley 1801
    cell_cursor_init(DE);
13974 hornik 1802
}
1803
 
1804
 
40759 ripley 1805
static void doConfigure(DEstruct DE, DEEvent * event)
13974 hornik 1806
{
40759 ripley 1807
    if ((DE->fullwindowWidth != (*event).xconfigure.width) ||
1808
	(DE->fullwindowHeight != (*event).xconfigure.height)) {
1809
	closerect(DE);
1810
	drawwindow(DE);
34639 ripley 1811
    }
13974 hornik 1812
}
1813
 
1814
static void RefreshKeyboardMapping(DEEvent * event)
1815
{
28840 ripley 1816
    XRefreshKeyboardMapping((XMappingEvent *)event);
13974 hornik 1817
}
1818
 
1819
/* Initialize/Close Windows */
1820
 
40759 ripley 1821
void closewin(DEstruct DE)
13974 hornik 1822
{
40759 ripley 1823
    XFreeGC(iodisplay, DE->iogc);
1824
    if(mbcslocale  && DE->isEditor) {
32518 ripley 1825
	XDestroyIC(ioic);
1826
	XCloseIM(ioim);
1827
    }
40759 ripley 1828
    XDestroyWindow(iodisplay, DE->iowindow);
41345 ripley 1829
    /* XCloseDisplay(iodisplay); */
46161 ripley 1830
    Rsync(DE);
13974 hornik 1831
}
1832
 
36044 ripley 1833
#define USE_Xt 1
1834
 
1835
#ifdef USE_Xt
1836
#include <X11/StringDefs.h>
1837
#include <X11/Intrinsic.h>
1838
#include <X11/Shell.h>
1839
typedef struct gx_device_X_s {
1840
    Pixel background, foreground, borderColor;
1841
    Dimension borderWidth;
1842
    String geometry;
1843
} gx_device_X;
1844
 
1845
/* (String) casts are here to suppress warnings about discarding `const' */
1846
#define RINIT(a,b,t,s,o,it,n)\
1847
  {(String)(a), (String)(b), (String)t, sizeof(s),\
1848
   XtOffsetOf(gx_device_X, o), (String)it, (n)}
1849
#define rpix(a,b,o,n)\
1850
  RINIT(a,b,XtRPixel,Pixel,o,XtRString,(XtPointer)(n))
1851
#define rdim(a,b,o,n)\
1852
  RINIT(a,b,XtRDimension,Dimension,o,XtRImmediate,(XtPointer)(n))
1853
#define rstr(a,b,o,n)\
1854
  RINIT(a,b,XtRString,String,o,XtRString,(char*)(n))
1855
 
1856
static XtResource x_resources[] = {
1857
    rpix(XtNforeground, XtCForeground, foreground, "XtDefaultForeground"),
1858
    rpix(XtNbackground, XtCBackground, background, "XtDefaultBackground"),
1859
    rstr(XtNgeometry, XtCGeometry, geometry, NULL),
1860
};
1861
 
1862
static const int x_resource_count = XtNumber(x_resources);
1863
static gx_device_X xdev;
1864
#endif
1865
 
59332 pd 1866
/* NB: Keep this in sync with similar handler in devX11.c */
13974 hornik 1867
static int R_X11Err(Display *dsp, XErrorEvent *event)
1868
{
1869
    char buff[1000];
59332 pd 1870
    /* for tcl/tk */
1871
    if (event->error_code == BadWindow) return 0;
1872
 
13974 hornik 1873
    XGetErrorText(dsp, event->error_code, buff, 1000);
59332 pd 1874
    warning(_("X11 protocol error: %s"), buff);
13974 hornik 1875
    return 0;
1876
}
1877
 
59332 pd 1878
 
67181 luke 1879
static int NORET R_X11IOErr(Display *dsp)
13974 hornik 1880
{
1881
    error("X11 fatal IO error: please save work and shut down R");
1882
}
1883
 
1884
/* set up the window, print the grid and column/row labels */
1885
 
41831 rgentlem 1886
static Rboolean initwin(DEstruct DE, const char *title) /* TRUE = Error */
13974 hornik 1887
{
40747 ripley 1888
    int i, twidth, w, minwidth, labdigs;
13974 hornik 1889
    int ioscreen;
1890
    unsigned long iowhite, ioblack;
1891
    char digits[] = "123456789.0";
72512 maechler 1892
    char             *font_name="9x15";
13974 hornik 1893
    Window root;
1894
    XEvent ioevent;
1895
    XSetWindowAttributes winattr;
1896
    XWindowAttributes attribs;
34639 ripley 1897
    XSizeHints *hint;
35415 ripley 1898
    unsigned long fevent=0UL;
34639 ripley 1899
    int j,k;
1900
    XVaNestedList   xva_nlist;
1901
    XPoint xpoint;
13974 hornik 1902
 
32424 ripley 1903
    strcpy(copycontents, "");
1904
 
34639 ripley 1905
    if (!XSupportsLocale ())
32424 ripley 1906
	warning("locale not supported by Xlib: some X ops will operate in C locale");
1907
    if (!XSetLocaleModifiers ("")) warning("X cannot set locale modifiers");
1908
 
40759 ripley 1909
    if(!iodisplay) {
1910
	if ((iodisplay = XOpenDisplay(NULL)) == NULL) {
1911
	    warning("unable to open display");
1912
	    return TRUE;
1913
	}
1914
	deContext = XUniqueContext();
1915
	XSetErrorHandler(R_X11Err);
1916
	XSetIOErrorHandler(R_X11IOErr);
32424 ripley 1917
    }
13974 hornik 1918
 
45475 ripley 1919
 
13974 hornik 1920
    /* Get Font Loaded if we can */
1921
 
32677 ripley 1922
    if(mbcslocale) {
32424 ripley 1923
	int  missing_charset_count;
1924
	char **missing_charset_list;
1925
	char *def_string;
34639 ripley 1926
	char opt_fontset_name[512];
1927
 
44345 ripley 1928
	/* options("X11fonts")[1] read font name */
54049 ripley 1929
	SEXP opt = GetOption1(install("X11fonts"));
44345 ripley 1930
	if(isString(opt)) {
1931
	    const char *s = CHAR(STRING_ELT(opt, 0));
1932
	    sprintf(opt_fontset_name, s, "medium", "r", 12);
1933
	} else strcpy(opt_fontset_name, fontset_name);
34639 ripley 1934
 
46169 ripley 1935
	if(font_set == NULL) {
1936
	    font_set = XCreateFontSet(iodisplay, opt_fontset_name,
1937
				      &missing_charset_list,
1938
				      &missing_charset_count, &def_string);
1939
	    if (missing_charset_count) XFreeStringList(missing_charset_list);
1940
	}
32424 ripley 1941
	if (font_set == NULL) {
44345 ripley 1942
	    warning("unable to create fontset %s", opt_fontset_name);
32424 ripley 1943
	    return TRUE; /* ERROR */
1944
	}
49591 ripley 1945
    } else {
40759 ripley 1946
	DE->font_info = XLoadQueryFont(iodisplay, font_name);
1947
	if (DE->font_info == NULL) {
65368 ripley 1948
	    warning("unable to load font %s", font_name);
32424 ripley 1949
	    return TRUE; /* ERROR */
1950
	}
1951
    }
13974 hornik 1952
 
1953
    /* find out how wide the input boxes should be and set up the
1954
       window size defaults */
1955
 
54049 ripley 1956
    DE->nboxchars = asInteger(GetOption1(install("de.cellwidth")));
40759 ripley 1957
    if (DE->nboxchars == NA_INTEGER || DE->nboxchars < 0) DE->nboxchars = 0;
13974 hornik 1958
 
59764 ripley 1959
    twidth = textwidth(DE, digits, (int) strlen(digits));
34639 ripley 1960
 
40759 ripley 1961
    if (DE->nboxchars > 0) twidth = (twidth * DE->nboxchars)/10;
1962
    DE->box_w = twidth + 4;
32677 ripley 1963
    if(mbcslocale) {
32424 ripley 1964
	XFontSetExtents *extent = XExtentsOfFontSet(font_set);
1965
	char **ml;
40759 ripley 1966
	DE->box_h = (extent->max_logical_extent.height)
32424 ripley 1967
	    + (extent->max_logical_extent.height / 5) + 4;
1968
	font_set_cnt = XFontsOfFontSet(font_set, &fs_list, &ml);
40759 ripley 1969
	DE->text_offset = 2 + fs_list[0]->max_bounds.descent;
49591 ripley 1970
    } else {
40759 ripley 1971
	DE->box_h = DE->font_info->max_bounds.ascent
1972
	    + DE->font_info->max_bounds.descent + 4;
1973
	DE->text_offset = 2 + DE->font_info->max_bounds.descent;
32424 ripley 1974
    }
40759 ripley 1975
    DE->windowHeight = 26 * DE->box_h + DE->hht + 2;
24271 ripley 1976
    /* this used to presume 4 chars sufficed for row numbering */
59764 ripley 1977
    labdigs = max(3, 1+ (int) floor(log10((double)DE->ymaxused)));
40759 ripley 1978
    sprintf(DE->labform, "%%%dd", labdigs);
59764 ripley 1979
    DE->boxw[0] = (int)( 0.1*labdigs*textwidth(DE, "0123456789", 10)) +
40759 ripley 1980
	textwidth(DE, " ", 1) + 8;
1981
    for(i = 1; i < 100; i++) DE->boxw[i] = get_col_width(DE, i);
34639 ripley 1982
 
13974 hornik 1983
    /* try for a window width that covers all the columns, or is around
1984
       800 pixels */
40759 ripley 1985
    w = DE->windowWidth = 0;
1986
    for(i = 0; i <= DE->xmaxused; i++) {
1987
	w += DE->boxw[i];
13974 hornik 1988
	if(w > 800) {
40759 ripley 1989
	    DE->windowWidth = w - DE->boxw[i];
13974 hornik 1990
	    break;
1991
	}
1992
    }
40759 ripley 1993
    if(DE->windowWidth == 0) DE->windowWidth = w;
1994
    DE->windowWidth += 2;
13974 hornik 1995
    /* allow enough width for buttons */
62295 ripley 1996
    minwidth = (int)(7.5 * textwidth(DE, "Paste", 5));
40759 ripley 1997
    if(DE->windowWidth < minwidth) DE->windowWidth = minwidth;
13974 hornik 1998
 
1999
    ioscreen = DefaultScreen(iodisplay);
2000
    iowhite = WhitePixel(iodisplay, ioscreen);
2001
    ioblack = BlackPixel(iodisplay, ioscreen);
2002
 
2003
 
36044 ripley 2004
    hint = XAllocSizeHints();
34639 ripley 2005
 
2006
    hint->x = 0;
2007
    hint->y = 0;
40759 ripley 2008
    hint->width = DE->windowWidth;
2009
    hint->height = DE->windowHeight;
34639 ripley 2010
    hint->flags = PPosition | PSize;
2011
    /*
2012
     * not necessary?
32518 ripley 2013
    hints.flags = InputHint;
2014
    hints.input = True;
34639 ripley 2015
    */
13974 hornik 2016
    root = DefaultRootWindow(iodisplay);
2017
 
36044 ripley 2018
#ifdef USE_Xt
2019
    {
2020
	XtAppContext app_con;
2021
	Widget toplevel;
2022
	Display *xtdpy;
45475 ripley 2023
	int zero = 0;
36044 ripley 2024
 
2025
	XtToolkitInitialize();
2026
	app_con = XtCreateApplicationContext();
2027
	/* XtAppSetFallbackResources(app_con, x_fallback_resources);*/
2028
	xtdpy = XtOpenDisplay(app_con, NULL, "r_dataentry", "R_dataentry",
2029
			      NULL, 0, &zero, NULL);
2030
	toplevel = XtAppCreateShell(NULL, "R_dataentry",
40759 ripley 2031
				    applicationShellWidgetClass,
36044 ripley 2032
				    xtdpy, NULL, 0);
2033
	XtGetApplicationResources(toplevel, (XtPointer) &xdev,
40759 ripley 2034
				  x_resources,
36044 ripley 2035
				  x_resource_count,
2036
				  NULL, 0);
2037
	XtDestroyWidget(toplevel);
2038
	XtCloseDisplay(xtdpy);
2039
	XtDestroyApplicationContext(app_con);
2040
	if (xdev.geometry != NULL) {
2041
	    char gstr[40];
2042
	    int bitmask;
40759 ripley 2043
 
36044 ripley 2044
	    sprintf(gstr, "%dx%d+%d+%d", hint->width,
2045
		    hint->height, hint->x, hint->y);
2046
	    bitmask = XWMGeometry(iodisplay, DefaultScreen(iodisplay),
40759 ripley 2047
				  xdev.geometry, gstr,
36044 ripley 2048
				  1,
2049
				  hint,
2050
				  &hint->x, &hint->y,
2051
				  &hint->width, &hint->height,
2052
				  &hint->win_gravity);
40759 ripley 2053
 
36044 ripley 2054
	    if (bitmask & (XValue | YValue))
2055
		hint->flags |= USPosition;
40759 ripley 2056
	    if (bitmask & (WidthValue | HeightValue))
36044 ripley 2057
		hint->flags |= USSize;
2058
	}
2059
	ioblack = xdev.foreground;
2060
	iowhite = xdev.background;
2061
    }
2062
#endif
40759 ripley 2063
    if ((DE->iowindow = XCreateSimpleWindow(
32518 ripley 2064
	     iodisplay,
2065
	     root,
34639 ripley 2066
	     hint->x,
2067
	     hint->y,
2068
	     hint->width,
2069
	     hint->height,
40759 ripley 2070
	     DE->bwidth,
32518 ripley 2071
	     ioblack,
2072
	     iowhite)) == 0) {
32424 ripley 2073
	warning("unable to open window for data editor");
13974 hornik 2074
	return TRUE;
32424 ripley 2075
    }
13974 hornik 2076
 
34639 ripley 2077
    /*
40759 ripley 2078
    XSetStandardProperties(iodisplay, DE->iowindow, ioname, ioname, None,
34639 ripley 2079
			   (char **)NULL, 0, iohint);
2080
    */
40759 ripley 2081
    XSetWMNormalHints(iodisplay, DE->iowindow, hint);
34639 ripley 2082
    XFree(hint);
13974 hornik 2083
 
34639 ripley 2084
 
2085
    winattr.backing_store = WhenMapped;
40759 ripley 2086
    XChangeWindowAttributes(iodisplay, DE->iowindow, CWBackingStore,
2087
			    &winattr);
13974 hornik 2088
 
2089
    /* set up protocols so that window manager sends */
2090
    /* me an event when user "destroys" window */
40756 ripley 2091
    if(!_XA_WM_PROTOCOLS)
2092
	_XA_WM_PROTOCOLS = XInternAtom(iodisplay, "WM_PROTOCOLS", 0);
40759 ripley 2093
    DE->prot = XInternAtom(iodisplay, "WM_DELETE_WINDOW", 0);
2094
    XSetWMProtocols(iodisplay, DE->iowindow, &DE->prot, 1);
34639 ripley 2095
    /*
2096
     * not necessary
40759 ripley 2097
    XSetWMHints(iodisplay, DE->iowindow, &hints);
34639 ripley 2098
     */
2099
 
40759 ripley 2100
    DE->iogc = XCreateGC(iodisplay, DE->iowindow, 0, 0);
34639 ripley 2101
 
46169 ripley 2102
    if(mbcslocale && DE->isEditor) {
32518 ripley 2103
	ioim = XOpenIM(iodisplay, NULL, NULL, NULL);
32527 ripley 2104
	if(!ioim) {
40759 ripley 2105
	    XDestroyWindow(iodisplay, DE->iowindow);
32527 ripley 2106
	    XCloseDisplay(iodisplay);
34639 ripley 2107
	    warning("unable to open X Input Method");
2108
	    return TRUE;
32527 ripley 2109
	}
32555 ripley 2110
 
34639 ripley 2111
	/* search supported input style */
2112
	XGetIMValues(ioim, XNQueryInputStyle, &ioim_styles,NULL);
2113
	for(i = 0; i < ioim_styles->count_styles; i++) {
2114
	    for(j = 0; preedit_styles[j]; j++){
2115
		for(k = 0; status_styles[k]; k++){
2116
		    ioim_style = (preedit_styles[j] | status_styles[k]);
2117
		    if( ioim_styles->supported_styles[i] == ioim_style) {
2118
			goto loop_out;
2119
		    }
2120
		}
2121
	    }
2122
	}
2123
    loop_out:
2124
 
2125
	/* create input context */
2126
	xpoint.x = 0; xpoint.y=0;
2127
	xva_nlist = XVaCreateNestedList(0, XNFontSet, font_set,
2128
					XNSpotLocation, &xpoint, NULL);
2129
 
2130
	ioic = XCreateIC(ioim,
45475 ripley 2131
			 XNInputStyle, ioim_style,
40759 ripley 2132
			 XNClientWindow,DE->iowindow,
2133
			 XNFocusWindow,DE->iowindow,
34639 ripley 2134
			 XNPreeditAttributes, xva_nlist,
2135
			 XNStatusAttributes, xva_nlist,
32518 ripley 2136
			 NULL);
34639 ripley 2137
	XFree(xva_nlist);
32518 ripley 2138
	if(!ioic) {
2139
	    XCloseIM(ioim);
40759 ripley 2140
	    XDestroyWindow(iodisplay, DE->iowindow);
32518 ripley 2141
	    XCloseDisplay(iodisplay);
34639 ripley 2142
	    warning("unable to open X Input Context");
2143
	    return TRUE;
32518 ripley 2144
	}
34639 ripley 2145
 
2146
	/* get XIM processes event. */
2147
	XGetICValues(ioic, XNFilterEvents, &fevent, NULL);
32518 ripley 2148
    }
13974 hornik 2149
 
32677 ripley 2150
    if(!mbcslocale)
40759 ripley 2151
	XSetFont(iodisplay, DE->iogc, DE->font_info->fid);
49591 ripley 2152
 
40759 ripley 2153
    XSetBackground(iodisplay, DE->iogc, iowhite);
2154
    XSetForeground(iodisplay, DE->iogc, ioblack);
2155
    XSetLineAttributes(iodisplay, DE->iogc, 1, LineSolid, CapRound,
2156
		       JoinRound);
32555 ripley 2157
 
34639 ripley 2158
    /*
40759 ripley 2159
    XSelectInput(iodisplay, DE->iowindow,
32555 ripley 2160
		 ButtonPressMask | KeyPressMask
34639 ripley 2161
		 | ExposureMask | StructureNotifyMask | fevent);
2162
    */
2163
 
40759 ripley 2164
    XSelectInput(iodisplay, DE->iowindow,
34639 ripley 2165
		 ButtonPressMask
2166
		 | KeyPressMask
2167
		 | StructureNotifyMask
2168
		 | ExposureMask
2169
		 | EnterWindowMask
2170
		 | LeaveWindowMask
2171
		 | fevent);
40759 ripley 2172
    XMapRaised(iodisplay, DE->iowindow);
32555 ripley 2173
 
13974 hornik 2174
    /* now set up the menu-window, for now use the same text
2175
       dimensions as above */
2176
 
34639 ripley 2177
    /* font size consideration */
2178
    for(i = 0; i < (sizeof(menu_label)/sizeof(char *)); i++)
59764 ripley 2179
	twidth = (twidth<textwidth(DE, menu_label[i],(int) strlen(menu_label[i]))) ?
2180
	    textwidth(DE, menu_label[i],(int) strlen(menu_label[i])) : twidth;
34639 ripley 2181
 
13974 hornik 2182
    menuwindow = XCreateSimpleWindow(iodisplay, root, 0, 0, twidth,
40759 ripley 2183
				     4 * DE->box_h, 2, ioblack, iowhite);
13974 hornik 2184
    for (i = 0; i < 4; i++) {
2185
	menupanes[i] = XCreateSimpleWindow(iodisplay, menuwindow, 0,
40759 ripley 2186
					   DE->box_h * i, twidth, DE->box_h,
13974 hornik 2187
					   1, ioblack, iowhite);
2188
	XSelectInput(iodisplay, menupanes[i],
34639 ripley 2189
		     ButtonPressMask | ButtonReleaseMask | ExposureMask
2190
		     );
13974 hornik 2191
    }
2192
 
2193
    /* XMapSubwindows(iodisplay, menuwindow); */
2194
 
2195
 
40759 ripley 2196
    XStoreName(iodisplay, DE->iowindow, title);
13974 hornik 2197
    winattr.override_redirect = True;
2198
    XChangeWindowAttributes(iodisplay, menuwindow,
2199
			    CWBackingStore | CWOverrideRedirect, &winattr);
40759 ripley 2200
    Rsync(DE);
13974 hornik 2201
 
2202
    /* this next sequence makes sure the window is up and ready before
2203
       you start drawing in it */
2204
 
2205
    XNextEvent(iodisplay, &ioevent);
2206
    if (ioevent.xany.type == Expose) {
2207
	while (ioevent.xexpose.count)
2208
	    XNextEvent(iodisplay, &ioevent);
2209
    }
40759 ripley 2210
    XGetWindowAttributes(iodisplay, DE->iowindow, &attribs);
2211
    DE->bwidth = attribs.border_width;
2212
    DE->fullwindowWidth = attribs.width;
2213
    DE->fullwindowHeight = attribs.height;
13974 hornik 2214
 
2215
 
2216
    /* set the active rectangle to be the upper left one */
40759 ripley 2217
    DE->crow = 1;
2218
    DE->ccol = 1;
13974 hornik 2219
    CellModified = FALSE;
40759 ripley 2220
    XSaveContext(iodisplay, DE->iowindow, deContext, (caddr_t) DE);
13974 hornik 2221
    return FALSE;/* success */
2222
}
2223
 
2224
/* MAC/X11 BASICS */
2225
 
40759 ripley 2226
static void bell()
13974 hornik 2227
{
2228
    XBell(iodisplay, 20);
2229
}
2230
 
40759 ripley 2231
static void cleararea(DEstruct DE, int xpos, int ypos, int width, int height)
13974 hornik 2232
{
40759 ripley 2233
    XClearArea(iodisplay, DE->iowindow, xpos, ypos, width, height, 0);
13974 hornik 2234
}
2235
 
40759 ripley 2236
static void clearwindow(DEstruct DE)
13974 hornik 2237
{
40759 ripley 2238
    XClearWindow(iodisplay, DE->iowindow);
13974 hornik 2239
}
2240
 
40759 ripley 2241
static void copyarea(DEstruct DE, int src_x, int src_y, int dest_x, int dest_y)
13974 hornik 2242
{
2243
    int mx = max(src_x, dest_x), my = max(src_y, dest_y);
40759 ripley 2244
    XCopyArea(iodisplay, DE->iowindow, DE->iowindow, DE->iogc,
2245
	      src_x, src_y,
2246
	      DE->fullwindowWidth - mx, DE->fullwindowHeight - my,
13974 hornik 2247
	      dest_x, dest_y);
40759 ripley 2248
    Rsync(DE);
13974 hornik 2249
}
2250
 
40759 ripley 2251
static void copyH(DEstruct DE, int src_x, int dest_x, int width)
13974 hornik 2252
{
40759 ripley 2253
    XCopyArea(iodisplay, DE->iowindow, DE->iowindow, DE->iogc,
2254
	      src_x+DE->bwidth, DE->hht,
2255
	      width, DE->windowHeight+1, dest_x+DE->bwidth, DE->hht);
13974 hornik 2256
}
2257
 
40759 ripley 2258
static void drawrectangle(DEstruct DE,
2259
			  int xpos, int ypos, int width, int height,
13974 hornik 2260
			  int lwd, int fore)
2261
{
36044 ripley 2262
#ifdef USE_Xt
13974 hornik 2263
    if (fore == 0)
40759 ripley 2264
	XSetForeground(iodisplay, DE->iogc, xdev.background);
36044 ripley 2265
    else
40759 ripley 2266
	XSetForeground(iodisplay, DE->iogc, xdev.foreground);
36044 ripley 2267
#else
2268
    if (fore == 0)
40759 ripley 2269
	XSetForeground(iodisplay, DE->iogc,
2270
		       WhitePixel(iodisplay, DefaultScreen(iodisplay)));
13974 hornik 2271
    else
40759 ripley 2272
	XSetForeground(iodisplay, DE->iogc,
2273
		       BlackPixel(iodisplay, DefaultScreen(iodisplay)));
36044 ripley 2274
#endif
40759 ripley 2275
    XSetLineAttributes(iodisplay, DE->iogc, lwd, LineSolid,
2276
		       CapRound, JoinRound);
2277
    XDrawRectangle(iodisplay, DE->iowindow, DE->iogc, xpos, ypos,
2278
		   width, height);
13974 hornik 2279
}
2280
 
40759 ripley 2281
static void drawtext(DEstruct DE, int xpos, int ypos, char *text, int len)
13974 hornik 2282
{
32677 ripley 2283
    if(mbcslocale)
32471 ripley 2284
#ifdef HAVE_XUTF8DRAWIMAGESTRING
45475 ripley 2285
	if(utf8locale)
40759 ripley 2286
	    Xutf8DrawImageString(iodisplay, DE->iowindow, font_set,
2287
				 DE->iogc, xpos, ypos,text, len);
45475 ripley 2288
	else
32471 ripley 2289
#endif
40759 ripley 2290
	    XmbDrawImageString(iodisplay, DE->iowindow, font_set,
2291
			       DE->iogc, xpos, ypos,text, len);
32424 ripley 2292
    else
40759 ripley 2293
	XDrawImageString(iodisplay, DE->iowindow, DE->iogc,
2294
			 xpos, ypos, text, len);
2295
    Rsync(DE);
13974 hornik 2296
}
2297
 
40759 ripley 2298
static void Rsync(DEstruct DE)
13974 hornik 2299
{
2300
    XSync(iodisplay, 0);
2301
}
2302
 
41831 rgentlem 2303
static int textwidth(DEstruct DE, const char *text, int nchar)
13974 hornik 2304
{
41831 rgentlem 2305
    int ans;
2306
    char *buf = CallocCharBuf(nchar);
2307
    strncpy(buf, text, nchar);
35844 ripley 2308
    if(mbcslocale) {
32471 ripley 2309
#ifdef HAVE_XUTF8TEXTESCAPEMENT
45475 ripley 2310
	if (utf8locale)
41831 rgentlem 2311
	    ans = Xutf8TextEscapement(font_set, buf, nchar);
45475 ripley 2312
	else
32424 ripley 2313
#endif
41831 rgentlem 2314
	    ans = XmbTextEscapement(font_set, buf, nchar);
45475 ripley 2315
	Free(buf);
2316
	return ans;
35844 ripley 2317
    }
41831 rgentlem 2318
    ans = XTextWidth(DE->font_info, buf, nchar);
2319
    Free(buf);
2320
    return ans;
13974 hornik 2321
}
2322
 
2323
/* Menus */
2324
 
40759 ripley 2325
void popupmenu(DEstruct DE, int x_pos, int y_pos, int col, int row)
13974 hornik 2326
{
40759 ripley 2327
    int i, button, popupcol = col + DE->colmin - 1;
41831 rgentlem 2328
    const char *name;
2329
    char clab[20];
13974 hornik 2330
    XEvent event;
2331
    Window selected_pane;
2332
    SEXP tvec;
2333
 
34639 ripley 2334
    XMoveWindow(iodisplay, menuwindow, x_pos, y_pos);
13974 hornik 2335
    XMapSubwindows(iodisplay, menuwindow);
2336
    XMapRaised(iodisplay, menuwindow);
2337
 
2338
    /* now fill in the menu panes with the correct information */
2339
 
40759 ripley 2340
    if (popupcol > DE->xmaxused) {
24274 ripley 2341
	/* extend work, names and lens */
40759 ripley 2342
	REPROTECT(DE->work = lengthgets(DE->work, popupcol), DE->wpi);
2343
	REPROTECT(DE->names = lengthgets(DE->names, popupcol), DE->npi);
2344
	for (i = DE->xmaxused+1; i < popupcol; i++) {
24274 ripley 2345
	    sprintf(clab, "var%d", i + 1);
40759 ripley 2346
	    SET_STRING_ELT(DE->names, i, mkChar(clab));
24274 ripley 2347
	}
40759 ripley 2348
	REPROTECT(DE->lens = lengthgets(DE->lens, popupcol), DE->lpi);
2349
	DE->xmaxused = popupcol;
24274 ripley 2350
    }
40759 ripley 2351
    tvec = VECTOR_ELT(DE->work, popupcol - 1);
2352
    name = CHAR(STRING_ELT(DE->names, popupcol - 1));
34639 ripley 2353
    if(mbcslocale)
2354
#ifdef HAVE_XUTF8DRAWSTRING
45475 ripley 2355
	if(utf8locale)
34639 ripley 2356
	    Xutf8DrawString(iodisplay,
2357
			    menupanes[0],
40759 ripley 2358
			    font_set, DE->iogc, 3, DE->box_h - 3, name,
59764 ripley 2359
			    (int) strlen(name));
34639 ripley 2360
	else
2361
#endif
2362
	    XmbDrawString(iodisplay,
2363
			  menupanes[0],
40759 ripley 2364
			  font_set, DE->iogc, 3, DE->box_h - 3, name,
59764 ripley 2365
			  (int) strlen(name));
34639 ripley 2366
    else
45475 ripley 2367
	XDrawString(iodisplay,
40759 ripley 2368
		    menupanes[0], DE->iogc, 3, DE->box_h - 3, name,
59764 ripley 2369
		    (int) strlen(name));
13974 hornik 2370
    for (i = 1; i < 4; i++)
49591 ripley 2371
	if(mbcslocale)
34639 ripley 2372
#ifdef HAVE_XUTF8DRAWSTRING
49591 ripley 2373
	    if(utf8locale)
2374
		Xutf8DrawString(iodisplay,
2375
				menupanes[i],
2376
				font_set, DE->iogc, 3, DE->box_h - 3,
59764 ripley 2377
				menu_label[i - 1], (int) strlen(menu_label[i - 1]));
49591 ripley 2378
	    else
2379
#endif
2380
		XmbDrawString(iodisplay,
2381
			      menupanes[i],
2382
			      font_set, DE->iogc, 3, DE->box_h - 3,
59764 ripley 2383
			      menu_label[i - 1], (int) strlen(menu_label[i - 1]));
34639 ripley 2384
	else
49591 ripley 2385
	    XDrawString(iodisplay,
2386
			menupanes[i], DE->iogc, 3, DE->box_h - 3,
59764 ripley 2387
			menu_label[i - 1], (int) strlen(menu_label[i - 1]));
34639 ripley 2388
 
24274 ripley 2389
    if (isNull(tvec) || TYPEOF(tvec) == REALSXP)
49591 ripley 2390
	if(mbcslocale)
34639 ripley 2391
#ifdef HAVE_XUTF8DRAWSTRING
49591 ripley 2392
	    if(utf8locale)
2393
		Xutf8DrawString(iodisplay,
2394
				menupanes[1],
2395
				font_set, DE->iogc, 0, DE->box_h - 3,
2396
				"*", 1);
2397
	    else
2398
#endif
2399
		XmbDrawString(iodisplay,
2400
			      menupanes[1],
2401
			      font_set, DE->iogc, 0, DE->box_h - 3,
2402
			      "*", 1);
34639 ripley 2403
	else
49591 ripley 2404
	    XDrawString(iodisplay, menupanes[1], DE->iogc, 0, DE->box_h - 3,
34639 ripley 2405
			"*", 1);
13974 hornik 2406
    else
49591 ripley 2407
	if(mbcslocale)
34639 ripley 2408
#ifdef HAVE_XUTF8DRAWSTRING
49591 ripley 2409
	    if(utf8locale)
2410
		Xutf8DrawString(iodisplay,
2411
				menupanes[2],
2412
				font_set, DE->iogc, 0, DE->box_h - 3,
2413
				"*", 1);
2414
	    else
2415
#endif
2416
		XmbDrawString(iodisplay,
2417
			      menupanes[2],
2418
			      font_set, DE->iogc, 0, DE->box_h - 3,
2419
			      "*", 1);
34639 ripley 2420
	else
49591 ripley 2421
	    XDrawString(iodisplay, menupanes[2], DE->iogc, 0, DE->box_h - 3,
34639 ripley 2422
			"*", 1);
13974 hornik 2423
 
2424
/*
2425
  start an event loop; we're looking for a button press and a button
2426
  release in the same window
2427
*/
2428
 
2429
    while (1) {
2430
	XNextEvent(iodisplay, &event);
34639 ripley 2431
 
2432
	/* event is processed with input method */
2433
 
13974 hornik 2434
	if (event.type == ButtonPress) {
2435
	    button = event.xbutton.button;
2436
	    selected_pane = event.xbutton.window;
2437
	    for (i = 0; selected_pane != menupanes[i]; i++)
53324 ripley 2438
		if (i >= 3) goto done;
13974 hornik 2439
	    while (1) {
2440
		while (XCheckTypedEvent(iodisplay, ButtonPress, &event));
2441
		XMaskEvent(iodisplay, ButtonReleaseMask, &event);
2442
		if (event.xbutton.button == button)
2443
		    break;
2444
	    }
2445
	    if (selected_pane == event.xbutton.window) {
2446
		for (i = 0; selected_pane != menupanes[i]; i++);
2447
		switch (i) {
2448
		case 0:
2449
		    bell();
2450
		    break;
2451
		case 1:
32424 ripley 2452
		    if (isNull(tvec))
40759 ripley 2453
			SET_VECTOR_ELT(DE->work, popupcol - 1,
24274 ripley 2454
				       ssNewVector(REALSXP, 100));
2455
		    else
40759 ripley 2456
			SET_VECTOR_ELT(DE->work, popupcol - 1,
24274 ripley 2457
				       coerceVector(tvec, REALSXP));
13974 hornik 2458
		    goto done;
2459
		case 2:
32424 ripley 2460
		    if (isNull(tvec))
40759 ripley 2461
			SET_VECTOR_ELT(DE->work, popupcol - 1,
24274 ripley 2462
				       ssNewVector(STRSXP, 100));
40747 ripley 2463
		    else {
40759 ripley 2464
			SET_VECTOR_ELT(DE->work, popupcol - 1,
24274 ripley 2465
				       coerceVector(tvec, STRSXP));
40747 ripley 2466
		    }
40759 ripley 2467
 
13974 hornik 2468
		    goto done;
2469
		case 3:
40759 ripley 2470
		    closerect(DE);
2471
		    DE->ccol = col;
2472
		    DE->crow = 0;
2473
		    clearrect(DE);
13974 hornik 2474
		    goto done;
2475
		}
2476
	    }
2477
	}
45475 ripley 2478
	/* this doesn't work and perhaps I should move it up to the
2479
	   main control loop */
13974 hornik 2480
	else if (event.type == Expose) {
2481
	    if (event.xexpose.window == menuwindow) {
40759 ripley 2482
		XDrawString(iodisplay, menupanes[0], DE->iogc, 3,
59764 ripley 2483
			    DE->box_h - 3, name, (int) strlen(name));
13974 hornik 2484
		for (i = 1; i < 4; i++)
40759 ripley 2485
		    XDrawString(iodisplay, menupanes[i], DE->iogc, 3,
2486
				DE->box_h - 3,
59764 ripley 2487
				menu_label[i - 1], (int) strlen(menu_label[i - 1]));
13974 hornik 2488
	    }
2489
	}
2490
    }
49591 ripley 2491
done:
40759 ripley 2492
    popdownmenu(DE);
2493
    highlightrect(DE);
13974 hornik 2494
}
2495
 
40759 ripley 2496
void popdownmenu(DEstruct DE)
13974 hornik 2497
{
2498
    XUnmapWindow(iodisplay, menuwindow);
2499
    XUnmapSubwindows(iodisplay, menuwindow);
2500
}
2501
 
40759 ripley 2502
static void copycell(DEstruct DE)
13974 hornik 2503
{
34639 ripley 2504
  /*
2505
   * whichrow = crow + colmin - 1 => whichrow = crow + rowmin - 1
2506
   *                   ^^^                             ^^^
2507
   */
40759 ripley 2508
    int i, whichrow = DE->crow + DE->rowmin - 1,
2509
	whichcol = DE->ccol + DE->colmin -1;
13974 hornik 2510
    SEXP tmp;
32424 ripley 2511
 
13974 hornik 2512
    if (whichrow == 0) {
2513
	/* won't have  cell here */
2514
    } else {
2515
	strcpy(copycontents, "");
40759 ripley 2516
	if (length(DE->work) >= whichcol) {
2517
	    tmp = VECTOR_ELT(DE->work, whichcol - 1);
13974 hornik 2518
	    if (tmp != R_NilValue &&
28786 ripley 2519
		(i = whichrow - 1) < LENGTH(tmp) ) {
54049 ripley 2520
		PrintDefaults();
45475 ripley 2521
		if (TYPEOF(tmp) == REALSXP) {
34639 ripley 2522
			strncpy(copycontents, EncodeElement(tmp, i, 0, '.'),
2523
				BOOSTED_BUF_SIZE-1);
2524
			copycontents[BOOSTED_BUF_SIZE-1]='\0';
45475 ripley 2525
		} else if (TYPEOF(tmp) == STRSXP) {
2526
		    if (STRING_ELT(tmp, i) != ssNA_STRING) {
34639 ripley 2527
			strncpy(copycontents, EncodeElement(tmp, i, 0, '.'),
2528
				BOOSTED_BUF_SIZE-1);
2529
			copycontents[BOOSTED_BUF_SIZE-1]='\0';
2530
		    }
13974 hornik 2531
		}
2532
	    }
2533
	}
2534
    }
40759 ripley 2535
    highlightrect(DE);
13974 hornik 2536
}
2537
 
40759 ripley 2538
static void pastecell(DEstruct DE, int row, int col)
13974 hornik 2539
{
40759 ripley 2540
    downlightrect(DE);
2541
    DE->crow = row; DE->ccol = col;
13974 hornik 2542
    if (strlen(copycontents)) {
2543
	strcpy(buf, copycontents);
59764 ripley 2544
	clength = (int) strlen(copycontents);
13974 hornik 2545
	bufp = buf + clength;
2546
	CellModified = TRUE;
2547
    }
40759 ripley 2548
    closerect(DE);
2549
    highlightrect(DE);
13974 hornik 2550
}
34639 ripley 2551
 
40759 ripley 2552
static void calc_pre_edit_pos(DEstruct DE)
34639 ripley 2553
{
2554
    XVaNestedList   xva_nlist;
2555
    XPoint          xpoint;
2556
    int i;
2557
    int w;
2558
 
62314 ripley 2559
    xpoint.x = (short) DE->boxw[0];
40759 ripley 2560
    for (i = 1; i < DE->ccol; i++)
2561
	xpoint.x += BOXW(DE->colmin + i - 1);
34639 ripley 2562
#ifdef HAVE_XUTF8TEXTESCAPEMENT
2563
    if(utf8locale)
2564
	w = Xutf8TextEscapement(font_set, buf, clength);
2565
    else
2566
#endif
2567
	w = XmbTextEscapement(font_set, buf, clength);
40759 ripley 2568
    xpoint.x += (w > BOXW(DE->colmin + DE->ccol - 1)) ?
2569
	BOXW(DE->colmin + DE->ccol - 1) : w;
2570
    xpoint.x += DE->text_offset;
62314 ripley 2571
    xpoint.y = (short)(DE->hht + (DE->crow+1) * DE->box_h - DE->text_offset);
34639 ripley 2572
 
2573
    /*
2574
      <FIXME>
2575
      I may depend on implementation of XIM, but I do not obey,
2576
      setting value, and investigation in various implementation
2577
      system is need.
2578
      It is only a problem in an appearance.
2579
    */
2580
    xva_nlist = XVaCreateNestedList(0,
2581
				    XNSpotLocation, &xpoint,
2582
				    XNFontSet, font_set,
2583
				    NULL);
2584
    XSetICValues(ioic, XNPreeditAttributes, xva_nlist, NULL);
2585
 
2586
    XFree(xva_nlist);
2587
    return;
2588
}
2589
 
2590
/* last character bytes */
2591
static int last_wchar_bytes(char *str)
2592
{
2593
    wchar_t   wcs[BOOSTED_BUF_SIZE];
2594
    mbstate_t mb_st;
2595
    int cnt;
2596
    char last_mbs[8];
2597
    char *mbs;
39658 ripley 2598
    size_t bytes;
34639 ripley 2599
 
2600
    mbs = (str == NULL) ? buf : str;
2601
 
2602
    memset(wcs, 0 ,sizeof(wcs));
2603
    memset(&mb_st,0, sizeof(mbstate_t));
2604
 
62627 ripley 2605
    if((int)-1 == (cnt = (int)mbsrtowcs(wcs, (const char **)&mbs,
2606
					(int) strlen(mbs), &mb_st))) {
45475 ripley 2607
	return 0;
34639 ripley 2608
    }
2609
    if(wcs[0] == L'\0') return 0;
2610
 
2611
    memset(last_mbs, 0, sizeof(last_mbs));
40756 ripley 2612
    bytes = wcrtomb(last_mbs, wcs[cnt-1], &mb_st); /* -Wall */
62295 ripley 2613
    return (int) bytes;
34639 ripley 2614
}
60708 ripley 2615