The R Project SVN R

Rev

Rev 88761 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
28991 murdoch 1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
88761 kalibera 3
 *  Copyright (C) 1999-2025  The R Core Team
28991 murdoch 4
 *
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
42300 ripley 16
 *  along with this program; if not, a copy is available at
68956 ripley 17
 *  https://www.R-project.org/Licenses/
28991 murdoch 18
 */
19
 
20
 
21
#ifdef HAVE_CONFIG_H
22
#include <config.h>
23
#endif
33001 ripley 24
#include "win-nls.h"
28991 murdoch 25
 
26
#ifdef Win32
27
#define USE_MDI 1
28
#endif
29
 
40700 ripley 30
#include <Defn.h>
31
#include <Fileio.h>
28991 murdoch 32
#include <stdio.h>
33
#include <Startup.h>
34
#include "graphapp/ga.h"
35
#include "graphapp/graphapp.h"
36
#include "graphapp/stdimg.h"
37
#include "console.h"
38
#include "consolestructs.h"
36901 ripley 39
#define WIN32_LEAN_AND_MEAN 1
28991 murdoch 40
#include <windows.h>
41
#include "rui.h"
42
#include "editor.h"
43
 
63218 ripley 44
/* from sysutils.c */
45
void reEnc2(const char *x, char *y, int ny,
46
	    cetype_t ce_in, cetype_t ce_out, int subst);
47
 
39274 ripley 48
#define gettext GA_gettext
49
 
28991 murdoch 50
#define MCHECK(a) if (!(a)) {del(c); return NULL;}
44201 ripley 51
RECT *RgetMDIsize(void);
28991 murdoch 52
 
53
/* Pointers to currently open editors */
54
static editor REditors[MAXNEDITORS];
55
static int neditors  = 0;
56
static Rboolean fix_editor_up = FALSE;
57
 
83709 kalibera 58
static EditorData neweditordata (int file)
28991 murdoch 59
{
60
    EditorData p;
61
    p = (EditorData) malloc(sizeof(struct structEditorData));
88761 kalibera 62
    if (!p)
63
	return NULL;
28991 murdoch 64
    p->file = file;
83709 kalibera 65
    p->filename = NULL;
29062 murdoch 66
    p->title = (char *) malloc((EDITORMAXTITLE + 1)*sizeof(char));
88761 kalibera 67
    if (!p->title) {
68
	free(p);
69
	return NULL;
70
    }
29062 murdoch 71
    p->title[EDITORMAXTITLE] = p->title[0] = '\0';
28991 murdoch 72
    return p;
73
}
74
 
39498 ripley 75
void deleditordata(EditorData p)
76
{
28991 murdoch 77
    if (p->stealconsole)
78
	fix_editor_up = FALSE;
83709 kalibera 79
    if (p->filename)
80
	free(p->filename);
29051 murdoch 81
    free(p->title);
36258 murdoch 82
    free(p->hmenu);
83
    free(p->pmenu);
28991 murdoch 84
    free(p);
85
}
86
 
43760 ripley 87
static void editor_set_title(editor c, const char *title)
39498 ripley 88
{
28991 murdoch 89
    char wtitle[EDITORMAXTITLE+1];
29051 murdoch 90
    textbox t = getdata(c);
91
    EditorData p = getdata(t);
92
    strncpy(wtitle, title, EDITORMAXTITLE);
29062 murdoch 93
    wtitle[EDITORMAXTITLE] = '\0';
29051 murdoch 94
    strcpy(p->title, wtitle);
43410 murdoch 95
    if (strlen(wtitle) + strlen(G_("R Editor")) + 3 < EDITORMAXTITLE) {
45070 ripley 96
	strcat(wtitle, " - ");
97
	strcat(wtitle, G_("R Editor"));
43410 murdoch 98
    }
28991 murdoch 99
    settext(c, wtitle);
100
}
101
 
102
/*** FILE MANAGEMENT FUNCTIONS ***/
103
 
83709 kalibera 104
/* FIXME: simplify this once UTF-8 is the only supported native encoding. */
105
 
106
static wchar_t* utf8_to_wchar(const char *src)
107
{
108
    size_t needed = Rf_utf8towcs(NULL, src, 0);
109
    wchar_t *res = (wchar_t  *)malloc((needed + 1)*sizeof(wchar_t));
110
    if (res)
111
	Rf_utf8towcs(res, src, needed + 1);
112
    return res;
113
}
114
 
115
static char* utf8_to_native(const char *src)
116
{
117
    /* a defensive guess, reEnc2 would throw error if not enough */
118
    size_t needed = strlen(src) * 4;
119
    char *res = (char *)malloc(needed + 1);
120
    if (res)
121
	reEnc2(src, res, needed + 1, CE_UTF8, CE_NATIVE, 3);
122
    return res;
123
}	
124
 
125
static char* native_to_utf8(const char *src)
126
{
127
    /* a defensive guess, reEnc2 would throw error if not enough */
128
    size_t needed = strlen(src) * 4;
129
    char *res = (char *)malloc(needed + 1);
130
    if (res)
131
	reEnc2(src, res, needed + 1, CE_NATIVE, CE_UTF8, 3);
132
    return res;
133
}	
134
 
