The R Project SVN R

Rev

Rev 83454 | 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
87194 kalibera 4
 *  Copyright (C) 1998--2024  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);
87194 kalibera 161
static int textwidth(DEstruct, const char*);
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;
40754 ripley 309
    char *title = "R Data Editor";
40759 ripley 310
    destruct DE1;
311
    DEstruct DE = &DE1;
13974 hornik 312
 
313
    nprotect = 0;/* count the PROTECT()s */
40759 ripley 314
    PROTECT_WITH_INDEX(DE->work = duplicate(CAR(args)), &DE->wpi); nprotect++;
24274 ripley 315
    colmodes = CADR(args);
40759 ripley 316
    tnames = getAttrib(DE->work, R_NamesSymbol);
13974 hornik 317
 
40759 ripley 318
    if (TYPEOF(DE->work) != VECSXP || TYPEOF(colmodes) != VECSXP)
13974 hornik 319
	errorcall(call, "invalid argument");
320
 
24274 ripley 321
    /* initialize the constants */
13974 hornik 322
 
323
    bufp = buf;
324
    ne = 0;
325
    currentexp = 0;
326
    nneg = 0;
327
    ndecimal = 0;
328
    clength = 0;
40747 ripley 329
    inSpecial = 0;
40759 ripley 330
    DE->ccol = 1;
331
    DE->crow = 1;
332
    DE->colmin = 1;
333
    DE->rowmin = 1;
40747 ripley 334
    PROTECT(ssNA_STRING = duplicate(NA_STRING));
335
    nprotect++;
40759 ripley 336
    DE->bwidth = 5;
337
    DE->hht = 30;
338
    DE->isEditor = TRUE;
13974 hornik 339
 
24274 ripley 340
    /* setup work, names, lens  */
40759 ripley 341
    DE->xmaxused = length(DE->work); DE->ymaxused = 0;
342
    PROTECT_WITH_INDEX(DE->lens = allocVector(INTSXP, DE->xmaxused), &DE->lpi);
24274 ripley 343
    nprotect++;
13974 hornik 344
 