135
static char* wchar_to_utf8(const wchar_t *src)
136
{
137
    size_t needed = Rf_wcstoutf8(NULL, src, INT_MAX);
138
    char *res = (char *)malloc(needed + 1);
139
    if (res)
140
	Rf_wcstoutf8(res, src, needed + 1);
141
    return res;
142
}
143
 
46842 ripley 144
FILE *R_wfopen(const wchar_t *filename, const wchar_t *mode);
145
 
83709 kalibera 146
#define MSGSIZE 512
147
 
44016 ripley 148
static void editor_load_file(editor c, const char *name, int enc)
28991 murdoch 149
{
150
    textbox t = getdata(c);
151
    EditorData p = getdata(t);
152
    FILE *f;
83709 kalibera 153
    char *buffer = NULL, tmp[MSGSIZE];
63219 ripley 154
    const char *sname;
39498 ripley 155
    long num = 1, bufsize;
156
 
46842 ripley 157
    if(enc == CE_UTF8) {
83709 kalibera 158
	wchar_t *wname = utf8_to_wchar(name);
88761 kalibera 159
	if (!wname) {
160
	    R_ShowMessage(G_("Not enough memory"));
161
	    return;
162
	}
46842 ripley 163
	f = R_wfopen(wname, L"r");
83709 kalibera 164
	free(wname);
165
	sname = utf8_to_native(name);
88761 kalibera 166
	if (!sname) {
167
	    R_ShowMessage(G_("Not enough memory"));
168
	    return;
169
	}
46842 ripley 170
    } else {
171
	f = R_fopen(name, "r");
172
	sname = name;
173
    }
174
    if (f == NULL) {
83709 kalibera 175
	snprintf(tmp, MSGSIZE, 
46842 ripley 176
		 G_("unable to open file %s for reading"), sname);
177
	R_ShowMessage(tmp);
83709 kalibera 178
	if (enc == CE_UTF8)
179
	    free((char *)sname); 
46842 ripley 180
	return;
181
    }
28991 murdoch 182
    p->file = 1;
83709 kalibera 183
    if (p->filename)
184
	free(p->filename);
185
    if (enc == CE_UTF8) {
186
	p->filename = (char *) malloc(strlen(name) + 1);
88761 kalibera 187
	if (!p->filename) {
188
	    free((char *)sname);
189
	    R_ShowMessage(G_("Not enough memory"));
190
	    return;
191
	}
83709 kalibera 192
	strcpy(p->filename, name);
88761 kalibera 193
    } else {
83709 kalibera 194
	p->filename = native_to_utf8(name);
88761 kalibera 195
	if (!p->filename) {
196
	    R_ShowMessage(G_("Not enough memory"));
197
	    return;
198
	}
199
    }
83709 kalibera 200
 
28991 murdoch 201
    bufsize = 0;
202
    while (num > 0) {
39498 ripley 203
	buffer = realloc(buffer, bufsize + 3000 + 1);
88761 kalibera 204
	if (!buffer) {
205
	    if (enc == CE_UTF8)
206
		free((char *)sname);
207
	    R_ShowMessage(G_("Not enough memory"));
208
	    return;
209
	}
39498 ripley 210
	num = fread(buffer + bufsize, 1, 3000 - 1, f);
28991 murdoch 211
	if (num >= 0) {
212
	    bufsize += num;
213
	    buffer[bufsize] = '\0';
214
	}
215
	else {
83709 kalibera 216
	    snprintf(tmp, MSGSIZE,
46842 ripley 217
		     G_("Could not read from file '%s'"), sname);
28991 murdoch 218
	    askok(tmp);
219
	}
220
    }
221
    setlimittext(t, 2 * strlen(buffer));
222
    settext(t, buffer);
223
    gsetmodified(t, 0);
224
    free(buffer);
225
    fclose(f);
83709 kalibera 226
    if (enc == CE_UTF8)
227
	free((char *)sname); 
28991 murdoch 228
}
229
 
46842 ripley 230
static void editor_save_file(editor c, const char *name, int enc)
28991 murdoch 231
{
232
    textbox t = getdata(c);
233
    FILE *f;
83709 kalibera 234
    char buf[MSGSIZE];
63219 ripley 235
    const char *sname;
46842 ripley 236
 
28991 murdoch 237
    if (name == NULL)
238
	return;
239
    else {
46842 ripley 240
	if(enc == CE_UTF8) {
83709 kalibera 241
	    wchar_t *wname = utf8_to_wchar(name);
46842 ripley 242
	    f = R_wfopen(wname, L"w");
83709 kalibera 243
	    free(wname);
244
	    sname = utf8_to_native(name);
46842 ripley 245
	} else {
246
	    sname = name;
247
	    f = R_fopen(sname, "w");
248
	}
28991 murdoch 249
	if (f == NULL) {
83709 kalibera 250
	    snprintf(buf, MSGSIZE, G_("Could not save file '%s'"), sname);
28991 murdoch 251
	    askok(buf);
83709 kalibera 252
	    if (enc == CE_UTF8)
253
		free((char *)sname);
28991 murdoch 254
	    return;
255
	}
256
	fprintf(f, "%s", gettext(t));
257
	fclose(f);
83709 kalibera 258
	if (enc == CE_UTF8)
259
	    free((char *)sname);
28991 murdoch 260
    }
261
}
262
 
45070 ripley 263
static void editorsaveas(editor c)
264
{
28991 murdoch 265
    textbox t = getdata(c);
266
    EditorData p = getdata(t);
46842 ripley 267
    wchar_t *wname;
268
 
269
    setuserfilterW(L"R files (*.R)\0*.R\0S files (*.q, *.ssc, *.S)\0*.q;*.ssc;*.S\0All files (*.*)\0*.*\0\0");
88858 kalibera 270
    wname = askfilesaveW(G_("Save script as"), p->file ? p->filename : "");
50851 murdoch 271
    if (wname) {
83709 kalibera 272
	char *name = wchar_to_utf8(wname);
57464 ripley 273
	char *q = strchr(name, '.');
83709 kalibera 274
	if(!q) {
275
	    char *tmp = (char *)malloc(strlen(name) + 2 + 1);
276
	    if (tmp) {
277
		strcpy(tmp, name);
278
		strcat(tmp, ".R");
279
		free(name);
280
		name = tmp;
88761 kalibera 281
	    } else {
282
		R_ShowMessage(G_("Not enough memory"));
283
		return;
83709 kalibera 284
	    }
285
	}
46842 ripley 286
	editor_save_file(c, name, CE_UTF8);
28991 murdoch 287
	p->file = 1;
83709 kalibera 288
	p->filename = name; /* keeps name */
28991 murdoch 289
	gsetmodified(t, 0);
83709 kalibera 290
	char *sname = utf8_to_native(name);
88761 kalibera 291
	if (sname) {
292
	    editor_set_title(c, sname);
293
	    free(sname);
294
	} else
295
	    R_ShowMessage(G_("Not enough memory"));
28991 murdoch 296
    }
50851 murdoch 297
    show(c);
28991 murdoch 298
}
299
 
300
static void menueditorsaveas(control m)
301
{
302
    editor c = getdata(m);
303
    editorsaveas(c);
304
}
305
 
306
static void editorsave(editor c)
307
{
308
    textbox t = getdata(c);
309
    EditorData p = getdata(t);
310
    if (p->file) {  /* save existing file without prompt */
46842 ripley 311
	editor_save_file(c, p->filename, CE_UTF8);
28991 murdoch 312
	gsetmodified(t, 0);
313
    }
314
    /* if new file then prompt for name to save as */
315
    else editorsaveas(c);
316
}
317
 
318
static void menueditorsave(control m)
319
{
320
    editor c = getdata(m);
321
    editorsave(c);
322
    show(c); /* steal focus back from tool button */
323
}
324
 
79040 kalibera 325
/* global console configuration variables, from console.c */
326
extern char fontname[LF_FACESIZE+4];
327
extern int fontsty, pointsize;
28991 murdoch 328
 
329
static void editorprint(control m)
330
{
331
    printer lpr;
332
    font f;
333
    textbox t = getdata(m);
43800 ripley 334
    const char *contents = gettext(t);
28991 murdoch 335
    char msg[LF_FACESIZE + 128];
336
    char *linebuf = NULL;
337
    int cc, rr, fh, page, linep, i, j, istartline;
338
    int top, left;
339
 
340
    if (!(lpr = newprinter(0.0, 0.0, ""))) return;
341
    f = gnewfont(lpr, strcmp(fontname, "FixedFont") ? fontname : "Courier New",
44575 ripley 342
		 fontsty, pointsize, 0.0, 1);
28991 murdoch 343
    top = devicepixelsy(lpr) / 5;
344
    left = devicepixelsx(lpr) / 5;
345
    fh = fontheight(f);
346
    rr = getheight(lpr) - top;
347
    cc = getwidth(lpr) - 2*left;
348
 
349
    linep = rr; /* line in printer page */
350
    page = 1;
351
    i = 0;
352
    while (i < strlen(contents)) {
353
	if ( linep + fh >= rr ) { /* new page */
354
	    if (page > 1) nextpage(lpr);
62583 ripley 355
	    snprintf(msg, LF_FACESIZE + 128, "Page %d", page++);
45070 ripley 356
	    gdrawstr(lpr, f, Black, pt(cc - gstrwidth(lpr, f, msg) - 1, top),
39498 ripley 357
		     msg);
28991 murdoch 358
	    linep = top + 2*fh;
359
	}
360
	j = 0;
361
	istartline = i;
362
	while (contents[i] != '\n' && contents[i] != '\0') {
363
	    ++i; ++j;
364
	}
365
	linebuf = realloc(linebuf, (j+1)*sizeof(char));
88761 kalibera 366
	if (!linebuf) {
367
	    del(lpr);
368
	    R_ShowMessage(G_("Not enough memory"));
369
	    return;
370
	}
28991 murdoch 371
	strncpy(linebuf, &contents[istartline], j);
372
	linebuf[j] = '\0';
45070 ripley 373
	gdrawstr(lpr, f, Black, pt(left, linep), linebuf);
28991 murdoch 374
	linep += fh;
375
	++i;
376
    }
377
    free(linebuf);
378
    if (f != FixedFont) del(f);
379
    del(lpr);
380
}
381
 