24274 ripley 345
    if (isNull(tnames)) {
40759 ripley 346
	PROTECT_WITH_INDEX(DE->names = allocVector(STRSXP, DE->xmaxused),
347
			   &DE->npi);
348
	for(i = 0; i < DE->xmaxused; i++) {
83436 ripley 349
	    char clab[25];
350
	    snprintf(clab, 25, "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;
78446 ripley 466
    // FIXME: this should be checked
40759 ripley 467
    DEstruct DE = (DEstruct) malloc(sizeof(destruct));
78446 ripley 468
    if(!DE) error("allocation failed in in_R_X11_dataviewer");
13974 hornik 469
 
40759 ripley 470
    nView++;
471
 
40754 ripley 472
    nprotect = 0;/* count the PROTECT()s */
40759 ripley 473
    DE->work = CAR(args);
474
    DE->names = getAttrib(DE->work, R_NamesSymbol);
40754 ripley 475
 
40759 ripley 476
    if (TYPEOF(DE->work) != VECSXP)
40754 ripley 477
	errorcall(call, "invalid argument");
478
    stitle = CADR(args);
479
    if (!isString(stitle) || LENGTH(stitle) != 1)
480
	errorcall(call, "invalid argument");
481
 
482
    /* initialize the constants */
483
 
484
    bufp = buf;
485
    ne = 0;
486
    currentexp = 0;
487
    nneg = 0;
488
    ndecimal = 0;
489
    clength = 0;
490
    inSpecial = 0;
40759 ripley 491
    DE->ccol = 1;
492
    DE->crow = 1;
493
    DE->colmin = 1;
494
    DE->rowmin = 1;
495
    DE->bwidth = 5;
496
    DE->hht = 10;
497
    DE->isEditor = FALSE;
40754 ripley 498
 
499
    /* setup work, names, lens  */
40759 ripley 500
    DE->xmaxused = length(DE->work); DE->ymaxused = 0;
501
    PROTECT_WITH_INDEX(DE->lens = allocVector(INTSXP, DE->xmaxused), &DE->lpi);
40754 ripley 502
    nprotect++;
503
 
40759 ripley 504
    for (i = 0; i < DE->xmaxused; i++) {
505
	int len = LENGTH(VECTOR_ELT(DE->work, i));
506
	INTEGER(DE->lens)[i] = len;
507
	DE->ymaxused = max(len, DE->ymaxused);
508
	type = TYPEOF(VECTOR_ELT(DE->work, i));
40756 ripley 509
	if (type != STRSXP && type != REALSXP)
510
	    errorcall(call, "invalid argument");
40754 ripley 511
    }
512
 
513
 
514
    /* start up the window, more initializing in here */
40759 ripley 515
    if (initwin(DE, CHAR(STRING_ELT(stitle, 0))))
65368 ripley 516
	errorcall(call, "unable to start data viewer");
40754 ripley 517
 
518
    /* set up a context which will close the window if there is an error */
519
    begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
520
		 R_NilValue, R_NilValue);
40759 ripley 521
    cntxt.cend = &dv_closewin_cend;
522
    cntxt.cenddata = (void *) DE;
40754 ripley 523
 
40759 ripley 524
    highlightrect(DE);
40754 ripley 525
 
40759 ripley 526
    cell_cursor_init(DE);
40754 ripley 527
 
40759 ripley 528
    if(fdView < 0) {
529
	fdView = ConnectionNumber(iodisplay);
530
	addInputHandler(R_InputHandlers, fdView,
531
			R_ProcessX11Events, XActivity);
532
    }
40754 ripley 533
 
40759 ripley 534
    drawwindow(DE);
40756 ripley 535
 
40759 ripley 536
    R_PreserveObject(DE->work); /* also preserves names */
537
    R_PreserveObject(DE->lens);
538
    UNPROTECT(nprotect);
40754 ripley 539
    return R_NilValue;
540
}
541
 
13974 hornik 542
/* Window Drawing Routines */
543
 
40759 ripley 544
static void setcellwidths(DEstruct DE)
13974 hornik 545
{
546
    int i, w, dw;
547
 
40759 ripley 548
    DE->windowWidth = w = 2*DE->bwidth + DE->boxw[0] + BOXW(DE->colmin);
549
    DE->nwide = 2;
13974 hornik 550
    for (i = 2; i < 100; i++) { /* 100 on-screen columns cannot occur */
40759 ripley 551
	dw = BOXW(i + DE->colmin - 1);
552
	if((w += dw) > DE->fullwindowWidth ||
553
	   (!DE->isEditor && i > DE->xmaxused - DE->colmin + 1)) {
554
	    DE->nwide = i;
555
	    DE->windowWidth = w - dw;
13974 hornik 556
	    break;
557
	}
558
    }
559
}
560
 
40759 ripley 561
static void drawwindow(DEstruct DE)
13974 hornik 562
{
563
    int i, st;
564
    XWindowAttributes attribs;
565
 
566
    /* if there is an active cell enter the data in it */
34639 ripley 567
    /*
568
     * case colname input Expose not use.
569
     * closerect();
570
     */
13974 hornik 571
 
572
    /* now set up the window with the new dimensions */
40759 ripley 573
    XGetWindowAttributes(iodisplay, DE->iowindow, &attribs);
574
    DE->bwidth = attribs.border_width;
575
    DE->fullwindowWidth = attribs.width;
576
    DE->fullwindowHeight = attribs.height;
577
    setcellwidths(DE);
578
    DE->nhigh = (DE->fullwindowHeight - 2 * DE->bwidth - DE->hht) / DE->box_h;
579
    DE->windowHeight = DE->nhigh * DE->box_h + 2 * DE->bwidth;
13974 hornik 580
 
40759 ripley 581
    clearwindow(DE);
13974 hornik 582
 
583
 
40759 ripley 584
    for (i = 1; i < DE->nhigh; i++)
585
	drawrectangle(DE, 0, DE->hht + i * DE->box_h, DE->boxw[0], DE->box_h,
586
		      1, 1);
13974 hornik 587
     /* so row 0 and col 0 are reserved for labels */
40759 ripley 588
    DE->colmax = DE->colmin + (DE->nwide - 2);
589
    DE->rowmax = DE->rowmin + (DE->nhigh - 2);
590
    printlabs(DE);
591
    for (i = DE->colmin; i <= DE->colmax; i++) drawcol(DE, i);
13974 hornik 592
 
40759 ripley 593
    if(DE->isEditor) {
40755 ripley 594
	/* draw the quit etc boxes */
13974 hornik 595
 
87194 kalibera 596
	i = textwidth(DE, "Quit");
40759 ripley 597
	box_coords[0] = st = DE->fullwindowWidth - 6 - DE->bwidth;
40755 ripley 598
	box_coords[1] = st - i;
40759 ripley 599
	drawrectangle(DE, st - i, 3, i + 4, DE->hht - 6, 1, 1);
600
	drawtext(DE, st + 2 - i, DE->hht - 7, "Quit", 4);
13974 hornik 601
 
40755 ripley 602
	box_coords[4] = st = st - 5*i;
87194 kalibera 603
	i = textwidth(DE, "Paste");
40755 ripley 604
	box_coords[5] = st - i;
40759 ripley 605
	drawrectangle(DE, st - i, 3, i + 4, DE->hht - 6, 1, 1);
606
	drawtext(DE, st + 2 - i, DE->hht - 7, "Paste", 5);
13974 hornik 607
 
40755 ripley 608
	box_coords[2] = st = st - 2*i;
87194 kalibera 609
	i = textwidth(DE, "Copy");
40755 ripley 610
	box_coords[3] = st - i;
40759 ripley 611
	drawrectangle(DE, st - i, 3, i + 4, DE->hht - 6, 1, 1);
612
	drawtext(DE, st + 2 - i, DE->hht - 7, "Copy", 4);
40755 ripley 613
    }
13974 hornik 614
 
40759 ripley 615
    highlightrect(DE);
13974 hornik 616
 
40759 ripley 617
    Rsync(DE);
618
 
13974 hornik 619
}
620
 
40759 ripley 621
static void doHscroll(DEstruct DE, int oldcol)
13974 hornik 622
{
623
    int i, dw;
40759 ripley 624
    int oldnwide = DE->nwide, oldwindowWidth = DE->windowWidth;
13974 hornik 625
 
626
    /* horizontal re-position */
40759 ripley 627
    setcellwidths(DE);
628
    DE->colmax = DE->colmin + (DE->nwide - 2);
629
    if (oldcol < DE->colmin) { /* drop oldcol...colmin - 1 */
630
	dw = DE->boxw[0];
631
	for (i = oldcol; i < DE->colmin; i++) dw += BOXW(i);
632
	copyH(DE, dw, DE->boxw[0], oldwindowWidth - dw + 1);
13974 hornik 633
	dw = oldwindowWidth - BOXW(oldcol) + 1;
40759 ripley 634
	cleararea(DE, dw, DE->hht, DE->fullwindowWidth-dw,
635
		  DE->fullwindowHeight);
13974 hornik 636
	/* oldnwide includes the row labels */
40759 ripley 637
	for (i = oldcol+oldnwide-1; i <= DE->colmax; i++) drawcol(DE, i);
13974 hornik 638
    } else {
639
	/* move one or more cols left */
40759 ripley 640
	dw = BOXW(DE->colmin);
641
	copyH(DE, DE->boxw[0], DE->boxw[0] + dw, DE->windowWidth - dw + 1);
642
	dw = DE->windowWidth + 1;
643
	cleararea(DE, dw, DE->hht, DE->fullwindowWidth-dw,
644
		  DE->fullwindowHeight);
645
	drawcol(DE, DE->colmin);
13974 hornik 646
    }
647
 
40759 ripley 648
    highlightrect(DE);
649
    cell_cursor_init(DE);
13974 hornik 650
 
40759 ripley 651
    Rsync(DE);
13974 hornik 652
}
653
 
654
/* find_coords finds the coordinates of the upper left corner of the
655
   given cell on the screen: row and col are on-screen coords */
656
 
40759 ripley 657
static void find_coords(DEstruct DE,
658
			int row, int col, int *xcoord, int *ycoord)
13974 hornik 659
{
660
    int i, w;
40759 ripley 661
    w = DE->bwidth;
662
    if (col > 0) w += DE->boxw[0];
663
    for(i = 1; i < col; i ++) w += BOXW(i + DE->colmin - 1);
13974 hornik 664
    *xcoord = w;
40759 ripley 665
    *ycoord = DE->bwidth + DE->hht + DE->box_h * row;
13974 hornik 666
}
667
 
668
/* draw the window with the top left box at column wcol and row wrow */
669
 
40759 ripley 670
static void jumpwin(DEstruct DE, int wcol, int wrow)
13974 hornik 671
{
672
    if (wcol < 0 || wrow < 0) {
673
	bell();
674
	return;
675
    }
40759 ripley 676
    closerect(DE);
677
    if (DE->colmin != wcol || DE->rowmin != wrow) {
678
	DE->colmin = wcol;
679
	DE->rowmin = wrow;
680
	closerect(DE);
681
	drawwindow(DE);
682
    } else highlightrect(DE);
13974 hornik 683
}
684
 
40759 ripley 685
static void advancerect(DEstruct DE, DE_DIRECTION which)
13974 hornik 686
{
687
 
688
    /* if we are in the header, changing a name then only down is
689
       allowed */
40759 ripley 690
    if (DE->crow < 1 && which != DOWN) {
13974 hornik 691
	bell();
692
	return;
693
    }
694
 
40759 ripley 695
    closerect(DE);
13974 hornik 696
 
697
    switch (which) {
698
    case UP:
40759 ripley 699
	if (DE->crow == 1) {
700
	    if (DE->rowmin == 1)
13974 hornik 701
		bell();
702
	    else
40759 ripley 703
		jumppage(DE, UP);
13974 hornik 704
	} else
40759 ripley 705
	    DE->crow--;
13974 hornik 706
	break;
707
    case DOWN:
40759 ripley 708
	if (!DE->isEditor && DE->crow+DE->rowmin > DE->ymaxused) {
40755 ripley 709
	    bell();
710
	    break;
711
	}
40759 ripley 712
	if (DE->crow == (DE->nhigh - 1))
713
	    jumppage(DE, DOWN);
13974 hornik 714
	else
40759 ripley 715
	    DE->crow++;
13974 hornik 716
	break;
717
    case RIGHT:
40759 ripley 718
	if (!DE->isEditor && DE->ccol+DE->colmin > DE->xmaxused) {
40755 ripley 719
	    bell();
720
	    break;
721
	}
40759 ripley 722
	if (DE->ccol == (DE->nwide - 1))
723
	    jumppage(DE, RIGHT);
13974 hornik 724
	else
40759 ripley 725
	    DE->ccol++;
13974 hornik 726
	break;
727
    case LEFT:
40759 ripley 728
	if (DE->ccol == 1) {
729
	    if (DE->colmin == 1)
13974 hornik 730
		bell();
731
	    else
40759 ripley 732
		jumppage(DE, LEFT);
13974 hornik 733
	} else
40759 ripley 734
	    DE->ccol--;
13974 hornik 735
	break;
736
    default:
737
	UNIMPLEMENTED("advancerect");
738
    }
739
 
40759 ripley 740
    highlightrect(DE);
34639 ripley 741
 
40759 ripley 742
    cell_cursor_init(DE);
13974 hornik 743
}
744
 
40759 ripley 745
static void cell_cursor_init(DEstruct DE)
34639 ripley 746
{
40759 ripley 747
    int i, whichrow = DE->crow + DE->rowmin - 1,
748
	whichcol = DE->ccol + DE->colmin -1;
34639 ripley 749
    SEXP tmp;
750
 
751
    memset(buf,0,sizeof(buf));
752
 
40759 ripley 753
    if (DE->crow == 0 ){
34639 ripley 754
	strncpy(buf,
40759 ripley 755
		get_col_name(DE, whichcol),
34639 ripley 756
		BOOSTED_BUF_SIZE-1);
757
    } else {
40759 ripley 758
	if (length(DE->work) >= whichcol) {
759
	    tmp = VECTOR_ELT(DE->work, whichcol - 1);
34639 ripley 760
	    if (tmp != R_NilValue &&
761
		(i = whichrow - 1) < LENGTH(tmp) ) {
54049 ripley 762
		PrintDefaults();
34639 ripley 763
		if (TYPEOF(tmp) == REALSXP) {
40747 ripley 764
		    strncpy(buf, EncodeElement(tmp, i, 0, '.'),
765
			    BOOSTED_BUF_SIZE-1);
34639 ripley 766
		} else if (TYPEOF(tmp) == STRSXP) {
40747 ripley 767
		    if (STRING_ELT(tmp, i) != ssNA_STRING)
34639 ripley 768
			strncpy(buf, EncodeElement(tmp, i, 0, '.'),
769
				BOOSTED_BUF_SIZE-1);
770
		}
771
	    }
772
	}
773
    }
774
    buf[BOOSTED_BUF_SIZE-1] = '\0';
59764 ripley 775
    clength = (int) strlen(buf);
34639 ripley 776
    bufp = buf + clength;
777
}
778
 
41831 rgentlem 779
static const char *get_col_name(DEstruct DE, int col)
13974 hornik 780
{
24274 ripley 781
    static char clab[25];
41831 rgentlem 782
    int nwrote;
40759 ripley 783
    if (col <= DE->xmaxused) {
24274 ripley 784
	/* don't use NA labels */
40759 ripley 785
	SEXP tmp = STRING_ELT(DE->names, col - 1);
24274 ripley 786
	if(tmp != NA_STRING) return(CHAR(tmp));
13974 hornik 787
    }
41831 rgentlem 788
    nwrote = snprintf(clab, 25, "var%d", col);
789
    if (nwrote >= 25)
45475 ripley 790
	error("get_col_name: column number too big to stringify");
41831 rgentlem 791
    return (const char *)clab;
13974 hornik 792
}
793
 
40759 ripley 794
static int get_col_width(DEstruct DE, int col)
13974 hornik 795
{
796
    int i, w = 0, w1;
41831 rgentlem 797
    const char *strp;
24274 ripley 798
    SEXP tmp, lab;
13974 hornik 799
 
40759 ripley 800
    if (DE->nboxchars > 0) return DE->box_w;
801
    if (col <= DE->xmaxused) {
802
	tmp = VECTOR_ELT(DE->work, col - 1);
803
	if (isNull(tmp)) return DE->box_w;
24274 ripley 804
	/* don't use NA labels */
40759 ripley 805
	lab = STRING_ELT(DE->names, col - 1);
24274 ripley 806
	if(lab != NA_STRING) strp = CHAR(lab); else strp = "var12";
54049 ripley 807
	PrintDefaults();
34639 ripley 808
 
87194 kalibera 809
	w = textwidth(DE, strp);
40759 ripley 810
	for (i = 0; i < INTEGER(DE->lens)[col - 1]; i++) {
34294 ripley 811
	    strp = EncodeElement(tmp, i, 0, '.');
87194 kalibera 812
	    w1 = textwidth(DE, strp);
13974 hornik 813
	    if (w1 > w) w = w1;
814
	}
59764 ripley 815
	if(w < 0.5*DE->box_w) w = (int) (0.5*DE->box_w);
816
	if(w < 0.8*DE->box_w) w+= (int) (0.1*DE->box_w);
13974 hornik 817
	if(w > 600) w = 600;
818
	return w+8;
819
    }
40759 ripley 820
    return DE->box_w;
13974 hornik 821
}
822
 
40759 ripley 823
static CellType get_col_type(DEstruct DE, int col)
13974 hornik 824
{
825
    SEXP tmp;
826
    CellType res = UNKNOWNN;
827
 
40759 ripley 828
    if (col <= DE->xmaxused) {
829
	tmp = VECTOR_ELT(DE->work, col - 1);
13974 hornik 830
	if(TYPEOF(tmp) == REALSXP) res = NUMERIC;
831
	if(TYPEOF(tmp) == STRSXP) res = CHARACTER;
832
    }
833
    return res;
834
}
835
 
836
 
837
/* whichcol is absolute col no, col is position on screen */
40759 ripley 838
static void drawcol(DEstruct DE, int whichcol)
13974 hornik 839
{
40759 ripley 840
    int i, src_x, src_y, len, col = whichcol - DE->colmin + 1,
841
	bw = BOXW(whichcol);
41831 rgentlem 842
    const char *clab;
13974 hornik 843
    SEXP tmp;
844
 
40759 ripley 845
    find_coords(DE, 0, col, &src_x, &src_y);
846
    cleararea(DE, src_x, src_y, bw, DE->windowHeight);
847
    for (i = 0; i < DE->nhigh; i++)
848
	drawrectangle(DE, src_x, DE->hht + i * DE->box_h, bw, DE->box_h, 1, 1);
13974 hornik 849
 
850
    /* now fill it in if it is active */
40759 ripley 851
    clab = get_col_name(DE, whichcol);
59764 ripley 852
    printstring(DE, clab, (int) strlen(clab), 0, col, 0);
13974 hornik 853
 
40759 ripley 854
   if (DE->xmaxused >= whichcol) {
855
	tmp = VECTOR_ELT(DE->work, whichcol - 1);
24274 ripley 856
	if (!isNull(tmp)) {
40759 ripley 857
	    len = min(DE->rowmax, INTEGER(DE->lens)[whichcol - 1]);
858
	    for (i = (DE->rowmin - 1); i < len; i++)
859
		printelt(DE, tmp, i, i - DE->rowmin + 2, col);
13974 hornik 860
	}
861
    }
40759 ripley 862
    Rsync(DE);
13974 hornik 863
}
864
 
865
 
866
/* whichrow is absolute row no */
40759 ripley 867
static void drawrow(DEstruct DE, int whichrow)
13974 hornik 868
{
40759 ripley 869
    int i, src_x, src_y, row = whichrow - DE->rowmin + 1, w;
13974 hornik 870
    char rlab[15];
871
    SEXP tvec;
872
 
40759 ripley 873
    find_coords(DE, row, 0, &src_x, &src_y);
874
    cleararea(DE, src_x, src_y, DE->windowWidth, DE->box_h);
875
    drawrectangle(DE, src_x, src_y, DE->boxw[0], DE->box_h, 1, 1);
13974 hornik 876
 
83436 ripley 877
    snprintf(rlab, 15, DE->labform, whichrow);
59764 ripley 878
    printstring(DE, rlab, (int)strlen(rlab), row, 0, 0);
13974 hornik 879
 
40759 ripley 880
    w = DE->bwidth + DE->boxw[0];
881
    for (i = DE->colmin; i <= DE->colmax; i++) {
882
	drawrectangle(DE, w, src_y, BOXW(i), DE->box_h, 1, 1);
13974 hornik 883
	w += BOXW(i);
884
    }
885
 
40759 ripley 886
    for (i = DE->colmin; i <= DE->colmax; i++) {
887
	if (i > DE->xmaxused) break;
888
	if (!isNull(tvec = VECTOR_ELT(DE->work, i - 1)))
889
	    if (whichrow <= INTEGER(DE->lens)[i - 1])
890
		printelt(DE, tvec, whichrow - 1, row, i - DE->colmin + 1);
13974 hornik 891
    }
892
 
40759 ripley 893
    Rsync(DE);
13974 hornik 894
}
895
 
896
/* printelt: print the correct value from vector[vrow] into the
897
   spreadsheet in row ssrow and col sscol */
898
 
899
/* WARNING: This has no check that you're not beyond the end of the
900
   vector. Caller must check. */
901
 
40759 ripley 902
static void printelt(DEstruct DE, SEXP invec, int vrow, int ssrow, int sscol)
13974 hornik 903
{
41861 ripley 904
    const char *strp;
54049 ripley 905
    PrintDefaults();
13974 hornik 906
    if (TYPEOF(invec) == REALSXP) {
40747 ripley 907
	strp = EncodeElement(invec, vrow, 0, '.');
59764 ripley 908
	printstring(DE ,strp, (int) strlen(strp), ssrow, sscol, 0);
13974 hornik 909
    }
910
    else if (TYPEOF(invec) == STRSXP) {
40747 ripley 911
	if (STRING_ELT(invec, vrow) != ssNA_STRING) {
34294 ripley 912
	    strp = EncodeElement(invec, vrow, 0, '.');
59764 ripley 913
	    printstring(DE ,strp, (int) strlen(strp), ssrow, sscol, 0);
13974 hornik 914
	}
915
    }
916
    else
917
	error("dataentry: internal memory error");
918
}
919
 
920
 
40759 ripley 921
static void drawelt(DEstruct DE, int whichrow, int whichcol)
13974 hornik 922
{
923
    int i;
41831 rgentlem 924
    const char *clab;
13974 hornik 925
    SEXP tmp;
926
 
927
    if (whichrow == 0) {
40759 ripley 928
	clab = get_col_name(DE, whichcol + DE->colmin - 1);
59764 ripley 929
	printstring(DE ,clab, (int) strlen(clab), 0, whichcol, 0);
13974 hornik 930
    } else {
40759 ripley 931
	if (DE->xmaxused >= whichcol + DE->colmin - 1) {
932
	    tmp = VECTOR_ELT(DE->work, whichcol + DE->colmin - 2);
933
	    if (!isNull(tmp) && (i = DE->rowmin + whichrow - 2) <
934
		INTEGER(DE->lens)[whichcol + DE->colmin - 2] )
935
		printelt(DE, tmp, i, whichrow, whichcol);
13974 hornik 936
	} else
40759 ripley 937
	    printstring(DE, "", 0, whichrow,  whichcol, 0);
13974 hornik 938
    }
939
 
40759 ripley 940
    Rsync(DE);
13974 hornik 941
}
942
 
40759 ripley 943
static void jumppage(DEstruct DE, DE_DIRECTION dir)
13974 hornik 944
{
945
    int i, w, oldcol, wcol;
946
 
947
    switch (dir) {
948
    case UP:
40759 ripley 949
	DE->rowmin--;
950
	DE->rowmax--;
951
	copyarea(DE, 0, DE->hht + DE->box_h, 0, DE->hht + 2 * DE->box_h);
952
	drawrow(DE, DE->rowmin);
13974 hornik 953
	break;
954
    case DOWN:
40759 ripley 955
	if (DE->rowmax >= 65535) return;
956
	DE->rowmin++;
957
	DE->rowmax++;
958
	copyarea(DE, 0, DE->hht + 2 * DE->box_h, 0, DE->hht + DE->box_h);
959
	drawrow(DE, DE->rowmax);
13974 hornik 960
	break;
961
    case LEFT:
40759 ripley 962
	DE->colmin--;
963
	doHscroll(DE, DE->colmin+1);
13974 hornik 964
	break;
965
    case RIGHT:
40759 ripley 966
	oldcol = DE->colmin;
967
	wcol = DE->colmin + DE->ccol + 1; /* column to be selected */
45475 ripley 968
	/* There may not be room to fit the next column in */
40759 ripley 969
	w = DE->fullwindowWidth - DE->boxw[0] - BOXW(DE->colmax + 1);
970
	for (i = DE->colmax; i >= oldcol; i--) {
13974 hornik 971
	    w -= BOXW(i);
972
	    if(w < 0) {
40759 ripley 973
		DE->colmin = i + 1;
13974 hornik 974
		break;
975
	    }
976
	}
40759 ripley 977
	DE->ccol = wcol - DE->colmin;
978
	doHscroll(DE, oldcol);
13974 hornik 979
	break;
980
    }
981
}
982
/* draw a rectangle, used to highlight/downlight the current box */
983
 
40759 ripley 984
static void printrect(DEstruct DE, int lwd, int fore)
13974 hornik 985
{
986
    int x, y;
40759 ripley 987
    find_coords(DE, DE->crow, DE->ccol, &x, &y);
988
    drawrectangle(DE, x + lwd - 1, y + lwd - 1,
989
		  BOXW(DE->ccol+DE->colmin-1) - lwd + 1,
990
		  DE->box_h - lwd + 1, lwd, fore);
991
    Rsync(DE);
13974 hornik 992
}
993
 
40759 ripley 994
static void downlightrect(DEstruct DE)
13974 hornik 995
{
40759 ripley 996
    printrect(DE, 2, 0);
997
    printrect(DE, 1, 1);
13974 hornik 998
}
999
 
40759 ripley 1000
static void highlightrect(DEstruct DE)
13974 hornik 1001
{
40759 ripley 1002
    printrect(DE, 2, 1);
13974 hornik 1003
}
1004
 
24274 ripley 1005
 
40759 ripley 1006
static Rboolean getccol(DEstruct DE)
13974 hornik 1007
{
1008
    SEXP tmp, tmp2;
1009
    int i, len, newlen, wcol, wrow;
1010
    SEXPTYPE type;
40747 ripley 1011
    Rboolean newcol = FALSE;
13974 hornik 1012
 
40759 ripley 1013
    wcol = DE->ccol + DE->colmin - 1;
1014
    wrow = DE->crow + DE->rowmin - 1;
1015
    if (wcol > DE->xmaxused) {
24274 ripley 1016
	/* extend work, names and lens */
40759 ripley 1017
	REPROTECT(DE->work = lengthgets(DE->work, wcol), DE->wpi);
1018
	REPROTECT(DE->names = lengthgets(DE->names, wcol), DE->npi);
1019
	for (i = DE->xmaxused; i < wcol; i++) {
83436 ripley 1020
	    char clab[25];
1021
	    snprintf(clab, 25, "var%d", i + 1);
40759 ripley 1022
	    SET_STRING_ELT(DE->names, i, mkChar(clab));
24274 ripley 1023
	}
40759 ripley 1024
	REPROTECT(DE->lens = lengthgets(DE->lens, wcol), DE->lpi);
1025
	DE->xmaxused = wcol;
24274 ripley 1026
    }
40759 ripley 1027
    if (isNull(VECTOR_ELT(DE->work, wcol - 1))) {
13974 hornik 1028
	newcol = TRUE;
40759 ripley 1029
	SET_VECTOR_ELT(DE->work, wcol - 1,
1030
		       ssNewVector(REALSXP, max(100, wrow)));
1031
	INTEGER(DE->lens)[wcol - 1] = 0;
13974 hornik 1032
    }
40759 ripley 1033
    if (!isVector(tmp = VECTOR_ELT(DE->work, wcol - 1)))
13974 hornik 1034
	error("internal type error in dataentry");
40759 ripley 1035
    len = INTEGER(DE->lens)[wcol - 1];
24274 ripley 1036
    type = TYPEOF(tmp);
13974 hornik 1037
    if (len < wrow) {
1038
	for (newlen = max(len * 2, 10) ; newlen < wrow ; newlen *= 2)
1039
	    ;
1040
	tmp2 = ssNewVector(type, newlen);
1041
	for (i = 0; i < len; i++)
1042
	    if (type == REALSXP)
24274 ripley 1043
		REAL(tmp2)[i] = REAL(tmp)[i];
13974 hornik 1044
	    else if (type == STRSXP)
24274 ripley 1045
		SET_STRING_ELT(tmp2, i, STRING_ELT(tmp, i));
13974 hornik 1046
	    else
1047
		error("internal type error in dataentry");
40759 ripley 1048
	SET_VECTOR_ELT(DE->work, wcol - 1, tmp2);
13974 hornik 1049
    }
40747 ripley 1050
    return newcol;
13974 hornik 1051
}
1052
 
46045 murdoch 1053
static SEXP processEscapes(SEXP x)
1054
{
1055
    SEXP newval, pattern, replacement, expr;
1056
    ParseStatus status;
51659 maechler 1057
 
46045 murdoch 1058
    /* We process escape sequences in a scalar string by escaping
1059
       unescaped quotes, then quoting the whole thing and parsing it.  This
1060
       is supposed to be equivalent to the R code
1061
 
1062
       newval <- gsub(perl=TRUE, "(?<!\\\\)((\\\\\\\\)*)\"", "\\1\\\\\"", x)
1063
       newval <- sub('(^.*$)', '"\1"', newval)
1064
       newval <- eval(parse(text=newval))
1065
 
1066
       We do it this way to avoid extracting the escape handling
1067
       code from the parser.  We need it in C code because this may be executed
1068
       numerous times from C in dataentry.c */
51659 maechler 1069
 
46045 murdoch 1070
    PROTECT( pattern = mkString("(?<!\\\\)((\\\\\\\\)*)\"") );
1071
    PROTECT( replacement = mkString("\\1\\\\\"") );
67099 luke 1072
    SEXP s_gsub = install("gsub");
1073
    PROTECT( expr = lang5(s_gsub, ScalarLogical(1), pattern, replacement, x) );
46045 murdoch 1074
    SET_TAG( CDR(expr), install("perl") );
1075
 
1076
    PROTECT( newval = eval(expr, R_BaseEnv) );
1077
    PROTECT( pattern = mkString("(^.*$)") );
1078
    PROTECT( replacement = mkString("\"\\1\"") );
1079
    PROTECT( expr = lang4(install("sub"), pattern, replacement, newval) );
1080
    PROTECT( newval = eval(expr, R_BaseEnv) );
1081
    PROTECT( expr = R_ParseVector( newval, 1, &status, R_NilValue) );
51659 maechler 1082
 
46045 murdoch 1083
    /* We only handle the first entry. If this were available more generally,
1084
       we'd probably want to loop over all of expr */
51659 maechler 1085
 
46045 murdoch 1086
    if (status == PARSE_OK && length(expr))
1087
	PROTECT( newval = eval(VECTOR_ELT(expr, 0), R_BaseEnv) );
1088
    else
1089
	PROTECT( newval = R_NilValue );  /* protect just so the count doesn't change */
1090
    UNPROTECT(10);
1091
    return newval;
1092
}
1093
 
13974 hornik 1094
/* close up the entry to a cell, put the value that has been entered
1095
   into the correct place and as the correct type */
1096
 
40759 ripley 1097
static void closerect(DEstruct DE)
13974 hornik 1098
{
24274 ripley 1099
    SEXP cvec;
40759 ripley 1100
    int i, wcol = DE->ccol + DE->colmin - 1,
1101
	wrow = DE->rowmin + DE->crow - 1, wrow0;
40747 ripley 1102
    Rboolean newcol;
13974 hornik 1103
 
1104
    *bufp = '\0';
1105
 
1106
    /* first check to see if anything has been entered */
1107
    if (CellModified) {
40759 ripley 1108
	if (DE->crow == 0) {
13974 hornik 1109
	    if (clength != 0) {
1110
		/* then we are entering a new column name */
40759 ripley 1111
		if (DE->xmaxused < wcol) {
24274 ripley 1112
		    /* extend work, names and lens */
40759 ripley 1113
		    REPROTECT(DE->work = lengthgets(DE->work, wcol), DE->wpi);
1114
		    REPROTECT(DE->names = lengthgets(DE->names, wcol),
1115
			      DE->npi);
1116
		    for (i = DE->xmaxused; i < wcol - 1; i++) {
83436 ripley 1117
			char clab[25];
1118
			snprintf(clab, 25, "var%d", i + 1);
40759 ripley 1119
			SET_STRING_ELT(DE->names, i, mkChar(clab));
24274 ripley 1120
		    }
40759 ripley 1121
		    REPROTECT(DE->lens = lengthgets(DE->lens, wcol), DE->lpi);
1122
		    DE->xmaxused = wcol;
24274 ripley 1123
		}
40759 ripley 1124
		SET_STRING_ELT(DE->names, wcol - 1, mkChar(buf));
59764 ripley 1125
		printstring(DE ,buf, (int) strlen(buf), 0, wcol, 0);
13974 hornik 1126
	    } else {
83436 ripley 1127
		snprintf(buf, BOOSTED_BUF_SIZE, "var%d", DE->ccol);
1128
		printstring(DE, buf, (int) strlen(buf), 0, wcol, 0);
13974 hornik 1129
	    }
1130
	} else {
40759 ripley 1131
	    newcol = getccol(DE);
1132
	    cvec = VECTOR_ELT(DE->work, wcol - 1);
1133
	    wrow0 = INTEGER(DE->lens)[wcol - 1];
1134
	    if (wrow > wrow0) INTEGER(DE->lens)[wcol - 1] = wrow;
1135
	    DE->ymaxused = max(DE->ymaxused, wrow);
24274 ripley 1136
	    if (clength != 0) {
1137
		/* do it this way to ensure NA, Inf, ...  can get set */
1138
		char *endp;
1139
		double new = R_strtod(buf, &endp);
1140
		Rboolean warn = !isBlankString(endp);
46045 murdoch 1141
		if (TYPEOF(cvec) == STRSXP) {
1142
		    SEXP newval;
1143
		    PROTECT( newval = mkString(buf) );
1144
		    PROTECT( newval = processEscapes(newval) );
1145
		    if (TYPEOF(newval) == STRSXP && length(newval) == 1)
1146
			SET_STRING_ELT(cvec, wrow - 1, STRING_ELT(newval, 0));
1147
		    else
1148
			warning("dataentry: parse error on string");
1149
		    UNPROTECT(2);
1150
		} else
24274 ripley 1151
		    REAL(cvec)[wrow - 1] = new;
40747 ripley 1152
		if (newcol && warn) {
24274 ripley 1153
		    /* change mode to character */
40747 ripley 1154
		    SEXP tmp = coerceVector(cvec, STRSXP);
67351 luke 1155
		    PROTECT(tmp);
40747 ripley 1156
		    SET_STRING_ELT(tmp, wrow - 1, mkChar(buf));
40759 ripley 1157
		    SET_VECTOR_ELT(DE->work, wcol - 1, tmp);
67351 luke 1158
		    UNPROTECT(1);
24274 ripley 1159
		}
1160
	    } else {
1161
		if (TYPEOF(cvec) == STRSXP)
1162
		    SET_STRING_ELT(cvec, wrow - 1, NA_STRING);
1163
		else
1164
		    REAL(cvec)[wrow - 1] = NA_REAL;
24255 ripley 1165
	    }
40759 ripley 1166
	    drawelt(DE, DE->crow, DE->ccol); /* to get the cell scrolling right */
1167
	    if(wrow > wrow0) drawcol(DE, wcol); /* to fill in NAs */
24255 ripley 1168
	}
13974 hornik 1169
    }
1170
    CellModified = FALSE;
1171
 
40759 ripley 1172
    downlightrect(DE);
13974 hornik 1173
 
1174
    ndecimal = 0;
1175
    nneg = 0;
1176
    ne = 0;
1177
    currentexp = 0;
1178
    clength = 0;
40747 ripley 1179
    inSpecial = 0;
13974 hornik 1180
    bufp = buf;
1181
}
1182
 
1183
/* print a null terminated string, check to see if it is longer than
1184
   the print area and print it, left adjusted if necessary; clear the
1185
   area of previous text; */
1186
 
1187
/* This version will only display 200 chars, but the maximum col width
1188
   will not allow that many */
41831 rgentlem 1189
static void printstring(DEstruct DE, const char *ibuf, int buflen, int row,
45475 ripley 1190
			int col, int left)
13974 hornik 1191
{
1192
    int i, x_pos, y_pos, bw, bufw;
34639 ripley 1193
    char pbuf[BOOSTED_BUF_SIZE];
1194
    int wcsbufw,j;
1195
    wchar_t wcspbuf[BOOSTED_BUF_SIZE], *wcspc = wcspbuf;
1196
    wchar_t wcs[BOOSTED_BUF_SIZE];
1197
    char    s[BOOSTED_BUF_SIZE];
1198
    wchar_t *w_p;
1199
    char    *p;
1200
    int cnt;
13974 hornik 1201
 
40759 ripley 1202
    find_coords(DE, row, col, &x_pos, &y_pos);
1203
    if (col == 0) bw = DE->boxw[0]; else bw = BOXW(col+DE->colmin-1);
1204
    cleararea(DE, x_pos + 2, y_pos + 2, bw - 3, DE->box_h - 3);
34639 ripley 1205
    bufw = (buflen > BOOSTED_BUF_SIZE-1) ? BOOSTED_BUF_SIZE-1 : buflen;
1206
    strncpy(pbuf, ibuf, bufw);
1207
    pbuf[bufw] = '\0';
1208
 
1209
    p = pbuf;
62295 ripley 1210
    wcsbufw = (int) mbsrtowcs(wcspbuf, (const char **)&p, bufw, NULL);
34639 ripley 1211
    wcspbuf[wcsbufw]=L'\0';
1212
    if(left) {
45475 ripley 1213
	for (i = wcsbufw; i > 1; i--) {
34639 ripley 1214
	    for(j=0;*(wcspc+j)!=L'\0';j++)wcs[j]=*(wcspc+j);
1215
	    wcs[j]=L'\0';
1216
	    w_p=wcs;
62295 ripley 1217
	    cnt = (int) wcsrtombs(s,(const wchar_t **)&w_p,sizeof(s)-1,NULL);
34639 ripley 1218
	    s[cnt]='\0';
87194 kalibera 1219
	    if (textwidth(DE, s) < (bw - DE->text_offset)) break;
45475 ripley 1220
	    *(++wcspc) = L'<';
1221
	}
34639 ripley 1222
    } else {
45475 ripley 1223
	for (i = wcsbufw; i > 1; i--) {
34639 ripley 1224
	    for(j=0;*(wcspc+j)!=L'\0';j++)wcs[j]=*(wcspc+j);
1225
	    wcs[j]=L'\0';
1226
	    w_p=wcs;
62314 ripley 1227
	    cnt = (int) wcsrtombs(s,(const wchar_t **)&w_p,sizeof(s)-1,NULL);
34639 ripley 1228
	    s[cnt]='\0';
87194 kalibera 1229
	    if (textwidth(DE, s) < (bw - DE->text_offset)) break;
45475 ripley 1230
	    *(wcspbuf + i - 2) = L'>';
1231
	    *(wcspbuf + i - 1) = L'\0';
1232
	}
34639 ripley 1233
    }
1234
    for(j=0;*(wcspc+j)!=L'\0';j++) wcs[j]=*(wcspc+j);
1235
    wcs[j]=L'\0';
1236
    w_p=wcs;
62295 ripley 1237
    cnt = (int) wcsrtombs(s,(const wchar_t **)&w_p,sizeof(s)-1,NULL);
34639 ripley 1238
 
40759 ripley 1239
    drawtext(DE, x_pos + DE->text_offset, y_pos + DE->box_h - DE->text_offset,
1240
	     s, cnt);
34639 ripley 1241
 
40759 ripley 1242
    Rsync(DE);
13974 hornik 1243
}
1244
 
40759 ripley 1245
static void clearrect(DEstruct DE)
13974 hornik 1246
{
1247
    int x_pos, y_pos;
1248
 
40759 ripley 1249
    find_coords(DE, DE->crow, DE->ccol, &x_pos, &y_pos);
1250
    cleararea(DE, x_pos, y_pos, BOXW(DE->ccol+DE->colmin-1), DE->box_h);
1251
    Rsync(DE);
13974 hornik 1252
}
1253
 
1254
/* handlechar has to be able to parse decimal numbers and strings,
1255
   depending on the current column type, only printing characters
1256
   should get this far */
1257
 
1258
/* --- Not true! E.g. ESC ends up in here... */
1259
 
60260 ripley 1260
#include <rlocale.h>
1261
 
34639 ripley 1262
/* <FIXME> This is not correct for stateful MBCSs, but that's hard to
32677 ripley 1263
   do as we get a char at a time */
40759 ripley 1264
static void handlechar(DEstruct DE, char *text)
13974 hornik 1265
{
44186 ripley 1266
    int c = text[0], j;
34639 ripley 1267
    wchar_t wcs[BOOSTED_BUF_SIZE];
13974 hornik 1268
 
34639 ripley 1269
    memset(wcs,0,sizeof(wcs));
1270
 
32432 ripley 1271
    if ( c == '\033' ) { /* ESC */
13974 hornik 1272
	CellModified = FALSE;
1273
	clength = 0;
45475 ripley 1274
	bufp = buf;
40759 ripley 1275
	drawelt(DE, DE->crow, DE->ccol);
1276
	cell_cursor_init(DE);
13974 hornik 1277
	return;
1278
    } else
1279
	CellModified = TRUE;
1280
 
1281
    if (clength == 0) {
1282
 
40759 ripley 1283
	if (DE->crow == 0)	                        /* variable name */
13974 hornik 1284
	    currentexp = 3;
1285
	else
40759 ripley 1286
	    switch(get_col_type(DE, DE->ccol + DE->colmin - 1)) {
13974 hornik 1287
	    case NUMERIC:
1288
		currentexp = 1;
1289
		break;
1290
	    default:
1291
		currentexp = 2;
1292
	    }
40759 ripley 1293
	clearrect(DE);
1294
	highlightrect(DE);
13974 hornik 1295
    }
1296
 
34639 ripley 1297
    /* NA number? */
40759 ripley 1298
    if (get_col_type(DE, DE->ccol + DE->colmin - 1) == NUMERIC) {
40754 ripley 1299
	/* input numeric for NA of buffer , suppress NA etc.*/
1300
	if(strcmp(buf, "NA") == 0 || strcmp(buf, "NaN") == 0 ||
1301
	   strcmp(buf, "Inf") == 0 || strcmp(buf, "-Inf") == 0) {
34639 ripley 1302
	    buf[0] = '\0';
1303
	    clength = 0;
1304
	    bufp = buf;
1305
	}
1306
    }
1307
 
32518 ripley 1308
    if (currentexp == 1) {	/* we are parsing a number */
49591 ripley 1309
	char *mbs = text;
62295 ripley 1310
	int i, cnt = (int)mbsrtowcs(wcs, (const char **)&mbs, (int) strlen(text)+1, NULL);
34639 ripley 1311
 
49591 ripley 1312
	for(i = 0; i < cnt; i++) {
1313
	    switch (wcs[i]) {
1314
	    case L'-':
1315
		if (nneg == 0) nneg++; else goto donehc;
40747 ripley 1316
	    break;
49591 ripley 1317
	    case L'.':
1318
		if (ndecimal == 0) ndecimal++; else goto donehc;
13974 hornik 1319
	    break;
49591 ripley 1320
	    case L'e':
1321
	    case L'E':
1322
		if (ne == 0) {
1323
		    nneg = ndecimal = 0;	/* might have decimal in exponent */
1324
		    ne++;
1325
		} else goto donehc;
13974 hornik 1326
	    break;
49591 ripley 1327
	    case L'N':
1328
		if(nneg) goto donehc;
1329
	    case L'I':
1330
		inSpecial++;
13974 hornik 1331
	    break;
49591 ripley 1332
	    default:
1333
		if (!inSpecial && !iswdigit(wcs[i])) goto donehc;
1334
		break;
1335
	    }
13974 hornik 1336
	}
32518 ripley 1337
    }
13974 hornik 1338
    if (currentexp == 3) {
40747 ripley 1339
	char *mbs = text;
62295 ripley 1340
	int i, cnt = (int) mbsrtowcs(wcs, (const char **)&mbs, (int) strlen(text)+1, NULL);
40747 ripley 1341
	for(i = 0; i < cnt; i++) {
1342
	    if (iswspace(wcs[i])) goto donehc;
40759 ripley 1343
	    if (clength == 0 && wcs[i] != L'.' && !iswalpha(wcs[i]))
1344
		goto donehc;
40747 ripley 1345
	    else if (wcs[i] != L'.' && !iswalnum(wcs[i])) goto donehc;
1346
	}
13974 hornik 1347
    }
1348
 
80402 kalibera 1349
    if (clength+strlen(text) > BOOSTED_BUF_SIZE - R_MB_CUR_MAX - 1) {
13974 hornik 1350
	warning("dataentry: expression too long");
1351
	goto donehc;
1352
    }
1353
 
42804 maechler 1354
    /* as originally written, this left an undefined byte at
1355
       the end of bufp, followed by a zero byte; luckily, the storage
1356
       pointed to by bufp had already been zeroed, so the undefined
1357
       byte was in fact zero.  */
1358
    strcpy(bufp, text);
59764 ripley 1359
    bufp += (j = (int) strlen(text));
42804 maechler 1360
    clength += j;
40759 ripley 1361
    printstring(DE, buf, clength, DE->crow, DE->ccol, 1);
13974 hornik 1362
    return;
1363
 
1364
 donehc:
1365
    bell();
1366
}
1367
 
40759 ripley 1368
static void printlabs(DEstruct DE)
13974 hornik 1369
{
41831 rgentlem 1370
    const char *p;
13974 hornik 1371
    int i;
1372
 
40759 ripley 1373
    for (i = DE->colmin; i <= DE->colmax; i++) {
1374
	p = get_col_name(DE, i);
59764 ripley 1375
	printstring(DE, p, (int) strlen(p), 0, i - DE->colmin + 1, 0);
13974 hornik 1376
    }
40759 ripley 1377
    for (i = DE->rowmin; i <= DE->rowmax; i++) {
83436 ripley 1378
	char clab[15];
1379
	snprintf(clab, 15, DE->labform, i);
59764 ripley 1380
	printstring(DE, clab, (int) strlen(clab), i - DE->rowmin + 1, 0, 0);
13974 hornik 1381
    }
1382
}
1383
 
45475 ripley 1384
	       /* ================ X11-specific ================ */
13974 hornik 1385
 
1386
/* find out whether the button click was in the quit box */
1387
static int checkquit(int xw)
1388
{
1389
    if (xw > box_coords[1] && xw < box_coords[0]) return 1;
1390
    if (xw > box_coords[3] && xw < box_coords[2]) return 2;
1391
    if (xw > box_coords[5] && xw < box_coords[4]) return 3;
1392
    return 0;
1393
}
1394
 
1395
/* when a buttonpress event happens find the square that is being
1396
   pointed to if the pointer is in the header we need to see if the
1397
   quit button was pressed and if so quit. This is done by having
32432 ripley 1398
   findcell return an int which is one if we should quit and zero
13974 hornik 1399
   otherwise */
1400
 
40759 ripley 1401
static int findcell(DEstruct DE)
13974 hornik 1402
{
1403
 
1404
    int xw, yw, xr, yr, wcol=0, wrow, i, w;
1405
    unsigned int keys;
1406
    Window root, child;
1407
 
40759 ripley 1408
    closerect(DE);
1409
    XQueryPointer(iodisplay, DE->iowindow, &root, &child,
13974 hornik 1410
		  &xr, &yr, &xw, &yw, &keys);
1411
 
1412
    if (keys & Button1Mask) { /* left click */
1413
 
1414
	/* check to see if the click was in the header */
1415
 
40759 ripley 1416
	if (yw < DE->hht + DE->bwidth) {
13974 hornik 1417
	    i =  checkquit(xw);
1418
	    if (i == 1) return 1;
40759 ripley 1419
	    else if (i == 2) copycell(DE);
1420
	    else if (i == 3) pastecell(DE, DE->crow, DE->ccol);
13974 hornik 1421
	    return 0;
1422
	}
1423
 
32424 ripley 1424
 
13974 hornik 1425
	/* see if it is in the row labels */
40759 ripley 1426
	if (xw < DE->bwidth + DE->boxw[0]) {
13974 hornik 1427
	    bell();
40759 ripley 1428
	    highlightrect(DE);
13974 hornik 1429
	    return 0;
1430
	}
1431
	/* translate to box coordinates */
40759 ripley 1432
	wrow = (yw - DE->bwidth - DE->hht) / DE->box_h;
1433
	w = DE->bwidth + DE->boxw[0];
1434
	for (i = 1; i <= DE->nwide; i++)
1435
	    if((w += BOXW(i+DE->colmin-1)) > xw) {
13974 hornik 1436
		wcol = i;
1437
		break;
1438
	    }
1439
 
1440
	/* next check to see if it is in the column labels */
1441
 
40759 ripley 1442
	if (yw < DE->hht + DE->bwidth + DE->box_h) {
1443
	    if (xw > DE->bwidth + DE->boxw[0])
1444
		popupmenu(DE, xr, yr, wcol, wrow);
13974 hornik 1445
	    else {
40759 ripley 1446
		highlightrect(DE);
13974 hornik 1447
		bell();
1448
	    }
40759 ripley 1449
	} else if (wrow > DE->nhigh - 1 || wcol > DE->nwide -1) {
13974 hornik 1450
		/* off the grid */
40759 ripley 1451
		highlightrect(DE);
13974 hornik 1452
		bell();
40759 ripley 1453
	} else if (wcol != DE->ccol || wrow != DE->crow) {
1454
	    DE->ccol = wcol;
1455
	    DE->crow = wrow;
13974 hornik 1456
	}
1457
    }
1458
    if (keys & Button2Mask) { /* Paste */
1459
	int row, col = 0;
32424 ripley 1460
 
40759 ripley 1461
	if (yw < DE->hht + DE->bwidth || xw < DE->bwidth + DE->boxw[0])
1462
	    return 0;
13974 hornik 1463
 
1464
	/* translate to box coordinates */
40759 ripley 1465
	row = (yw - DE->bwidth - DE->hht) / DE->box_h;
1466
	w = DE->bwidth + DE->boxw[0];
1467
	for (i = 1; i <= DE->nwide; i++)
1468
	    if ((w += BOXW(i+DE->colmin-1)) > xw) {
13974 hornik 1469
		col = i;
1470
		break;
1471
	    }
40759 ripley 1472
	pastecell(DE, row, col);
13974 hornik 1473
    }
40759 ripley 1474
    highlightrect(DE);
13974 hornik 1475
    return 0;
1476
}
1477
 
1478
 
1479
/* Event Loop Functions */
45475 ripley 1480
#define mouseDown	ButtonPress
40756 ripley 1481
#define keyDown		KeyPress
1482
#define activateEvt	MapNotify
1483
#define updateEvt	Expose
13974 hornik 1484
 
40759 ripley 1485
static void eventloop(DEstruct DE)
13974 hornik 1486
{
1487
    int done;
1488
    DEEvent ioevent;
46169 ripley 1489
    caddr_t temp;
13974 hornik 1490
 
1491
    done = 0;
1492
    while (done == 0) {
45475 ripley 1493
	XNextEvent(iodisplay, &ioevent);
46169 ripley 1494
	XFindContext(iodisplay, ioevent.xany.window, deContext, &temp);
1495
	if ((DEstruct) temp != DE) { /* so a View window */
1496
	    if (WhichEvent(ioevent) == Expose)
1497
		drawwindow((DEstruct) temp);
1498
	} else {
45475 ripley 1499
	    if (XFilterEvent(&ioevent, None)){
40754 ripley 1500
		if(ioic){
1501
		    XSetICFocus(ioic);
1502
		    if (ioim_style & XIMPreeditPosition)
40759 ripley 1503
			calc_pre_edit_pos(DE);
40754 ripley 1504
		}
1505
		continue;
34639 ripley 1506
	    }
1507
 
13974 hornik 1508
	    switch (WhichEvent(ioevent)) {
34639 ripley 1509
	    case keyDown:/* KeyPress */
40759 ripley 1510
		doSpreadKey(DE, 0, &ioevent);
34639 ripley 1511
		break;
45475 ripley 1512
	    case Expose:
40759 ripley 1513
		while(XCheckTypedEvent(iodisplay, Expose, &ioevent))
1514
		    ;
40754 ripley 1515
		/*
1516
		 * XIM on  - KeyPress - Expose
1517
		 * XIM off - KeyPress - KeyRelease
1518
		 * colname change XIM on mode. type Backspace.
1519
		 */
45475 ripley 1520
		if(DE->crow == 0){
40759 ripley 1521
		    drawwindow(DE);
1522
		    printstring(DE, buf, clength, DE->crow, DE->ccol, 1);
40747 ripley 1523
		} else {
40759 ripley 1524
		    closerect(DE);
1525
		    drawwindow(DE);
1526
		    cell_cursor_init(DE);
34639 ripley 1527
		}
1528
		break;
1529
	    case activateEvt:/* MapNotify */
45475 ripley 1530
		closerect(DE);
40759 ripley 1531
		drawwindow(DE);
1532
		cell_cursor_init(DE);
45475 ripley 1533
		break;
34639 ripley 1534
	    case mouseDown:/* ButtonPress */
40759 ripley 1535
		if(DE->isEditor) {
1536
		    done  = doMouseDown(DE, &ioevent);
1537
		    cell_cursor_init(DE);
40755 ripley 1538
		}
13974 hornik 1539
		break;
1540
	    case MappingNotify:
1541
		RefreshKeyboardMapping(&ioevent);
1542
		break;
1543
	    case ConfigureNotify:
40759 ripley 1544
		while(XCheckTypedEvent(iodisplay, ConfigureNotify, &ioevent))
1545
		    ;
1546
		doConfigure(DE, &ioevent);
1547
		cell_cursor_init(DE);
13974 hornik 1548
		break;
1549
	    case ClientMessage:
1550
		if(ioevent.xclient.message_type == _XA_WM_PROTOCOLS
40759 ripley 1551
		   && ioevent.xclient.data.l[0] == DE->prot) {
13974 hornik 1552
		    /* user clicked on `close' aka `destroy' */
40754 ripley 1553
		    done = 1;
13974 hornik 1554
		}
1555
		break;
1556
	    }
1557
	}
1558
    }
1559
}
1560
 
40756 ripley 1561
static void R_ProcessX11Events(void *data)
1562
{
40759 ripley 1563
    caddr_t temp;
1564
    DEstruct DE = NULL;
40756 ripley 1565
    DEEvent ioevent;
1566
    int done = 0;
1567
 
46169 ripley 1568
    while (nView && XPending(iodisplay)) {
45475 ripley 1569
	XNextEvent(iodisplay, &ioevent);
40759 ripley 1570
	XFindContext(iodisplay, ioevent.xany.window, deContext, &temp);
1571
	DE = (DEstruct) temp;
40756 ripley 1572
	switch (WhichEvent(ioevent)) {
1573
	case keyDown:/* KeyPress */
40759 ripley 1574
	    doSpreadKey(DE, 0, &ioevent);
40756 ripley 1575
	    break;
1576
	case Expose:
40759 ripley 1577
	    while(XCheckTypedEvent(iodisplay, Expose, &ioevent))
1578
		;
1579
	    drawwindow(DE);
40756 ripley 1580
	    break;
1581
	case MappingNotify:
1582
	    RefreshKeyboardMapping(&ioevent);
1583
	    break;
1584
	case ConfigureNotify:
40759 ripley 1585
	    while(XCheckTypedEvent(iodisplay, ConfigureNotify, &ioevent))
1586
		;
1587
	    doConfigure(DE, &ioevent);
1588
	    cell_cursor_init(DE);
40756 ripley 1589
	    break;
1590
	case activateEvt:/* MapNotify */
1591
	    break;
1592
	case ClientMessage:
1593
	    if(ioevent.xclient.message_type == _XA_WM_PROTOCOLS
40759 ripley 1594
	       && ioevent.xclient.data.l[0] == DE->prot) {
40756 ripley 1595
		/* user clicked on `close' aka `destroy' */
1596
		done = 1;
1597
	    }
1598
	    break;
1599
	}
1600
    }
1601
    if(done) {
40759 ripley 1602
	R_ReleaseObject(DE->lens);
1603
	R_ReleaseObject(DE->work);
1604
	closewin(DE);
1605
	free(DE);
1606
	nView--;
1607
	if(nView == 0) {
46169 ripley 1608
	    /* NB: this is removing the handler that is currently
1609
	       being used: only OK to free here in R > 2.8.0 */
40759 ripley 1610
	    removeInputHandler(&R_InputHandlers,
1611
			       getInputHandler(R_InputHandlers,fdView));
1612
	    fdView = -1;
46169 ripley 1613
	    if(font_set) {
1614
		XFreeFontSet(iodisplay, font_set);
1615
		font_set = NULL;
1616
	    }
40759 ripley 1617
	    XCloseDisplay(iodisplay);
1618
	    iodisplay = NULL;
1619
	}
45475 ripley 1620
 
40756 ripley 1621
    }
1622
}
1623
 
40759 ripley 1624
static int doMouseDown(DEstruct DE, DEEvent * event)
13974 hornik 1625
{
40759 ripley 1626
    return findcell(DE);
13974 hornik 1627
}
1628
 
40759 ripley 1629
static void doSpreadKey(DEstruct DE, int key, DEEvent * event)
13974 hornik 1630
{
1631
    KeySym iokey;
49591 ripley 1632
    char *text = "";
13974 hornik 1633
 
1634
    iokey = GetKey(event);
48544 ripley 1635
    if(DE->isEditor) text = GetCharP(event);
13974 hornik 1636
 
1637
    if (CheckControl(event))
40759 ripley 1638
	doControl(DE, event);
32424 ripley 1639
    else if ((iokey == XK_Return)  || (iokey == XK_KP_Enter) ||
13974 hornik 1640
	     (iokey == XK_Linefeed)|| (iokey == XK_Down))
40759 ripley 1641
	advancerect(DE, DOWN);
13974 hornik 1642
    else if (iokey == XK_Left)
40759 ripley 1643
	advancerect(DE, LEFT);
13974 hornik 1644
    else if (iokey == XK_Right)
40759 ripley 1645
	advancerect(DE, RIGHT);
13974 hornik 1646
    else if (iokey == XK_Up)
40759 ripley 1647
	advancerect(DE, UP);
22718 ripley 1648
#ifdef XK_Page_Up
1649
    else if (iokey == XK_Page_Up) {
40759 ripley 1650
	int i = DE->rowmin - DE->nhigh + 2;
1651
	jumpwin(DE, DE->colmin, max(1, i));
1652
	cell_cursor_init(DE);
13974 hornik 1653
    }
22718 ripley 1654
#elif defined(XK_Prior)
1655
    else if (iokey == XK_Prior) {
40759 ripley 1656
	int i = DE->rowmin - DE->nhigh + 2;
1657
	jumpwin(DE, DE->colmin, max(1, i));
1658
	cell_cursor_init(DE);
13974 hornik 1659
    }
22718 ripley 1660
#endif
1661
#ifdef XK_Page_Down
34639 ripley 1662
    else if (iokey == XK_Page_Down) {
40759 ripley 1663
	if(DE->isEditor)
48544 ripley 1664
	    jumpwin(DE, DE->colmin, DE->rowmax);
40755 ripley 1665
	else {
40759 ripley 1666
	    int i = DE->ymaxused - DE->nhigh + 2;
78044 kalibera 1667
            jumpwin(DE, DE->colmin, max(1, min(i, DE->rowmax)));
40755 ripley 1668
	}
40759 ripley 1669
	cell_cursor_init(DE);
34639 ripley 1670
    }
22718 ripley 1671
#elif defined(XK_Next)
34639 ripley 1672
    else if (iokey == XK_Next) {
48544 ripley 1673
	if(DE->isEditor)
1674
	    jumpwin(DE, DE->colmin, DE->rowmax);
1675
	else {
1676
	    int i = DE->ymaxused - DE->nhigh + 2;
78044 kalibera 1677
            jumpwin(DE, DE->colmin, max(1, min(i, DE->rowmax)));
48544 ripley 1678
	}
40759 ripley 1679
	cell_cursor_init(DE);
34639 ripley 1680
    }
13974 hornik 1681
#endif
40759 ripley 1682
    else if (DE->isEditor && (iokey == XK_BackSpace || iokey == XK_Delete)) {
13974 hornik 1683
	if (clength > 0) {
34639 ripley 1684
	    int last_w ;
1685
	    last_w = last_wchar_bytes(NULL);
1686
	    clength -= last_w;
1687
	    bufp -= last_w;
1688
	    *bufp = '\0';
1689
	    CellModified = TRUE;
40759 ripley 1690
	    printstring(DE, buf, clength, DE->crow, DE->ccol, 1);
13974 hornik 1691
	} else bell();
1692
    }
1693
    else if (iokey == XK_Tab) {
40759 ripley 1694
	if(CheckShift(event)) advancerect(DE, LEFT);
1695
	else advancerect(DE, RIGHT);
13974 hornik 1696
    }
1697
    else if (iokey == XK_Home) {
40759 ripley 1698
	jumpwin(DE, 1, 1);
1699
	downlightrect(DE);
1700
	DE->crow = DE->ccol = 1;
1701
	highlightrect(DE);
1702
	cell_cursor_init(DE);
13974 hornik 1703
    }
1704
    else if (iokey == XK_End) {
40759 ripley 1705
	int i = DE->ymaxused - DE->nhigh + 2, j, w = 0 ;
1706
	/* Try to work out which cols we can fit in */
1707
	for(j = DE->xmaxused;j >= 0; j--) {
1708
	    w += BOXW(j);
1709
	    if(w > DE->fullwindowWidth) break;
1710
	}
44407 ripley 1711
	jumpwin(DE, min(1 + max(j, 0), DE->xmaxused), max(i, 1));
40759 ripley 1712
	downlightrect(DE);
1713
	DE->crow = DE->ymaxused - DE->rowmin + 1;
1714
	DE->ccol = DE->xmaxused - DE->colmin + 1;
1715
	highlightrect(DE);
1716
	cell_cursor_init(DE);
13974 hornik 1717
    }
1718
    else if (IsModifierKey(iokey)) {
1719
    }
40759 ripley 1720
    else if(DE->isEditor) {
1721
	handlechar(DE, text);
1722
    }
13974 hornik 1723
}
1724
 
1725
 
1726
static int WhichEvent(DEEvent ioevent)
1727
{
1728
    return ioevent.type;
1729
}
1730
 
1731
static KeySym GetKey(DEEvent * event)
1732
{
1733
    char text[1];
1734
    KeySym iokey;
1735
 
31990 ripley 1736
    XLookupString((XKeyEvent *)event, text, 1, &iokey, NULL);
13974 hornik 1737
    return iokey;
1738
}
1739
 
32432 ripley 1740
static char *GetCharP(DEEvent * event)
13974 hornik 1741
{
34639 ripley 1742
    static char text[BOOSTED_BUF_SIZE];
13974 hornik 1743
    KeySym iokey;
1744
 
34639 ripley 1745
    memset(text,0,sizeof(text));
1746
 
32677 ripley 1747
    if(mbcslocale) {
32471 ripley 1748
#ifdef HAVE_XUTF8LOOKUPSTRING
45475 ripley 1749
	if(utf8locale)
34639 ripley 1750
	    Xutf8LookupString(ioic, (XKeyEvent *)event,
1751
			      text, sizeof(text) - clength,
1752
			      &iokey, &status);
1753
	else
32471 ripley 1754
#endif
34639 ripley 1755
	    XmbLookupString(ioic, (XKeyEvent *)event,
1756
			    text, sizeof(text) - clength,
1757
			    &iokey, &status);
32432 ripley 1758
	/* FIXME check the return code */
34639 ripley 1759
	if(status == XBufferOverflow)
1760
	    warning("dataentry: expression too long");
32432 ripley 1761
    } else
34639 ripley 1762
	XLookupString((XKeyEvent *)event,
1763
		      text, sizeof(text) - clength,
1764
		      &iokey, NULL);
32432 ripley 1765
    return text;
13974 hornik 1766
}
1767
 
1768
static int CheckControl(DEEvent * event)
1769
{
1770
    return (*event).xkey.state & ControlMask;
1771
}
1772
 
1773
static int CheckShift(DEEvent * event)
1774
{
1775
    return (*event).xkey.state & ShiftMask;
1776
}
1777
 
40759 ripley 1778
static void doControl(DEstruct DE, DEEvent * event)
13974 hornik 1779
{
1780
    int i;
1781
    char text[1];
1782
    KeySym iokey;
1783
 
1784
    (*event).xkey.state = 0;
31990 ripley 1785
    XLookupString((XKeyEvent *)event, text, 1, &iokey, NULL);
13974 hornik 1786
    /* one row overlap when scrolling: top line <--> bottom line */
1787
    switch (text[0]) {
1788
	case 'b':
40759 ripley 1789
	    i = DE->rowmin - DE->nhigh + 2;
1790
	    jumpwin(DE, DE->colmin, max(1, i));
13974 hornik 1791
	    break;
1792
	case 'f':
40759 ripley 1793
	    jumpwin(DE, DE->colmin, DE->rowmax);
13974 hornik 1794
	    break;
1795
	case 'l':
40759 ripley 1796
	    closerect(DE);
1797
	    for (i = 1 ; i <= min(100, DE->xmaxused); i++)
1798
		DE->boxw[i] = get_col_width(DE, i);
1799
	    closerect(DE);
1800
	    drawwindow(DE);
13974 hornik 1801
	    break;
1802
    }
40759 ripley 1803
    cell_cursor_init(DE);
13974 hornik 1804
}
1805
 
1806
 
40759 ripley 1807
static void doConfigure(DEstruct DE, DEEvent * event)
13974 hornik 1808
{
40759 ripley 1809
    if ((DE->fullwindowWidth != (*event).xconfigure.width) ||
1810
	(DE->fullwindowHeight != (*event).xconfigure.height)) {
1811
	closerect(DE);
1812
	drawwindow(DE);
34639 ripley 1813
    }
13974 hornik 1814
}
1815
 
1816
static void RefreshKeyboardMapping(DEEvent * event)
1817
{
28840 ripley 1818
    XRefreshKeyboardMapping((XMappingEvent *)event);
13974 hornik 1819
}
1820
 
1821
/* Initialize/Close Windows */
1822
 
40759 ripley 1823
void closewin(DEstruct DE)
13974 hornik 1824
{
40759 ripley 1825
    XFreeGC(iodisplay, DE->iogc);
1826
    if(mbcslocale  && DE->isEditor) {
32518 ripley 1827
	XDestroyIC(ioic);
1828
	XCloseIM(ioim);
1829
    }
40759 ripley 1830
    XDestroyWindow(iodisplay, DE->iowindow);
41345 ripley 1831
    /* XCloseDisplay(iodisplay); */
46161 ripley 1832
    Rsync(DE);
13974 hornik 1833
}
1834
 
36044 ripley 1835
#define USE_Xt 1
1836
 
1837
#ifdef USE_Xt
1838
#include <X11/StringDefs.h>
1839
#include <X11/Intrinsic.h>
1840
#include <X11/Shell.h>
1841
typedef struct gx_device_X_s {
1842
    Pixel background, foreground, borderColor;
1843
    Dimension borderWidth;
1844
    String geometry;
1845
} gx_device_X;
1846
 
1847
/* (String) casts are here to suppress warnings about discarding `const' */
1848
#define RINIT(a,b,t,s,o,it,n)\
1849
  {(String)(a), (String)(b), (String)t, sizeof(s),\
1850
   XtOffsetOf(gx_device_X, o), (String)it, (n)}
1851
#define rpix(a,b,o,n)\
1852
  RINIT(a,b,XtRPixel,Pixel,o,XtRString,(XtPointer)(n))
1853
#define rdim(a,b,o,n)\
1854
  RINIT(a,b,XtRDimension,Dimension,o,XtRImmediate,(XtPointer)(n))
1855
#define rstr(a,b,o,n)\
1856
  RINIT(a,b,XtRString,String,o,XtRString,(char*)(n))
1857
 
1858
static XtResource x_resources[] = {
1859
    rpix(XtNforeground, XtCForeground, foreground, "XtDefaultForeground"),
1860
    rpix(XtNbackground, XtCBackground, background, "XtDefaultBackground"),
1861
    rstr(XtNgeometry, XtCGeometry, geometry, NULL),
1862
};
1863
 
1864
static const int x_resource_count = XtNumber(x_resources);
1865
static gx_device_X xdev;
1866
#endif
1867
 
59332 pd 1868
/* NB: Keep this in sync with similar handler in devX11.c */
13974 hornik 1869
static int R_X11Err(Display *dsp, XErrorEvent *event)
1870
{
1871
    char buff[1000];
59332 pd 1872
    /* for tcl/tk */
1873
    if (event->error_code == BadWindow) return 0;
1874
 
13974 hornik 1875
    XGetErrorText(dsp, event->error_code, buff, 1000);
59332 pd 1876
    warning(_("X11 protocol error: %s"), buff);
13974 hornik 1877
    return 0;
1878
}
1879
 
59332 pd 1880
 
83454 ripley 1881
NORET static int R_X11IOErr(Display *dsp)
13974 hornik 1882
{
1883
    error("X11 fatal IO error: please save work and shut down R");
1884
}
1885
 
1886
/* set up the window, print the grid and column/row labels */
1887
 
41831 rgentlem 1888
static Rboolean initwin(DEstruct DE, const char *title) /* TRUE = Error */
13974 hornik 1889
{
40747 ripley 1890
    int i, twidth, w, minwidth, labdigs;
13974 hornik 1891
    int ioscreen;
1892
    unsigned long iowhite, ioblack;
1893
    char digits[] = "123456789.0";
72512 maechler 1894
    char             *font_name="9x15";
13974 hornik 1895
    Window root;
1896
    XEvent ioevent;
1897
    XSetWindowAttributes winattr;
1898
    XWindowAttributes attribs;
34639 ripley 1899
    XSizeHints *hint;
35415 ripley 1900
    unsigned long fevent=0UL;
34639 ripley 1901
    int j,k;
1902
    XVaNestedList   xva_nlist;
1903
    XPoint xpoint;
13974 hornik 1904
 
32424 ripley 1905
    strcpy(copycontents, "");
1906
 
34639 ripley 1907
    if (!XSupportsLocale ())
32424 ripley 1908
	warning("locale not supported by Xlib: some X ops will operate in C locale");
1909
    if (!XSetLocaleModifiers ("")) warning("X cannot set locale modifiers");
1910
 
40759 ripley 1911
    if(!iodisplay) {
1912
	if ((iodisplay = XOpenDisplay(NULL)) == NULL) {
1913
	    warning("unable to open display");
1914
	    return TRUE;
1915
	}
1916
	deContext = XUniqueContext();
1917
	XSetErrorHandler(R_X11Err);
1918
	XSetIOErrorHandler(R_X11IOErr);
32424 ripley 1919
    }
13974 hornik 1920
 
45475 ripley 1921
 
13974 hornik 1922
    /* Get Font Loaded if we can */
1923
 
32677 ripley 1924
    if(mbcslocale) {
32424 ripley 1925
	int  missing_charset_count;
1926
	char **missing_charset_list;
1927
	char *def_string;
34639 ripley 1928
	char opt_fontset_name[512];
1929
 
44345 ripley 1930
	/* options("X11fonts")[1] read font name */
54049 ripley 1931
	SEXP opt = GetOption1(install("X11fonts"));
44345 ripley 1932
	if(isString(opt)) {
1933
	    const char *s = CHAR(STRING_ELT(opt, 0));
83436 ripley 1934
	    snprintf(opt_fontset_name, 512, s, "medium", "r", 12);
44345 ripley 1935
	} else strcpy(opt_fontset_name, fontset_name);
34639 ripley 1936
 
46169 ripley 1937
	if(font_set == NULL) {
1938
	    font_set = XCreateFontSet(iodisplay, opt_fontset_name,
1939
				      &missing_charset_list,
1940
				      &missing_charset_count, &def_string);
1941
	    if (missing_charset_count) XFreeStringList(missing_charset_list);
1942
	}
32424 ripley 1943
	if (font_set == NULL) {
44345 ripley 1944
	    warning("unable to create fontset %s", opt_fontset_name);
32424 ripley 1945
	    return TRUE; /* ERROR */
1946
	}
49591 ripley 1947
    } else {
40759 ripley 1948
	DE->font_info = XLoadQueryFont(iodisplay, font_name);
1949
	if (DE->font_info == NULL) {
65368 ripley 1950
	    warning("unable to load font %s", font_name);
32424 ripley 1951
	    return TRUE; /* ERROR */
1952
	}
1953
    }
13974 hornik 1954
 
1955
    /* find out how wide the input boxes should be and set up the
1956
       window size defaults */
1957
 
54049 ripley 1958
    DE->nboxchars = asInteger(GetOption1(install("de.cellwidth")));
40759 ripley 1959
    if (DE->nboxchars == NA_INTEGER || DE->nboxchars < 0) DE->nboxchars = 0;
13974 hornik 1960
 
87194 kalibera 1961
    twidth = textwidth(DE, digits);
34639 ripley 1962
 
40759 ripley 1963
    if (DE->nboxchars > 0) twidth = (twidth * DE->nboxchars)/10;
1964
    DE->box_w = twidth + 4;
32677 ripley 1965
    if(mbcslocale) {
32424 ripley 1966
	XFontSetExtents *extent = XExtentsOfFontSet(font_set);
1967
	char **ml;
40759 ripley 1968
	DE->box_h = (extent->max_logical_extent.height)
32424 ripley 1969
	    + (extent->max_logical_extent.height / 5) + 4;
1970
	font_set_cnt = XFontsOfFontSet(font_set, &fs_list, &ml);
40759 ripley 1971
	DE->text_offset = 2 + fs_list[0]->max_bounds.descent;
49591 ripley 1972
    } else {
40759 ripley 1973
	DE->box_h = DE->font_info->max_bounds.ascent
1974
	    + DE->font_info->max_bounds.descent + 4;
1975
	DE->text_offset = 2 + DE->font_info->max_bounds.descent;
32424 ripley 1976
    }
40759 ripley 1977
    DE->windowHeight = 26 * DE->box_h + DE->hht + 2;
24271 ripley 1978
    /* this used to presume 4 chars sufficed for row numbering */
59764 ripley 1979
    labdigs = max(3, 1+ (int) floor(log10((double)DE->ymaxused)));
83436 ripley 1980
    snprintf(DE->labform, 15, "%%%dd", labdigs);
87194 kalibera 1981
    DE->boxw[0] = (int)( 0.1*labdigs*textwidth(DE, "0123456789")) +
1982
	textwidth(DE, " ") + 8;
40759 ripley 1983
    for(i = 1; i < 100; i++) DE->boxw[i] = get_col_width(DE, i);
34639 ripley 1984
 
13974 hornik 1985
    /* try for a window width that covers all the columns, or is around
1986
       800 pixels */
40759 ripley 1987
    w = DE->windowWidth = 0;
1988
    for(i = 0; i <= DE->xmaxused; i++) {
1989
	w += DE->boxw[i];
13974 hornik 1990
	if(w > 800) {
40759 ripley 1991
	    DE->windowWidth = w - DE->boxw[i];
13974 hornik 1992
	    break;
1993
	}
1994
    }
40759 ripley 1995
    if(DE->windowWidth == 0) DE->windowWidth = w;
1996
    DE->windowWidth += 2;
13974 hornik 1997
    /* allow enough width for buttons */
87194 kalibera 1998
    minwidth = (int)(7.5 * textwidth(DE, "Paste"));
40759 ripley 1999
    if(DE->windowWidth < minwidth) DE->windowWidth = minwidth;
13974 hornik 2000
 
2001
    ioscreen = DefaultScreen(iodisplay);
2002
    iowhite = WhitePixel(iodisplay, ioscreen);
2003
    ioblack = BlackPixel(iodisplay, ioscreen);
2004
 
2005
 
36044 ripley 2006
    hint = XAllocSizeHints();
34639 ripley 2007
 
2008
    hint->x = 0;
2009
    hint->y = 0;
40759 ripley 2010
    hint->width = DE->windowWidth;
2011
    hint->height = DE->windowHeight;
34639 ripley 2012
    hint->flags = PPosition | PSize;
2013
    /*
2014
     * not necessary?
32518 ripley 2015
    hints.flags = InputHint;
2016
    hints.input = True;
34639 ripley 2017
    */
13974 hornik 2018
    root = DefaultRootWindow(iodisplay);
2019
 
36044 ripley 2020
#ifdef USE_Xt
2021
    {
2022
	XtAppContext app_con;
2023
	Widget toplevel;
2024
	Display *xtdpy;
45475 ripley 2025
	int zero = 0;
36044 ripley 2026
 
2027
	XtToolkitInitialize();
2028
	app_con = XtCreateApplicationContext();
2029
	/* XtAppSetFallbackResources(app_con, x_fallback_resources);*/
2030
	xtdpy = XtOpenDisplay(app_con, NULL, "r_dataentry", "R_dataentry",
2031
			      NULL, 0, &zero, NULL);
2032
	toplevel = XtAppCreateShell(NULL, "R_dataentry",
40759 ripley 2033
				    applicationShellWidgetClass,
36044 ripley 2034
				    xtdpy, NULL, 0);
2035
	XtGetApplicationResources(toplevel, (XtPointer) &xdev,
40759 ripley 2036
				  x_resources,
36044 ripley 2037
				  x_resource_count,
2038
				  NULL, 0);
2039
	XtDestroyWidget(toplevel);
2040
	XtCloseDisplay(xtdpy);
2041
	XtDestroyApplicationContext(app_con);
2042
	if (xdev.geometry != NULL) {
2043
	    char gstr[40];
2044
	    int bitmask;
40759 ripley 2045
 
83436 ripley 2046
	    snprintf(gstr, 40, "%dx%d+%d+%d", hint->width,
2047
		     hint->height, hint->x, hint->y);
36044 ripley 2048
	    bitmask = XWMGeometry(iodisplay, DefaultScreen(iodisplay),
40759 ripley 2049
				  xdev.geometry, gstr,
36044 ripley 2050
				  1,
2051
				  hint,
2052
				  &hint->x, &hint->y,
2053
				  &hint->width, &hint->height,
2054
				  &hint->win_gravity);
40759 ripley 2055
 
36044 ripley 2056
	    if (bitmask & (XValue | YValue))
2057
		hint->flags |= USPosition;
40759 ripley 2058
	    if (bitmask & (WidthValue | HeightValue))
36044 ripley 2059
		hint->flags |= USSize;
2060
	}
2061
	ioblack = xdev.foreground;
2062
	iowhite = xdev.background;
2063
    }
2064
#endif
40759 ripley 2065
    if ((DE->iowindow = XCreateSimpleWindow(
32518 ripley 2066
	     iodisplay,
2067
	     root,
34639 ripley 2068
	     hint->x,
2069
	     hint->y,
2070
	     hint->width,
2071
	     hint->height,
40759 ripley 2072
	     DE->bwidth,
32518 ripley 2073
	     ioblack,
2074
	     iowhite)) == 0) {
32424 ripley 2075
	warning("unable to open window for data editor");
13974 hornik 2076
	return TRUE;
32424 ripley 2077
    }
13974 hornik 2078
 
34639 ripley 2079
    /*
40759 ripley 2080
    XSetStandardProperties(iodisplay, DE->iowindow, ioname, ioname, None,
34639 ripley 2081
			   (char **)NULL, 0, iohint);
2082
    */
40759 ripley 2083
    XSetWMNormalHints(iodisplay, DE->iowindow, hint);
34639 ripley 2084
    XFree(hint);
13974 hornik 2085
 
34639 ripley 2086
 
2087
    winattr.backing_store = WhenMapped;
40759 ripley 2088
    XChangeWindowAttributes(iodisplay, DE->iowindow, CWBackingStore,
2089
			    &winattr);
13974 hornik 2090
 
2091
    /* set up protocols so that window manager sends */
2092
    /* me an event when user "destroys" window */
40756 ripley 2093
    if(!_XA_WM_PROTOCOLS)
2094
	_XA_WM_PROTOCOLS = XInternAtom(iodisplay, "WM_PROTOCOLS", 0);
40759 ripley 2095
    DE->prot = XInternAtom(iodisplay, "WM_DELETE_WINDOW", 0);
2096
    XSetWMProtocols(iodisplay, DE->iowindow, &DE->prot, 1);
34639 ripley 2097
    /*
2098
     * not necessary
40759 ripley 2099
    XSetWMHints(iodisplay, DE->iowindow, &hints);
34639 ripley 2100
     */
2101
 
40759 ripley 2102
    DE->iogc = XCreateGC(iodisplay, DE->iowindow, 0, 0);
34639 ripley 2103
 
46169 ripley 2104
    if(mbcslocale && DE->isEditor) {
32518 ripley 2105
	ioim = XOpenIM(iodisplay, NULL, NULL, NULL);
32527 ripley 2106
	if(!ioim) {
40759 ripley 2107
	    XDestroyWindow(iodisplay, DE->iowindow);
32527 ripley 2108
	    XCloseDisplay(iodisplay);
34639 ripley 2109
	    warning("unable to open X Input Method");
2110
	    return TRUE;
32527 ripley 2111
	}
32555 ripley 2112
 
34639 ripley 2113
	/* search supported input style */
2114
	XGetIMValues(ioim, XNQueryInputStyle, &ioim_styles,NULL);
2115
	for(i = 0; i < ioim_styles->count_styles; i++) {
2116
	    for(j = 0; preedit_styles[j]; j++){
2117
		for(k = 0; status_styles[k]; k++){
2118
		    ioim_style = (preedit_styles[j] | status_styles[k]);
2119
		    if( ioim_styles->supported_styles[i] == ioim_style) {
2120
			goto loop_out;
2121
		    }
2122
		}
2123
	    }
2124
	}
2125
    loop_out:
2126
 
2127
	/* create input context */
2128
	xpoint.x = 0; xpoint.y=0;
2129
	xva_nlist = XVaCreateNestedList(0, XNFontSet, font_set,
2130
					XNSpotLocation, &xpoint, NULL);
2131
 
2132
	ioic = XCreateIC(ioim,
45475 ripley 2133
			 XNInputStyle, ioim_style,
40759 ripley 2134
			 XNClientWindow,DE->iowindow,
2135
			 XNFocusWindow,DE->iowindow,
34639 ripley 2136
			 XNPreeditAttributes, xva_nlist,
2137
			 XNStatusAttributes, xva_nlist,
32518 ripley 2138
			 NULL);
34639 ripley 2139
	XFree(xva_nlist);
32518 ripley 2140
	if(!ioic) {
2141
	    XCloseIM(ioim);
40759 ripley 2142
	    XDestroyWindow(iodisplay, DE->iowindow);
32518 ripley 2143
	    XCloseDisplay(iodisplay);
34639 ripley 2144
	    warning("unable to open X Input Context");
2145
	    return TRUE;
32518 ripley 2146
	}
34639 ripley 2147
 
2148
	/* get XIM processes event. */
2149
	XGetICValues(ioic, XNFilterEvents, &fevent, NULL);
32518 ripley 2150
    }
13974 hornik 2151
 
32677 ripley 2152
    if(!mbcslocale)
40759 ripley 2153
	XSetFont(iodisplay, DE->iogc, DE->font_info->fid);
49591 ripley 2154
 
40759 ripley 2155
    XSetBackground(iodisplay, DE->iogc, iowhite);
2156
    XSetForeground(iodisplay, DE->iogc, ioblack);
2157
    XSetLineAttributes(iodisplay, DE->iogc, 1, LineSolid, CapRound,
2158
		       JoinRound);
32555 ripley 2159
 
34639 ripley 2160
    /*
40759 ripley 2161
    XSelectInput(iodisplay, DE->iowindow,
32555 ripley 2162
		 ButtonPressMask | KeyPressMask
34639 ripley 2163
		 | ExposureMask | StructureNotifyMask | fevent);
2164
    */
2165
 
40759 ripley 2166
    XSelectInput(iodisplay, DE->iowindow,
34639 ripley 2167
		 ButtonPressMask
2168
		 | KeyPressMask
2169
		 | StructureNotifyMask
2170
		 | ExposureMask
2171
		 | EnterWindowMask
2172
		 | LeaveWindowMask
2173
		 | fevent);
40759 ripley 2174
    XMapRaised(iodisplay, DE->iowindow);
32555 ripley 2175
 
13974 hornik 2176
    /* now set up the menu-window, for now use the same text
2177
       dimensions as above */
2178
 
34639 ripley 2179
    /* font size consideration */
87194 kalibera 2180
    for(i = 0; i < (sizeof(menu_label)/sizeof(char *)); i++) {
2181
	int twidthi = textwidth(DE, menu_label[i]);
2182
	if (twidthi > twidth)
2183
	    twidth = twidthi;
2184
    }
34639 ripley 2185
 
13974 hornik 2186
    menuwindow = XCreateSimpleWindow(iodisplay, root, 0, 0, twidth,
40759 ripley 2187
				     4 * DE->box_h, 2, ioblack, iowhite);
13974 hornik 2188
    for (i = 0; i < 4; i++) {
2189
	menupanes[i] = XCreateSimpleWindow(iodisplay, menuwindow, 0,
40759 ripley 2190
					   DE->box_h * i, twidth, DE->box_h,
13974 hornik 2191
					   1, ioblack, iowhite);
2192
	XSelectInput(iodisplay, menupanes[i],
34639 ripley 2193
		     ButtonPressMask | ButtonReleaseMask | ExposureMask
2194
		     );
13974 hornik 2195
    }
2196
 
2197
    /* XMapSubwindows(iodisplay, menuwindow); */
2198
 
2199
 
40759 ripley 2200
    XStoreName(iodisplay, DE->iowindow, title);
13974 hornik 2201
    winattr.override_redirect = True;
2202
    XChangeWindowAttributes(iodisplay, menuwindow,
2203
			    CWBackingStore | CWOverrideRedirect, &winattr);
40759 ripley 2204
    Rsync(DE);
13974 hornik 2205
 
2206
    /* this next sequence makes sure the window is up and ready before
2207
       you start drawing in it */
2208
 
2209
    XNextEvent(iodisplay, &ioevent);
2210
    if (ioevent.xany.type == Expose) {
2211
	while (ioevent.xexpose.count)
2212
	    XNextEvent(iodisplay, &ioevent);
2213
    }
40759 ripley 2214
    XGetWindowAttributes(iodisplay, DE->iowindow, &attribs);
2215
    DE->bwidth = attribs.border_width;
2216
    DE->fullwindowWidth = attribs.width;
2217
    DE->fullwindowHeight = attribs.height;
13974 hornik 2218
 
2219
 
2220
    /* set the active rectangle to be the upper left one */
40759 ripley 2221
    DE->crow = 1;
2222
    DE->ccol = 1;
13974 hornik 2223
    CellModified = FALSE;
40759 ripley 2224
    XSaveContext(iodisplay, DE->iowindow, deContext, (caddr_t) DE);
13974 hornik 2225
    return FALSE;/* success */
2226
}
2227
 
2228
/* MAC/X11 BASICS */
2229
 
82931 ripley 2230
static void bell(void)
13974 hornik 2231
{
2232
    XBell(iodisplay, 20);
2233
}
2234
 
40759 ripley 2235
static void cleararea(DEstruct DE, int xpos, int ypos, int width, int height)
13974 hornik 2236
{
40759 ripley 2237
    XClearArea(iodisplay, DE->iowindow, xpos, ypos, width, height, 0);
13974 hornik 2238
}
2239
 
40759 ripley 2240
static void clearwindow(DEstruct DE)
13974 hornik 2241
{
40759 ripley 2242
    XClearWindow(iodisplay, DE->iowindow);
13974 hornik 2243
}
2244
 
40759 ripley 2245
static void copyarea(DEstruct DE, int src_x, int src_y, int dest_x, int dest_y)
13974 hornik 2246
{
2247
    int mx = max(src_x, dest_x), my = max(src_y, dest_y);
40759 ripley 2248
    XCopyArea(iodisplay, DE->iowindow, DE->iowindow, DE->iogc,
2249
	      src_x, src_y,
2250
	      DE->fullwindowWidth - mx, DE->fullwindowHeight - my,
13974 hornik 2251
	      dest_x, dest_y);
40759 ripley 2252
    Rsync(DE);
13974 hornik 2253
}
2254
 
40759 ripley 2255
static void copyH(DEstruct DE, int src_x, int dest_x, int width)
13974 hornik 2256
{
40759 ripley 2257
    XCopyArea(iodisplay, DE->iowindow, DE->iowindow, DE->iogc,
2258
	      src_x+DE->bwidth, DE->hht,
2259
	      width, DE->windowHeight+1, dest_x+DE->bwidth, DE->hht);
13974 hornik 2260
}
2261
 
40759 ripley 2262
static void drawrectangle(DEstruct DE,
2263
			  int xpos, int ypos, int width, int height,
13974 hornik 2264
			  int lwd, int fore)
2265
{
36044 ripley 2266
#ifdef USE_Xt
13974 hornik 2267
    if (fore == 0)
40759 ripley 2268
	XSetForeground(iodisplay, DE->iogc, xdev.background);
36044 ripley 2269
    else
40759 ripley 2270
	XSetForeground(iodisplay, DE->iogc, xdev.foreground);
36044 ripley 2271
#else
2272
    if (fore == 0)
40759 ripley 2273
	XSetForeground(iodisplay, DE->iogc,
2274
		       WhitePixel(iodisplay, DefaultScreen(iodisplay)));
13974 hornik 2275
    else
40759 ripley 2276
	XSetForeground(iodisplay, DE->iogc,
2277
		       BlackPixel(iodisplay, DefaultScreen(iodisplay)));
36044 ripley 2278
#endif
40759 ripley 2279
    XSetLineAttributes(iodisplay, DE->iogc, lwd, LineSolid,
2280
		       CapRound, JoinRound);
2281
    XDrawRectangle(iodisplay, DE->iowindow, DE->iogc, xpos, ypos,
2282
		   width, height);
13974 hornik 2283
}
2284
 
40759 ripley 2285
static void drawtext(DEstruct DE, int xpos, int ypos, char *text, int len)
13974 hornik 2286
{
32677 ripley 2287
    if(mbcslocale)
32471 ripley 2288
#ifdef HAVE_XUTF8DRAWIMAGESTRING
45475 ripley 2289
	if(utf8locale)
40759 ripley 2290
	    Xutf8DrawImageString(iodisplay, DE->iowindow, font_set,
2291
				 DE->iogc, xpos, ypos,text, len);
45475 ripley 2292
	else
32471 ripley 2293
#endif
40759 ripley 2294
	    XmbDrawImageString(iodisplay, DE->iowindow, font_set,
2295
			       DE->iogc, xpos, ypos,text, len);
32424 ripley 2296
    else
40759 ripley 2297
	XDrawImageString(iodisplay, DE->iowindow, DE->iogc,
2298
			 xpos, ypos, text, len);
2299
    Rsync(DE);
13974 hornik 2300
}
2301
 
40759 ripley 2302
static void Rsync(DEstruct DE)
13974 hornik 2303
{
2304
    XSync(iodisplay, 0);
2305
}
2306
 
87194 kalibera 2307
static int textwidth(DEstruct DE, const char *text)
13974 hornik 2308
{
41831 rgentlem 2309
    int ans;
35844 ripley 2310
    if(mbcslocale) {
32471 ripley 2311
#ifdef HAVE_XUTF8TEXTESCAPEMENT
45475 ripley 2312
	if (utf8locale)
87194 kalibera 2313
	    ans = Xutf8TextEscapement(font_set, text, (int)strlen(text));
45475 ripley 2314
	else
32424 ripley 2315
#endif
87194 kalibera 2316
	    ans = XmbTextEscapement(font_set, text, (int)strlen(text));
45475 ripley 2317
	return ans;
35844 ripley 2318
    }
87194 kalibera 2319
    ans = XTextWidth(DE->font_info, text, (int)strlen(text));
41831 rgentlem 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;
13974 hornik 2329
    XEvent event;
2330
    Window selected_pane;
2331
    SEXP tvec;
2332
 
34639 ripley 2333
    XMoveWindow(iodisplay, menuwindow, x_pos, y_pos);
13974 hornik 2334
    XMapSubwindows(iodisplay, menuwindow);
2335
    XMapRaised(iodisplay, menuwindow);
2336
 
2337
    /* now fill in the menu panes with the correct information */
2338
 
40759 ripley 2339
    if (popupcol > DE->xmaxused) {
24274 ripley 2340
	/* extend work, names and lens */
40759 ripley 2341
	REPROTECT(DE->work = lengthgets(DE->work, popupcol), DE->wpi);
2342
	REPROTECT(DE->names = lengthgets(DE->names, popupcol), DE->npi);
2343
	for (i = DE->xmaxused+1; i < popupcol; i++) {
83436 ripley 2344
	    char clab[20];
2345
	    snprintf(clab, 20, "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