382
static void editorconsole(editor c)
383
{
384
    show(RConsole);
385
}
386
 
39498 ripley 387
/* Remove global pointer to editor when closing an editor. Fill the
388
 * gap in the array with the last editor in the list */
28991 murdoch 389
 
39498 ripley 390
static void editorupdateglobals(editor c)
391
{
392
    int i = 0;
28991 murdoch 393
    if (neditors > 1) {
394
	while (REditors[i] != c) ++i;
395
	if (i < neditors - 1)
396
	    REditors[i] = REditors[neditors-1];
397
    }
398
    --neditors;
399
}
400
 
29062 murdoch 401
/* Hooks called when editor window is destroyed */
28991 murdoch 402
 
39498 ripley 403
static void editordel(editor c)
404
{
29062 murdoch 405
    editorupdateglobals(c);
406
}
407
 
39498 ripley 408
static void textboxdel(textbox t)
409
{
28991 murdoch 410
    EditorData p = getdata(t);
411
    deleditordata(p);
412
}
413
 
39498 ripley 414
int editorchecksave(editor c)
415
{
28991 murdoch 416
    textbox t = getdata(c);
417
    EditorData p = getdata(t);
418
    int save;
419
    char buf[EDITORMAXTITLE + 100];
420
    if (ggetmodified(t)) {
33756 ripley 421
	snprintf(buf, EDITORMAXTITLE + 100,
32604 ripley 422
		 "\"%s\" has been modified.  Do you want to save the changes?",
29051 murdoch 423
		 (p->title ? p->title : "Untitled"));
28991 murdoch 424
	save = askyesnocancel(buf);
425
	switch (save) {
426
	case YES:
427
	    editorsave(c);
428
	    break;
429
	case NO:
430
	    break;
431
	case CANCEL:
61963 ripley 432
	    return 1;
28991 murdoch 433
	}
434
    }
435
    return 0;
436
}
437
 
438
static void editorclose(editor c)
439
{
440
    if (!editorchecksave(c))
441
	del(c);
442
}
443
 
444
static void menueditorclose(control m)
445
{
446
    editor c = getdata(m);
447
    editorclose(c);
448
}
449
 
450
/* Called when exiting Rgui, check if any open editors need saving */
451
 
44201 ripley 452
void editorcleanall(void)
28991 murdoch 453
{
454
    int i;
455
    for (i = neditors-1;  i >= 0; --i) {
61963 ripley 456
	if (editorchecksave(REditors[i])) {
457
	    R_ProcessEvents();  // see R_CleanUp
458
	    jump_to_toplevel();
459
	}
28991 murdoch 460
	del(REditors[i]);
461
    }
462
}
463
 
44201 ripley 464
static void editornew(void)
28991 murdoch 465
{
44016 ripley 466
    Rgui_Edit("", CE_NATIVE, "", 0);
28991 murdoch 467
}
468
 
469
void menueditornew(control m)
470
{
471
    editornew();
472
}
473
 
43760 ripley 474
static void editoropen(const char *default_name)
28991 murdoch 475
{
46842 ripley 476
    wchar_t *wname;
477
 
28991 murdoch 478
    int i; textbox t; EditorData p;
46842 ripley 479
    setuserfilterW(L"R files (*.R)\0*.R\0S files (*.q, *.ssc, *.S)\0*.q;*.ssc;*.S\0All files (*.*)\0*.*\0\0");
480
    wname = askfilenameW(G_("Open script"), default_name); /* returns NULL if open dialog cancelled */
481
    if (wname) {
83709 kalibera 482
	char *name = wchar_to_utf8(wname);
28991 murdoch 483
	/* check if file is already open in an editor. If so, close and open again */
39498 ripley 484
	for (i = 0; i < neditors; ++i) {
28991 murdoch 485
	    t = getdata(REditors[i]);
486
	    p = getdata(t);
83709 kalibera 487
	    if (p->filename && !strcmp (name, p->filename)) {
28991 murdoch 488
		editorclose(REditors[i]);
489
		break;
490
	    }
491
	}
83709 kalibera 492
	char *sname = utf8_to_native(name);
493
	Rgui_Edit(name, CE_UTF8, sname, 0);
494
	free(name);
495
	free(sname);
50851 murdoch 496
    } else show(RConsole);
28991 murdoch 497
}
498
 
499
void menueditoropen(control m)
500
{
501
    editor c = getdata(m);
45070 ripley 502
    /* It really is not clear what is meant here: seems to assume an
503
       editor window but is called from elsewhere, hopefully with NULL
35234 ripley 504
       (since 2.1.1 patched). */
28991 murdoch 505
    if (c) {
506
	textbox t = getdata(c);
507
	EditorData p = getdata(t);
83709 kalibera 508
	if (p->file && p->filename) {
509
	    /* name of currently-open file, if there is one */
510
	    char *sname = utf8_to_native(p->filename);
511
	    editoropen(sname);
512
	    free(sname);
513
	    return;
514
	}
515
    } 
516
    editoropen("");
28991 murdoch 517
}
518
 
519
 
520
/*** EDITING FUNCTIONS ***/
521
 
522
static void editorundo(control m)
523
{
524
    textbox t = getdata(m);
525
    undotext(t);
526
}
527
 
528
static void editorcut(control m)
529
{
530
    textbox t = getdata(m);
531
    cuttext(t);
532
}
533
 
534
static void editorcopy(control m)
535
{
536
    textbox t = getdata(m);
537
    copytext(t);
538
}
539
 
540
static void editorpaste(control m)
541
{
542
    textbox t = getdata(m);
39498 ripley 543
    /* check whether the widget text limit needs to be increased
544
     * before doing the paste */
28991 murdoch 545
    int pastelen = getpastelength();
546
    checklimittext(t, pastelen + 1);
547
    pastetext(t);
548
}
549
 
550
static void editordelete(control m)
551
{
552
    textbox t = getdata(m);
553
    cleartext(t);
554
}
555
 
556
static void editorselectall(control m)
557
{
558
    textbox t = getdata(m);
559
    selecttextex(t, 0, -1);
560
}
561
 
562
static void editorfind(control m)
563
{
564
    textbox t = getdata(m);
565
    finddialog(t);  /* actual find/replace work is in graphapp/dialogs.c */
566
}
567
 
568
static void editorreplace(control m)
569
{
570
    textbox t = getdata(m);
571
    replacedialog(t);
572
}
573
 
574
 
575
/*** FUNCTIONS FOR RUNNING R CODE FROM THE EDITOR ***/
576
 
577
static void editorrunline(textbox t)
578
{
38972 ripley 579
    int length = getlinelength(t); /* return character num */
82394 kalibera 580
    /* Extra space for null and word length in getcurrentline */
581
    size_t alength = length * MB_CUR_MAX + 1 + sizeof(WORD);
582
    char *line = malloc(alength);
88761 kalibera 583
    if (!line) {
584
	R_ShowMessage(G_("Not enough memory"));
585
	return;
586
    }
82394 kalibera 587
    memset(line, 0, alength);
28991 murdoch 588
    getcurrentline(t, line, length);
589
    consolecmd(RConsole, line);
590
    free(line);
591
    scrollcaret(t, 1);
592
    show(t);
593
}
594
 
595
/* For the moment, send the selected text to the console via the
596
   clipboard. Wasteful, but currently consolecmd only allows a maximum
597
   of 8K of text */
598
 
599
static void editorrunselection(textbox t, long start, long end)
600
{
601
    copytext(t);
602
    if (consolecanpaste(RConsole)) {
603
	consolepaste(RConsole);
604
	if (gettext(t)[end-1] != '\n')
605
	    consolenewline(RConsole);
606
    }
607
}
608
 
29001 murdoch 609
static Rboolean busy_running = FALSE;
610
 
28991 murdoch 611
static void editorrun(textbox t)
612
{
29001 murdoch 613
    if (!busy_running) {
45070 ripley 614
	long start=0, end=0;
615
	if (CharacterMode != RGui) {
616
	    R_ShowMessage(G_("No RGui console to paste to"));
617
	    return;
618
	}
619
	busy_running = TRUE;
620
	textselectionex(t, &start, &end);
621
	if (start >= end)
29001 murdoch 622
	    editorrunline(t);
45070 ripley 623
	else {
29001 murdoch 624
	    editorrunselection(t, start, end);
625
	    selecttextex(t, end, end); /* move insertion point to end of selection after running */
45070 ripley 626
	}
627
	busy_running = FALSE;
28991 murdoch 628
    }
629
}
630
 
631
static void menueditorrun(control m)
632
{
633
    textbox t = getdata(m);
634
    editorrun(t);
635
}
636
 
637
static void editorrunall(control m)
638
{
639
    textbox t = getdata(m);
640
    long start=0, end=0;
641
    textselectionex(t, &start, &end); /* save current selection state */
642
    selecttextex(t, 0, -1);
29001 murdoch 643
    editorrun(t);
28991 murdoch 644
    selecttextex(t, start, end);  /* return to original selection state */
645
}
646
 
647
/* Grey out menu buttons as appropriate */
648
 
649
static void editormenuact(control m)
650
{
651
    long start, end;
652
    textbox t = getdata(m);
653
    EditorData p = getdata(t);
654
    textselectionex(t, &start, &end);
655
    if (start < end) {
45070 ripley 656
	enable(p->mcut);
657
	enable(p->mcopy);
658
	enable(p->mdelete);
659
	enable(p->mpopcut);
660
	enable(p->mpopcopy);
661
	enable(p->mpopdelete);
28991 murdoch 662
    }
663
    else {
664
	disable(p->mcut);
665
	disable(p->mcopy);
666
	disable(p->mdelete);
667
	disable(p->mpopcut);
668
	disable(p->mpopcopy);
669
	disable(p->mpopdelete);
670
    }
671
    if (modeless_active()){
672
	disable(p->mfind);
673
	disable(p->mreplace);
674
    }
675
    else {
676
	enable(p->mfind);
677
	enable(p->mreplace);
678
    }
36258 murdoch 679
    helpmenuact(p->hmenu);
680
    pkgmenuact(p->pmenu);
28991 murdoch 681
}
682
 
683
static void editorresize(editor c, rect r)
684
{
685
    resize(getdata(c), r);
686
    resize(c, r);
687
}
688
 
689
static void editorcontrolkeydown(textbox t, int key)
690
{
691
    switch (key) {
692
    case F5:
693
	editorrun(t);
694
	break;
695
    }
696
}
697
 
698
static void editorasciikeydown(textbox t, int key)
699
{
700
    /* check whether the text limit is about to be exceeded and
45070 ripley 701
       increase it if necessary.  Ugh - this callback is called after
702
       the text is inserted, so we make space for two characters */
28991 murdoch 703
    checklimittext(t, 2);
704
}
705
 
706
/* Set keyboard focus to the text editing area when the editor window receives focus */
707
 
708
static void editorfocus(editor c)
709
{
710
    textbox t = getdata(c);
711
    show(t);
712
}
713
 
44201 ripley 714
static void editorhelp(void)
28991 murdoch 715
{
716
    char s[4096];
717
 
39498 ripley 718
    strcpy(s, G_("R EDITOR\n"));
719
    strcat(s, "\n");
720
    strcat(s, G_("A standard text editor for editing and running R code.\n"));
721
    strcat(s, "\n");
722
    strcat(s, G_("RUNNING COMMANDS\n"));
723
    strcat(s, G_("To run a line or section of R code, select the code and either\n"));
724
    strcat(s, G_("     Press Ctrl-R\n"));
725
    strcat(s, G_("     Select \"Run line or selection\" from the \"Edit\" menu\n"));
726
    strcat(s, G_("     Press the \"Run line or selection\" icon on the toolbar\n"));
727
    strcat(s, G_("This will copy the selected commands to the console and evaluate them.\n"));
728
    strcat(s, G_("If there is no selection, this will just run the current line and advance\n"));
729
    strcat(s, G_("the cursor by one line.\n"));
28991 murdoch 730
 
731
    askok(s);
732
}
733
 
734
 
735
static void menueditorhelp(control m)
736
{
737
    editorhelp();
738
}
739
 
740
static MenuItem EditorPopup[] = {                /* Numbers used below */
33001 ripley 741
    {GN_("Run line or selection"), menueditorrun, 'R', 0}, /* 0 */
28991 murdoch 742
    {"-", 0, 0, 0},
33001 ripley 743
    {GN_("Undo"), editorundo, 'Z', 0},                     /* 2 */
28991 murdoch 744
    {"-", 0, 0, 0},
33001 ripley 745
    {GN_("Cut"), editorcut, 'X', 0},                       /* 4 */
746
    {GN_("Copy"), editorcopy, 'C', 0},                     /* 5 */
747
    {GN_("Paste"), editorpaste, 'V', 0},                   /* 6 */
748
    {GN_("Delete"), editordelete, 0, 0},                   /* 7 */
28991 murdoch 749
    {"-", 0, 0, 0},
33001 ripley 750
    {GN_("Select all"), editorselectall, 'A', 0},          /* 9 */
28991 murdoch 751
    LASTMENUITEM
752
};
753
 
44201 ripley 754
static editor neweditor(void)
28991 murdoch 755
{
756
    int x, y, w, h, w0, h0;
757
    editor c;
758
    menuitem m;
759
    textbox t;
760
    long flags;
761
    font editorfn = (consolefn ? consolefn : FixedFont);
83709 kalibera 762
    EditorData p = neweditordata(0);
28991 murdoch 763
    DWORD rand;
764
 
88761 kalibera 765
    if (!p)
766
	return NULL;
28991 murdoch 767
    w = (pagercol + 1)*fontwidth(editorfn);
768
    h = (pagerrow + 1)*fontheight(editorfn) + 1;
769
#ifdef USE_MDI
770
    if(ismdi()) {
771
	RECT *pR = RgetMDIsize();
772
	w0 = pR->right;
773
	h0 = pR->bottom;
774
    } else {
775
#endif
776
	w0 = devicewidth(NULL);
777
	h0 = deviceheight(NULL);
778
#ifdef USE_MDI
779
    }
780
#endif
781
    x = (w0 - w) / 2; x = x > 20 ? x : 20;
782
    y = (h0 - h) / 2; y = y > 20 ? y : 20;
783
    rand = GetTickCount();
784
    w0 = 0.4*x; h0 = 0.4*y;
785
    w0 = w0 > 20 ? w0 : 20;
786
    h0 = h0 > 20 ? h0 : 20;
787
    x += (rand % w0) - w0/2;
788
    y += ((rand/w0) % h0) - h0/2;
789
    flags = StandardWindow | Menubar;
790
#ifdef USE_MDI
791
    if (ismdi()) flags |= Document;
792
#endif
793
    c = (editor) newwindow("", rect(x, y, w, h), flags);
794
    t = newrichtextarea(NULL, rect(0, 0, w, h));
795
    setdata(c, t);
796
    setdata(t, p);
797
 
798
    gsetcursor(c, ArrowCursor);
46784 murdoch 799
    setforeground(c, guiColors[editorfg]);
800
    setbackground(c, guiColors[editorbg]);
801
    setbackground(t, guiColors[editorbg]);
46774 murdoch 802
 
28991 murdoch 803
#ifdef USE_MDI
804
    if (ismdi() && (RguiMDI & RW_TOOLBAR)) {
805
	int btsize = 24;
806
	rect r = rect(2, 2, btsize, btsize);
807
	control tb, bt;
808
	addto(c);
809
	MCHECK(tb = newtoolbar(btsize + 4));
810
	addto(tb);
811
	MCHECK(bt = newtoolbutton(open_image, r, menueditoropen));
33001 ripley 812
	MCHECK(addtooltip(bt, G_("Open script")));
28991 murdoch 813
	setdata(bt, c);
814
	r.x += (btsize + 1) ;
815
	MCHECK(bt = newtoolbutton(save_image, r, menueditorsave));
33001 ripley 816
	MCHECK(addtooltip(bt,  G_("Save script")));
28991 murdoch 817
	setdata(bt, c);
818
	r.x += (btsize + 6);
819
	MCHECK(bt = newtoolbutton(copy1_image, r, menueditorrun));
33001 ripley 820
	MCHECK(addtooltip(bt, G_("Run line or selection")));
28991 murdoch 821
	setdata(bt, t);
822
	r.x += (btsize + 6);
823
	MCHECK(bt = newtoolbutton(console_image, r, editorconsole));
33001 ripley 824
	MCHECK(addtooltip(bt, G_("Return focus to Console")));
28991 murdoch 825
	r.x += (btsize + 6);
826
	MCHECK(bt = newtoolbutton(print_image, r, editorprint));
33001 ripley 827
	MCHECK(addtooltip(bt, G_("Print script")));
28991 murdoch 828
	setdata(bt, t);
33001 ripley 829
	MCHECK(addtooltip(bt, G_("Print")));
28991 murdoch 830
    }
831
#endif
832
    addto(c);
833
    /* Right-click context menu */
834
    MCHECK(m = gpopup(editormenuact, EditorPopup));
835
    setdata(m, t);
836
    setdata(EditorPopup[0].m, t);
837
    setdata(EditorPopup[2].m, t);
838
    setdata(p->mpopcut = EditorPopup[4].m, t);
839
    setdata(p->mpopcopy = EditorPopup[5].m, t);
840
    setdata(EditorPopup[6].m, t);
841
    setdata(p->mpopdelete = EditorPopup[7].m, t);
842
    setdata(EditorPopup[9].m, t);
843
 
844
    addto(c);
845
    MCHECK(m = newmenubar(editormenuact));
846
    setdata(m, t);
33001 ripley 847
    MCHECK(newmenu(G_("File")));
848
    MCHECK(m = newmenuitem(G_("New script"), 'N', menueditornew));
28991 murdoch 849
    setdata(m, c);
33001 ripley 850
    MCHECK(m = newmenuitem(G_("Open script..."), 'O', menueditoropen));
28991 murdoch 851
    setdata(m, c);
33001 ripley 852
    MCHECK(m = newmenuitem(G_("Save"), 'S', menueditorsave));
28991 murdoch 853
    setdata(m, c);
33001 ripley 854
    MCHECK(m = newmenuitem(G_("Save as..."), 0, menueditorsaveas));
28991 murdoch 855
    setdata(m, c);
856
    MCHECK(m = newmenuitem("-", 0, NULL));
69561 murdoch 857
    MCHECK(m = newmenuitem(G_("Print..."), 'P', editorprint));
28991 murdoch 858
    setdata(m, t);
859
    MCHECK(m = newmenuitem("-", 0, NULL));
33001 ripley 860
    MCHECK(m = newmenuitem(G_("Close script"), 0, menueditorclose));
28991 murdoch 861
    setdata(m, c);
33001 ripley 862
    MCHECK(newmenu(G_("Edit")));
863
    MCHECK(m = newmenuitem(G_("Undo"), 'Z', editorundo));
28991 murdoch 864
    setdata(m, t);
865
    MCHECK(m = newmenuitem("-", 0, NULL));
33001 ripley 866
    MCHECK(p->mcut = newmenuitem(G_("Cut"), 'X', editorcut));
28991 murdoch 867
    setdata(p->mcut, t);
33001 ripley 868
    MCHECK(p->mcopy = newmenuitem(G_("Copy"), 'C', editorcopy));
28991 murdoch 869
    setdata(p->mcopy, t);
33001 ripley 870
    MCHECK(m = newmenuitem(G_("Paste"), 'V', editorpaste));
28991 murdoch 871
    setdata(m, t);
33001 ripley 872
    MCHECK(p->mdelete = newmenuitem(G_("Delete"), 0, editordelete));
28991 murdoch 873
    setdata(p->mdelete, t);
33001 ripley 874
    MCHECK(m = newmenuitem(G_("Select all"), 'A', editorselectall));
28991 murdoch 875
    setdata(m, t);
33001 ripley 876
    MCHECK(newmenuitem(G_("Clear console"), 'L', menuclear));
28991 murdoch 877
    MCHECK(m = newmenuitem("-", 0, NULL));
33001 ripley 878
    MCHECK(m = newmenuitem(G_("Run line or selection"), 'R', menueditorrun));
28991 murdoch 879
    setdata(m, t);
33001 ripley 880
    MCHECK(m = newmenuitem(G_("Run all"), 0, editorrunall));
28991 murdoch 881
    setdata(m, t);
882
    MCHECK(m = newmenuitem("-", 0, NULL));
33001 ripley 883
    MCHECK(p->mfind = newmenuitem(G_("Find..."), 'F', editorfind));
28991 murdoch 884
    setdata(p->mfind, t);
33001 ripley 885
    MCHECK(p->mreplace = newmenuitem(G_("Replace..."), 'H', editorreplace));
28991 murdoch 886
    setdata(p->mreplace, t);
887
    MCHECK(m = newmenuitem("-", 0, NULL));
33001 ripley 888
    MCHECK(newmenuitem(G_("GUI preferences..."), 0, menuconfig));
28991 murdoch 889
 
890
    /* Packages menu should go here */
36258 murdoch 891
    p->pmenu = (PkgMenuItems) malloc(sizeof(struct structPkgMenuItems));
88761 kalibera 892
    if (!p->pmenu)
893
	return NULL;
36258 murdoch 894
    RguiPackageMenu(p->pmenu);
895
 
28991 murdoch 896
#ifdef USE_MDI
897
    newmdimenu(); /* Create and fill the 'Window' menu */
898
#endif
899
 
33001 ripley 900
    MCHECK(m = newmenu(G_("Help")));
901
    MCHECK(newmenuitem(G_("Editor"), 0, menueditorhelp));
28991 murdoch 902
    MCHECK(newmenuitem("-", 0, NULL));
36258 murdoch 903
    p->hmenu = (HelpMenuItems) malloc(sizeof(struct structHelpMenuItems));
88761 kalibera 904
    if (!p->hmenu)
905
	return NULL;
36258 murdoch 906
    RguiCommonHelp(m, p->hmenu);
28991 murdoch 907
 
908
    settextfont(t, editorfn);
46784 murdoch 909
    setforeground(t, guiColors[editorfg]);    
28991 murdoch 910
    setresize(c, editorresize);
911
    setclose(c, editorclose);
912
    setdel(c, editordel);
29062 murdoch 913
    setdel(t, textboxdel);
28991 murdoch 914
    setonfocus(c, editorfocus);
915
    setkeyaction(t, editorcontrolkeydown);
916
    setkeydown(t, editorasciikeydown);
917
 
918
    /* Store pointer to new editor in global array */
919
    REditors[neditors] = c;
920
    ++neditors;
921
    return c;
922
}
923
 
924
/* Change the font used in all running editors */
925
 
926
void editorsetfont(font f)
927
{
928
    int i, ismod;
929
    textbox t;
33756 ripley 930
    for (i = 0; i < neditors; i++) {
28991 murdoch 931
	t = getdata(REditors[i]);
45070 ripley 932
	ismod = ggetmodified(t);
33756 ripley 933
	/* Don't change the modification flag when changing font  */
28991 murdoch 934
	settextfont(t, f);
935
	gsetmodified(t, ismod);
936
	show(t);
937
    }
938
}
939
 
940
static void eventloop(editor c)
941
{
942
    while (fix_editor_up) {
943
	/* avoid consuming 100% CPU time here */
64106 murdoch 944
	R_WaitEvent();
28991 murdoch 945
	R_ProcessEvents();
946
    }
947
}
948
 
949
/* Open existing file for editing or open blank editor for a new
950
   file. If calling from fix() or edit(), then don't send events to
951
   the console until editor is closed.  */
952
 
29219 ripley 953
#include <unistd.h>
954
 
44016 ripley 955
int Rgui_Edit(const char *filename, int enc, const char *title,
956
	      int modal)
28991 murdoch 957
{
958
    editor c;
959
    EditorData p;
29219 ripley 960
 
28991 murdoch 961
    if (neditors == MAXNEDITORS) {
33001 ripley 962
	R_ShowMessage(G_("Maximum number of editors reached"));
28991 murdoch 963
	return 1;
964
    }
965
    c = neweditor();
966
    if (!c) {
33001 ripley 967
	R_ShowMessage(G_("Unable to create editor window"));
28991 murdoch 968
	return 1;
969
    }
970
    if (strlen(filename) > 0) {
46842 ripley 971
	editor_load_file(c, filename, enc);
29051 murdoch 972
	editor_set_title(c, title);
28991 murdoch 973
    }
974
    else {
33001 ripley 975
	editor_set_title(c, G_("Untitled"));
28991 murdoch 976
    }
977
    show(c);
45070 ripley 978
 
36252 murdoch 979
    p = getdata(getdata(c));
44016 ripley 980
    p->stealconsole = modal;
981
    if (modal) {
45070 ripley 982
	fix_editor_up = TRUE;
983
	eventloop(c);
36258 murdoch 984
    }
28991 murdoch 985
    return 0;
986
}