The R Project SVN R

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4394 ripley 1
/*
2
 *  R : A Computer Langage for Statistical Data Analysis
3
 *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
24610 ripley 4
 *  Copyright (C) 1998--2003  Guido Masarotto and Brian Ripley
4394 ripley 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
17
 *  along with this program; if not, write to the Free Software
5458 ripley 18
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
4394 ripley 19
 */
20
 
6098 pd 21
/*--- Device Driver for Windows; this file started from
8971 ripley 22
 *  ../unix/X11/devX11.c --
6098 pd 23
 */
24610 ripley 24
 
5222 ripley 25
#ifdef HAVE_CONFIG_H
7701 hornik 26
#include <config.h>
5222 ripley 27
#endif
28
 
11510 ripley 29
#include <Defn.h>
30
#include <Graphics.h>
12778 pd 31
#include <Rdevices.h>
4394 ripley 32
#include <stdio.h>
33
#include "opt.h"
34
#include "graphapp/ga.h"
35
#include "graphapp/stdimg.h"
36
#include "console.h"
37
#include "rui.h"
38
#include "devga.h"		/* 'Public' routines from here */
5429 ripley 39
#include "windows.h"
4394 ripley 40
 
41
extern console RConsole;
11067 maechler 42
extern Rboolean AllDevicesKilled;
4394 ripley 43
 
24606 ripley 44
int graphicsx = -25, graphicsy = 0;
45
 
16137 ripley 46
/* a colour used to represent the background on png if transparent
18312 ripley 47
   NB: used as RGB and BGR
16137 ripley 48
*/
18312 ripley 49
#define PNG_TRANS 0xd6d3d6
16137 ripley 50
 
13634 ripley 51
/* these really are globals: per machine, not per window */
52
static double user_xpinch = 0.0, user_ypinch = 0.0;
6430 guido 53
 
13634 ripley 54
void GAsetunits(double xpinch, double ypinch)
55
{
56
    user_xpinch = xpinch;
57
    user_ypinch = ypinch;
58
}
59
 
15641 ripley 60
static rgb GArgb(int color, double gamma)
61
{
62
    int r, g, b;
63
    if (gamma != 1) {
64
	r = (int) (255 * pow(R_RED(color) / 255.0, gamma));
65
	g = (int) (255 * pow(R_GREEN(color) / 255.0, gamma));
66
	b = (int) (255 * pow(R_BLUE(color) / 255.0, gamma));
67
    } else {
68
	r = R_RED(color);
69
	g = R_GREEN(color);
70
	b = R_BLUE(color);
71
    }
72
    return rgb(r, g, b);
73
}
13634 ripley 74
 
75
 
15641 ripley 76
 
4394 ripley 77
	/********************************************************/
78
	/* This device driver has been documented so that it be	*/
79
	/* used as a template for new drivers 			*/
80
	/********************************************************/
81
 
82
#define MM_PER_INCH	25.4	/* mm -> inch conversion */
83
 
6192 maechler 84
#define TRACEDEVGA(a)
24610 ripley 85
#define CLIP if (xd->clip.width>0) gsetcliprect(_d,xd->clip)
86
 
87
static drawing _d;
26109 ripley 88
 
89
#define DRAW(a) {if(xd->kind != SCREEN) {_d=xd->gawin; CLIP; a;} else {_d=xd->bm; CLIP; a; if(!xd->buffered) {_d=xd->gawin; CLIP; a;} _d=xd->bm; CLIP; a;}}
90
 
26125 murdoch 91
#define SHOW  if(xd->kind==SCREEN) {gbitblt(xd->gawin,xd->bm,pt(0,0),getrect(xd->bm));GALastUpdate=GetTickCount();}
26109 ripley 92
#define SH if(xd->kind==SCREEN && xd->buffered) GA_Timer(xd)
4394 ripley 93
 
24610 ripley 94
 
12256 pd 95
#define SF 20  /* scrollbar resolution */
96
 
4394 ripley 97
        /********************************************************/
98
	/* Each driver can have its own device-specic graphical */
99
	/* parameters and resources.  these should be wrapped	*/
8971 ripley 100
	/* in a structure (like the gadesc structure below)    */
4394 ripley 101
	/* and attached to the overall device description via 	*/
102
	/* the dd->deviceSpecific pointer			*/
103
	/* NOTE that there are generic graphical parameters	*/
104
	/* which must be set by the device driver, but are	*/
105
	/* common to all device types (see Graphics.h)		*/
106
	/* so go in the GPar structure rather than this device- */
107
	/* specific structure					*/
108
	/********************************************************/
109
 
6458 guido 110
enum DeviceKinds {SCREEN=0, PRINTER, METAFILE, PNG, JPEG, BMP};
6430 guido 111
 
4394 ripley 112
typedef struct {
113
    /* R Graphics Parameters */
114
    /* local device copy so that we can detect */
115
    /* when parameter changes */
6625 guido 116
    int   col;			   /* Color */
117
    int   bg;			   /* Background */
118
    int   fontface;		   /* Typeface */
119
    int   fontsize, basefontsize;  /* Size in points */
4394 ripley 120
    double fontangle;
121
 
15641 ripley 122
    /* devga Driver Specific */
123
    /* parameters with copy per devga device */
4394 ripley 124
 
6430 guido 125
    enum DeviceKinds kind;
4394 ripley 126
    int   windowWidth;		/* Window width (pixels) */
127
    int   windowHeight;		/* Window height (pixels) */
11597 ripley 128
    int   showWidth;		/* device width (pixels) */
129
    int   showHeight;		/* device height (pixels) */
130
    int   origWidth, origHeight, xshift, yshift;
11067 maechler 131
    Rboolean resize;		/* Window resized */
4394 ripley 132
    window gawin;		/* Graphics window */
6430 guido 133
  /*FIXME: we should have union for this stuff and
134
    maybe change gawin to canvas*/
135
  /* SCREEN section*/
4605 ripley 136
    popup locpopup, grpopup;
137
    button  stoploc;
138
    menubar mbar, mbarloc;
4394 ripley 139
    menu  msubsave;
6458 guido 140
    menuitem mpng, mbmp, mjpeg50, mjpeg75, mjpeg100;
14461 ripley 141
    menuitem mps, mpdf, mwm, mclpbm, mclpwm, mprint, mclose;
4394 ripley 142
    menuitem mrec, madd, mreplace, mprev, mnext, mclear, msvar, mgvar;
27686 murdoch 143
    menuitem mR, mfit, mfix, grmenustayontop;
11067 maechler 144
    Rboolean recording, replaying, needsave;
4394 ripley 145
    bitmap bm;
6430 guido 146
  /* PNG and JPEG section */
147
    FILE *fp;
21024 ripley 148
    char filename[512];
6430 guido 149
    int quality;
21024 ripley 150
    int npage;
151
    double w, h;
8971 ripley 152
  /* Used to rescale font size so that bitmap devices have 72dpi */
22270 ripley 153
    int truedpi, wanteddpi;
4394 ripley 154
    rgb   fgcolor;		/* Foreground color */
155
    rgb   bgcolor;		/* Background color */
15641 ripley 156
    rgb   canvascolor;		/* Canvas color */
157
    rgb   outcolor;		/* Outside canvas color */
4394 ripley 158
    rect  clip;			/* The clipping rectangle */
11067 maechler 159
    Rboolean usefixed;
4394 ripley 160
    font  fixedfont;
161
    font  font;
11067 maechler 162
    Rboolean locator;
163
    int clicked; /* {0,1,2} */
164
    int	px, py, lty, lwd;
11597 ripley 165
    int resizing; /* {1,2,3} */
166
    double rescale_factor;
12778 pd 167
    int fast; /* Use fast fixed-width lines? */
18312 ripley 168
    unsigned int pngtrans; /* what PNG_TRANS get mapped to */
26109 ripley 169
    Rboolean buffered;
170
    int timeafter, timesince;
27526 ripley 171
    SEXP psenv;
11067 maechler 172
} gadesc;
4394 ripley 173
 
11597 ripley 174
rect getregion(gadesc *xd)
175
{
176
    rect r = getrect(xd->bm);
177
    r.x += max(0, xd->xshift);
178
    r.y += max(0, xd->yshift);
179
    r.width = min(r.width, xd->showWidth);
180
    r.height = min(r.height, xd->showHeight);
181
    return r;
182
}
4394 ripley 183
 
24610 ripley 184
/* Update the screen 100ms after last plotting call or 500ms after last
185
   update */
186
 
187
static UINT TimerNo = 0;
188
static gadesc *GA_xd;
189
static DWORD GALastUpdate = 0;
190
 
191
static VOID CALLBACK
192
GA_timer_proc(HWND hwnd, UINT message, UINT tid, DWORD time)
193
{
27143 ripley 194
    if ((message != WM_TIMER) || tid != TimerNo || !GA_xd) return;
24610 ripley 195
    gbitblt(GA_xd->gawin, GA_xd->bm, pt(0,0), getrect(GA_xd->bm));
196
    GALastUpdate = time;
197
}
198
 
199
static void GA_Timer(gadesc *xd)
200
{
201
    DWORD now = GetTickCount();
202
    if(TimerNo != 0) KillTimer(0, TimerNo);
26109 ripley 203
    if(now > GALastUpdate + xd->timesince) {
24610 ripley 204
	gbitblt(xd->gawin, xd->bm, pt(0,0), getrect(xd->bm));
205
	GALastUpdate = now;
206
    } else {
207
	GA_xd = xd;
26109 ripley 208
	TimerNo = SetTimer(0, 0, (UINT) xd->timeafter, GA_timer_proc);
24610 ripley 209
    }
210
}
211
 
4394 ripley 212
	/********************************************************/
213
	/* There are a number of actions that every device 	*/
214
	/* driver is expected to perform (even if, in some	*/
215
	/* cases it does nothing - just so long as it doesn't 	*/
216
	/* crash !).  this is how the graphics engine interacts */
11597 ripley 217
	/* with each device. Each action will be documented 	*/
4394 ripley 218
	/* individually. 					*/
219
	/* hooks for these actions must be set up when the 	*/
220
	/* device is first created				*/
221
	/********************************************************/
222
 
223
	/* Device Driver Actions */
224
 
17050 ripley 225
static void GA_Activate(NewDevDesc *dd);
226
static void GA_Circle(double x, double y, double r,
27236 murrell 227
		      R_GE_gcontext *gc,
17050 ripley 228
		      NewDevDesc *dd);
22270 ripley 229
static void GA_Clip(double x0, double x1, double y0, double y1,
17050 ripley 230
		     NewDevDesc *dd);
231
static void GA_Close(NewDevDesc *dd);
232
static void GA_Deactivate(NewDevDesc *dd);
233
static void GA_Hold(NewDevDesc *dd);
234
static Rboolean GA_Locator(double *x, double *y, NewDevDesc *dd);
235
static void GA_Line(double x1, double y1, double x2, double y2,
27236 murrell 236
		    R_GE_gcontext *gc,
17050 ripley 237
		    NewDevDesc *dd);
27357 murdoch 238
static void GA_MetricInfo(int c,
27236 murrell 239
			  R_GE_gcontext *gc,
17050 ripley 240
			  double* ascent, double* descent,
241
			  double* width, NewDevDesc *dd);
242
static void GA_Mode(int mode, NewDevDesc *dd);
27236 murrell 243
static void GA_NewPage(R_GE_gcontext *gc,
244
		       NewDevDesc *dd);
22270 ripley 245
static void GA_Polygon(int n, double *x, double *y,
27236 murrell 246
		       R_GE_gcontext *gc,
17050 ripley 247
		       NewDevDesc *dd);
22270 ripley 248
static void GA_Polyline(int n, double *x, double *y,
27236 murrell 249
			R_GE_gcontext *gc,
17050 ripley 250
			NewDevDesc *dd);
251
static void GA_Rect(double x0, double y0, double x1, double y1,
27236 murrell 252
		    R_GE_gcontext *gc,
17050 ripley 253
		    NewDevDesc *dd);
254
static void GA_Size(double *left, double *right,
255
		    double *bottom, double *top,
256
		    NewDevDesc *dd);
257
static void GA_Resize(NewDevDesc *dd);
27357 murdoch 258
static double GA_StrWidth(char *str,
27236 murrell 259
			  R_GE_gcontext *gc,
260
			  NewDevDesc *dd);
22270 ripley 261
static void GA_Text(double x, double y, char *str,
262
		    double rot, double hadj,
27236 murrell 263
		    R_GE_gcontext *gc,
17050 ripley 264
		    NewDevDesc *dd);
22270 ripley 265
static Rboolean GA_Open(NewDevDesc*, gadesc*, char*, double, double,
24724 ripley 266
			Rboolean, int, int, double, int, int);
4394 ripley 267
 
268
	/********************************************************/
269
	/* end of list of required device driver actions 	*/
270
	/********************************************************/
271
 
272
	/* Support Routines */
273
 
274
static double pixelHeight(drawing  d);
275
static double pixelWidth(drawing d);
17165 murrell 276
static void SetColor(int, double, NewDevDesc *);
17050 ripley 277
static void SetFont(int, int, double, NewDevDesc *);
278
static void SetLinetype(int, double, NewDevDesc *);
6430 guido 279
static int Load_Rbitmap_Dll();
6648 ripley 280
void UnLoad_Rbitmap_Dll();
17050 ripley 281
static void SaveAsPng(NewDevDesc *dd, char *fn);
282
static void SaveAsJpeg(NewDevDesc *dd, int quality, char *fn);
283
static void SaveAsBmp(NewDevDesc *dd, char *fn);
284
static void SaveAsBitmap(NewDevDesc *dd);
4394 ripley 285
 
17050 ripley 286
static void PrivateCopyDevice(NewDevDesc *dd, NewDevDesc *ndd, char *name)
5341 guido 287
{
17050 ripley 288
    GEDevDesc* gdd;
22270 ripley 289
    int saveDev = curDevice();
8971 ripley 290
    gadesc *xd = (gadesc *) dd->deviceSpecific;
5341 guido 291
    gsetcursor(xd->gawin, WatchCursor);
292
    gsetVar(install(".Device"),
293
	    mkString(name), R_NilValue);
17050 ripley 294
    gdd = GEcreateDevDesc(ndd);
295
    addDevice((DevDesc*) gdd);
296
    GEcopyDisplayList(devNumber((DevDesc*) dd));
297
    KillDevice((DevDesc*) gdd);
22270 ripley 298
    selectDevice(saveDev);
17050 ripley 299
/*    KillDevice(GetDevice(devNumber((DevDesc*) ndd))); */
5341 guido 300
    gsetcursor(xd->gawin, ArrowCursor);
301
    show(xd->gawin);
302
}
303
 
17050 ripley 304
static void SaveAsWin(NewDevDesc *dd, char *display)
5341 guido 305
{
17050 ripley 306
    NewDevDesc *ndd = (NewDevDesc *) calloc(1, sizeof(NewDevDesc));
307
    GEDevDesc* gdd = (GEDevDesc*) GetDevice(devNumber((DevDesc*) dd));
5341 guido 308
    if (!ndd) {
24724 ripley 309
	R_ShowMessage("Not enough memory to copy graphics window");
5341 guido 310
	return;
311
    }
11700 ripley 312
    if(!R_CheckDeviceAvailableBool()) {
313
	free(ndd);
314
	R_ShowMessage("No device available to copy graphics window");
315
	return;
316
    }
22270 ripley 317
 
5341 guido 318
    ndd->displayList = R_NilValue;
8971 ripley 319
    if (GADeviceDriver(ndd, display,
22270 ripley 320
		       fromDeviceWidth(toDeviceWidth(1.0, GE_NDC, gdd),
17050 ripley 321
				       GE_INCHES, gdd),
22270 ripley 322
		       fromDeviceHeight(toDeviceHeight(-1.0, GE_NDC, gdd),
17050 ripley 323
					GE_INCHES, gdd),
20312 ripley 324
		       ((gadesc*) dd->deviceSpecific)->basefontsize,
27662 murdoch 325
		       0, 1, White, 1, NA_INTEGER, NA_INTEGER, FALSE,
27526 ripley 326
		       R_GlobalEnv))
8701 ripley 327
        PrivateCopyDevice(dd, ndd, display);
5341 guido 328
}
329
 
6192 maechler 330
 
17050 ripley 331
static void SaveAsPostscript(NewDevDesc *dd, char *fn)
5341 guido 332
{
27526 ripley 333
    SEXP s;
17050 ripley 334
    NewDevDesc *ndd = (NewDevDesc *) calloc(1, sizeof(NewDevDesc));
335
    GEDevDesc* gdd = (GEDevDesc*) GetDevice(devNumber((DevDesc*) dd));
27526 ripley 336
    gadesc *xd = (gadesc *) dd->deviceSpecific;
22270 ripley 337
    char family[256], encoding[256], paper[256], bg[256], fg[256],
13645 ripley 338
	**afmpaths = NULL;
5341 guido 339
 
8971 ripley 340
    if (!ndd) {
12778 pd 341
	R_ShowMessage("Not enough memory to copy graphics window");
5341 guido 342
	return;
8971 ripley 343
    }
11700 ripley 344
    if(!R_CheckDeviceAvailableBool()) {
345
	free(ndd);
346
	R_ShowMessage("No device available to copy graphics window");
347
	return;
348
    }
349
 
27662 murdoch 350
 
8971 ripley 351
    ndd->displayList = R_NilValue;
5341 guido 352
 
8971 ripley 353
    /* Set default values... */
354
    strcpy(family, "Helvetica");
13645 ripley 355
    strcpy(encoding, "ISOLatin1.enc");
8971 ripley 356
    strcpy(paper, "default");
15641 ripley 357
    strcpy(bg, "transparent");
8971 ripley 358
    strcpy(fg, "black");
359
    /* and then try to get it from .PostScript.Options */
27526 ripley 360
    s = findVar(install(".PostScript.Options"), xd->psenv);
361
    if ((s != R_UnboundValue) && (s != R_NilValue)) {
8971 ripley 362
	SEXP names = getAttrib(s, R_NamesSymbol);
363
	int i,done;
364
	for (i=0, done=0; (done<4) && (i<length(s)) ; i++) {
10172 luke 365
	    if(!strcmp("family", CHAR(STRING_ELT(names, i)))) {
366
		strcpy(family, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)));
8971 ripley 367
		done += 1;
368
	    }
10172 luke 369
	    if(!strcmp("paper", CHAR(STRING_ELT(names, i)))) {
370
		strcpy(paper, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)));
8971 ripley 371
		done += 1;
372
	    }
10172 luke 373
	    if(!strcmp("bg", CHAR(STRING_ELT(names, i)))) {
374
		strcpy(bg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)));
8971 ripley 375
		done += 1;
376
	    }
10172 luke 377
	    if(!strcmp("fg", CHAR(STRING_ELT(names, i)))) {
378
		strcpy(fg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)));
8971 ripley 379
		done += 1;
380
	    }
381
	}
382
    }
22270 ripley 383
    if (PSDeviceDriver((DevDesc *) ndd,
17050 ripley 384
		       fn, paper, family, afmpaths, encoding, bg, fg,
22270 ripley 385
		       fromDeviceWidth(toDeviceWidth(1.0, GE_NDC, gdd),
17050 ripley 386
				       GE_INCHES, gdd),
22270 ripley 387
		       fromDeviceHeight(toDeviceHeight(-1.0, GE_NDC, gdd),
17050 ripley 388
					GE_INCHES, gdd),
20312 ripley 389
		       (double)0, ((gadesc*) dd->deviceSpecific)->basefontsize,
22279 ripley 390
		       0, 1, 0, "", "R Graphics Output"))
8971 ripley 391
	/* horizontal=F, onefile=F, pagecentre=T, print.it=F */
392
	PrivateCopyDevice(dd, ndd, "postscript");
5341 guido 393
}
394
 
395
 
17050 ripley 396
static void SaveAsPDF(NewDevDesc *dd, char *fn)
14461 ripley 397
{
27526 ripley 398
    SEXP s;
17050 ripley 399
    NewDevDesc *ndd = (NewDevDesc *) calloc(1, sizeof(NewDevDesc));
400
    GEDevDesc* gdd = (GEDevDesc*) GetDevice(devNumber((DevDesc*) dd));
27526 ripley 401
    gadesc *xd = (gadesc *) dd->deviceSpecific;
14461 ripley 402
    char family[256], encoding[256], bg[256], fg[256];
5341 guido 403
 
14461 ripley 404
    if (!ndd) {
405
	R_ShowMessage("Not enough memory to copy graphics window");
406
	return;
407
    }
408
    if(!R_CheckDeviceAvailableBool()) {
409
	free(ndd);
410
	R_ShowMessage("No device available to copy graphics window");
411
	return;
412
    }
413
 
414
    ndd->displayList = R_NilValue;
415
 
416
    /* Set default values... */
27526 ripley 417
    s = findVar(install(".PostScript.Options"), xd->psenv);
14461 ripley 418
    strcpy(family, "Helvetica");
419
    strcpy(encoding, "ISOLatin1.enc");
15641 ripley 420
    strcpy(bg, "transparent");
14461 ripley 421
    strcpy(fg, "black");
422
    /* and then try to get it from .PostScript.Options */
27526 ripley 423
    if ((s != R_UnboundValue) && (s != R_NilValue)) {
14461 ripley 424
	SEXP names = getAttrib(s, R_NamesSymbol);
425
	int i,done;
426
	for (i=0, done=0; (done<4) && (i<length(s)) ; i++) {
427
	    if(!strcmp("family", CHAR(STRING_ELT(names, i)))) {
428
		strcpy(family, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)));
429
		done += 1;
430
	    }
431
	    if(!strcmp("bg", CHAR(STRING_ELT(names, i)))) {
432
		strcpy(bg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)));
433
		done += 1;
434
	    }
435
	    if(!strcmp("fg", CHAR(STRING_ELT(names, i)))) {
436
		strcpy(fg, CHAR(STRING_ELT(VECTOR_ELT(s, i), 0)));
437
		done += 1;
438
	    }
439
	}
440
    }
17050 ripley 441
    if (PDFDeviceDriver((DevDesc *) ndd, fn, family, encoding, bg, fg,
22270 ripley 442
			fromDeviceWidth(toDeviceWidth(1.0, GE_NDC, gdd),
17050 ripley 443
					GE_INCHES, gdd),
22270 ripley 444
			fromDeviceHeight(toDeviceHeight(-1.0, GE_NDC, gdd),
17050 ripley 445
					 GE_INCHES, gdd),
27143 ripley 446
			((gadesc*) dd->deviceSpecific)->basefontsize,
22279 ripley 447
			1, "R Graphics Output"))
14461 ripley 448
	PrivateCopyDevice(dd, ndd, "PDF");
449
}
450
 
451
 
452
 
4394 ripley 453
			/* Pixel Dimensions (Inches) */
454
static double pixelWidth(drawing obj)
455
{
456
    return ((double) 1) / devicepixelsx(obj);
457
}
458
 
459
static double pixelHeight(drawing obj)
460
{
461
    return ((double) 1) / devicepixelsy(obj);
462
}
463
 
464
			/* Font information array. */
465
			/* Point sizes: 6-24 */
466
			/* Faces: plain, bold, oblique, bold-oblique */
467
			/* Symbol may be added later */
468
 
469
#define NFONT 19
470
#define MAXFONT 32
471
static int fontnum;
11067 maechler 472
static int fontinitdone = 0;/* in {0,1,2} */
4394 ripley 473
static char *fontname[MAXFONT];
474
static int fontstyle[MAXFONT];
475
 
476
static void RStandardFonts()
477
{
478
    int   i;
479
 
480
    for (i = 0; i < 4; i++)
481
	fontname[i] = "Times New Roman";
482
    fontname[4] = "Symbol";
483
    fontstyle[0] = fontstyle[4] = Plain;
484
    fontstyle[1] = Bold;
485
    fontstyle[2] = Italic;
486
    fontstyle[3] = BoldItalic;
487
    fontnum = 5;
10658 ripley 488
    fontinitdone = 2;		/* =fontinit done & fontname must not be
4394 ripley 489
				   free-ed */
490
}
491
 
492
 
493
static void RFontInit()
494
{
495
    int   i, notdone;
496
    char *opt[2];
497
    char  oops[256];
498
 
4626 ripley 499
    sprintf(oops, "%s/Rdevga", getenv("R_USER"));
4394 ripley 500
    notdone = 1;
501
    fontnum = 0;
502
    fontinitdone = 1;
503
    if (!optopenfile(oops)) {
4626 ripley 504
	sprintf(oops, "%s/etc/Rdevga", getenv("R_HOME"));
4394 ripley 505
	if (!optopenfile(oops)) {
506
	    RStandardFonts();
507
	    notdone = 0;
508
	}
509
    }
510
    while (notdone) {
511
	oops[0] = '\0';
512
	notdone = optread(opt, ':');
513
	if (notdone == 1)
514
	    sprintf(oops, "[%s] Error at line %d.", optfile(), optline());
515
	else if (notdone == 2) {
11169 ripley 516
	    fontname[fontnum] = strdup(opt[0]);
4394 ripley 517
	    if (!fontname[fontnum])
518
		strcpy(oops, "Insufficient memory. ");
519
	    else {
520
		if (!strcmpi(opt[1], "plain"))
521
		    fontstyle[fontnum] = Plain;
522
		else if (!strcmpi(opt[1], "bold"))
523
		    fontstyle[fontnum] = Bold;
524
		else if (!strcmpi(opt[1], "italic"))
525
		    fontstyle[fontnum] = Italic;
526
		else if (!strcmpi(opt[1], "bold&italic"))
527
		    fontstyle[fontnum] = BoldItalic;
528
		else
529
		    sprintf(oops, "Unknown style at line %d. ", optline());
530
		fontnum += 1;
531
	    }
532
	}
533
	if (oops[0]) {
534
	    optclosefile();
535
	    strcat(oops, optfile());
536
	    strcat(oops, " will be ignored.");
4649 ripley 537
	    R_ShowMessage(oops);
26905 ripley 538
	    for (i = 0; i < fontnum; i++) free(fontname[i]);
4394 ripley 539
	    RStandardFonts();
540
	    notdone = 0;
541
	}
542
	if (fontnum == MAXFONT) {
543
	    optclosefile();
544
	    notdone = 0;
545
	}
546
    }
547
}
548
 
549
 
550
 
8971 ripley 551
static int SetBaseFont(gadesc *xd)
4394 ripley 552
{
553
    xd->fontface = 1;
6625 guido 554
    xd->fontsize = xd->basefontsize;
4394 ripley 555
    xd->fontangle = 0.0;
12778 pd 556
    xd->usefixed = FALSE;
22270 ripley 557
    xd->font = gnewfont(xd->gawin, fontname[0], fontstyle[0],
8971 ripley 558
			MulDiv(xd->fontsize, xd->wanteddpi, xd->truedpi), 0.0);
4394 ripley 559
    if (!xd->font) {
11067 maechler 560
	xd->usefixed= TRUE;
4394 ripley 561
	xd->font = xd->fixedfont = FixedFont;
562
	if (!xd->fixedfont)
563
	    return 0;
564
    }
565
    return 1;
566
}
567
 
568
 
569
 
6625 guido 570
/* Set the font size and face */
6430 guido 571
/* If the font of this size and at that the specified */
572
/* rotation is not present it is loaded. */
573
/* 0 = plain text, 1 = bold */
574
/* 2 = oblique, 3 = bold-oblique */
4394 ripley 575
 
10658 ripley 576
#define SMALLEST 2
12256 pd 577
#define LARGEST 100
4394 ripley 578
 
17050 ripley 579
static void SetFont(int face, int size, double rot, NewDevDesc *dd)
4394 ripley 580
{
8971 ripley 581
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 582
 
583
    if (face < 1 || face > fontnum)
584
	face = 1;
12778 pd 585
    if (size < SMALLEST) size = SMALLEST;
586
    if (size > LARGEST) size = LARGEST;
11597 ripley 587
    size = MulDiv(size, xd->wanteddpi, xd->truedpi);
4394 ripley 588
    if (!xd->usefixed &&
589
	(size != xd->fontsize || face != xd->fontface ||
590
	 rot != xd->fontangle)) {
4605 ripley 591
	 del(xd->font); doevent();
592
	 xd->font = gnewfont(xd->gawin,
4394 ripley 593
			    fontname[face - 1], fontstyle[face - 1],
594
			    size, rot);
595
	if (xd->font) {
596
	    xd->fontface = face;
597
	    xd->fontsize = size;
598
	    xd->fontangle = rot;
599
	} else {
600
	    SetBaseFont(xd);
601
	}
602
    }
603
}
604
 
605
 
17165 murrell 606
static void SetColor(int color, double gamma, NewDevDesc *dd)
4394 ripley 607
{
8971 ripley 608
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 609
 
610
    if (color != xd->col) {
611
	xd->col = color;
17165 murrell 612
	xd->fgcolor = GArgb(color, gamma);
4394 ripley 613
    }
614
}
615
 
616
 
617
/*
618
 *	Some Notes on Line Textures
619
 *
620
 *	Line textures are stored as an array of 4-bit integers within
621
 *	a single 32-bit word.  These integers contain the lengths of
622
 *	lines to be drawn with the pen alternately down and then up.
623
 *	The device should try to arrange that these values are measured
624
 *	in points if possible, although pixels is ok on most displays.
625
 *
626
 *	If newlty contains a line texture description it is decoded
627
 *	as follows:
628
 *
629
 *		ndash = 0;
630
 *		for(i=0 ; i<8 && newlty&15 ; i++) {
631
 *			dashlist[ndash++] = newlty&15;
632
 *			newlty = newlty>>4;
633
 *		}
634
 *		dashlist[0] = length of pen-down segment
635
 *		dashlist[1] = length of pen-up segment
636
 *		etc
637
 *
638
 *	An integer containing a zero terminates the pattern.  Hence
639
 *	ndash in this code fragment gives the length of the texture
640
 *	description.  If a description contains an odd number of
641
 *	elements it is replicated to create a pattern with an
642
 *	even number of elements.  (If this is a pain, do something
643
 *	different its not crucial).
644
 *
645
 *	27/5/98 Paul - change to allow lty and lwd to interact:
646
 *	the line texture is now scaled by the line width so that,
647
 *	for example, a wide (lwd=2) dotted line (lty=2) has bigger
648
 *	dots which are more widely spaced.  Previously, such a line
649
 *	would have "dots" which were wide, but not long, nor widely
650
 *	spaced.
651
 */
652
 
17050 ripley 653
static void SetLinetype(int newlty, double nlwd, NewDevDesc *dd)
4394 ripley 654
{
8971 ripley 655
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 656
    int   newlwd;
657
 
658
    newlwd = nlwd;
659
    if (newlwd < 1)
660
	newlwd = 1;
661
    xd->lty = newlty;
662
    xd->lwd = newlwd;
663
}
664
 
665
 
666
 
667
/* Callback functions */
668
 
669
 
12256 pd 670
static void HelpResize(window w, rect r)
4394 ripley 671
{
672
    if (AllDevicesKilled) return;
673
    {
17050 ripley 674
	NewDevDesc *dd = (NewDevDesc *) getdata(w);
8971 ripley 675
	gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 676
 
677
	if (r.width) {
678
	    if ((xd->windowWidth != r.width) ||
679
		((xd->windowHeight != r.height))) {
680
		xd->windowWidth = r.width;
681
		xd->windowHeight = r.height;
12256 pd 682
		xd->resize = TRUE;
4394 ripley 683
	    }
684
	}
685
    }
686
}
687
 
688
static void HelpClose(window w)
689
{
690
    if (AllDevicesKilled) return;
691
    {
17050 ripley 692
	NewDevDesc *dd = (NewDevDesc *) getdata(w);
693
	KillDevice(GetDevice(devNumber((DevDesc*) dd)));
4394 ripley 694
    }
695
}
696
 
11597 ripley 697
static void HelpExpose(window w, rect r)
4394 ripley 698
{
699
    if (AllDevicesKilled) return;
700
    {
17050 ripley 701
	NewDevDesc *dd = (NewDevDesc *) getdata(w);
702
	GEDevDesc* gdd = (GEDevDesc*) GetDevice(devNumber((DevDesc*) dd));
8971 ripley 703
	gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 704
 
705
	if (xd->resize) {
17050 ripley 706
	    GA_Resize(dd);
11597 ripley 707
	    xd->replaying = TRUE;
17125 ripley 708
	    GEplayDisplayList(gdd);
11597 ripley 709
	    xd->replaying = FALSE;
7824 ripley 710
	    R_ProcessEvents();
4394 ripley 711
	} else
712
	    SHOW;
713
    }
714
}
715
 
4605 ripley 716
static void HelpMouseClick(window w, int button, point pt)
4394 ripley 717
{
718
    if (AllDevicesKilled) return;
719
    {
17050 ripley 720
	NewDevDesc *dd = (NewDevDesc *) getdata(w);
8971 ripley 721
	gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 722
 
723
	if (!xd->locator)
724
	    return;
725
	if (button & LeftButton) {
27143 ripley 726
	    int useBeep = asLogical(GetOption(install("locatorBell"),
24345 ripley 727
					      R_NilValue));
728
	    if(useBeep) gabeep();
4394 ripley 729
	    xd->clicked = 1;
730
	    xd->px = pt.x;
731
	    xd->py = pt.y;
732
	} else
733
	    xd->clicked = 2;
734
    }
735
}
736
 
6192 maechler 737
static void menustop(control m)
4605 ripley 738
{
17050 ripley 739
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
8971 ripley 740
    gadesc *xd = (gadesc *) dd->deviceSpecific;
741
    if (!xd->locator)
742
	return;
743
    xd->clicked = 2;
4605 ripley 744
}
745
 
4394 ripley 746
void  fixslash(char *);
747
 
6430 guido 748
static void menufilebitmap(control m)
4394 ripley 749
{
17050 ripley 750
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
8971 ripley 751
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 752
    char *fn;
6430 guido 753
    if (m==xd->mpng) {
754
      setuserfilter("Png files (*.png)\0*.png\0\0");
6458 guido 755
      fn = askfilesave("Portable network graphics file", "");
756
    } else if (m==xd->mbmp) {
757
      setuserfilter("Windows bitmap files (*.bmp)\0*.bmp\0\0");
22270 ripley 758
      fn = askfilesave("Windows bitmap file", "");
759
    } else {
6430 guido 760
      setuserfilter("Jpeg files (*.jpeg,*jpg)\0*.jpeg;*.jpg\0\0");
761
      fn = askfilesave("Jpeg file", "");
762
    }
4394 ripley 763
    if (!fn) return;
764
    fixslash(fn);
5213 ripley 765
    gsetcursor(xd->gawin, WatchCursor);
4394 ripley 766
    show(xd->gawin);
8701 ripley 767
    if (m==xd->mpng) SaveAsPng(dd, fn);
768
    else if (m==xd->mbmp) SaveAsBmp(dd, fn);
769
    else if (m==xd->mjpeg50) SaveAsJpeg(dd, 50, fn);
770
    else if (m==xd->mjpeg75) SaveAsJpeg(dd, 75, fn);
771
    else SaveAsJpeg(dd, 100, fn);
5213 ripley 772
    gsetcursor(xd->gawin, ArrowCursor);
773
    show(xd->gawin);
4394 ripley 774
}
775
 
776
 
777
static void menups(control m)
778
{
17050 ripley 779
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
5341 guido 780
    char  *fn;
4394 ripley 781
 
782
    setuserfilter("Postscript files (*.ps)\0*.ps\0All files (*.*)\0*.*\0\0");
783
    fn = askfilesave("Postscript file", "");
784
    if (!fn) return;
785
    fixslash(fn);
14461 ripley 786
    SaveAsPostscript(dd, fn);
4394 ripley 787
}
788
 
5341 guido 789
 
14461 ripley 790
static void menupdf(control m)
791
{
17050 ripley 792
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
14461 ripley 793
    char  *fn;
794
 
795
    setuserfilter("PDF files (*.pdf)\0*.pdf\0All files (*.*)\0*.*\0\0");
796
    fn = askfilesave("PDF file", "");
797
    if (!fn) return;
798
    fixslash(fn);
799
    SaveAsPDF(dd, fn);
800
}
801
 
802
 
4394 ripley 803
static void menuwm(control m)
804
{
17050 ripley 805
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
25312 ripley 806
    char  display[550], *fn;
4394 ripley 807
 
808
    setuserfilter("Enhanced metafiles (*.emf)\0*.emf\0All files (*.*)\0*.*\0\0");
809
    fn = askfilesave("Enhanced metafiles", "");
810
    if (!fn) return;
811
    fixslash(fn);
25312 ripley 812
    if(strlen(fn) > 512) {
813
	askok("file path selected is too long: only 512 bytes are allowed");
814
	return;
815
    }
5341 guido 816
    sprintf(display, "win.metafile:%s", fn);
14461 ripley 817
    SaveAsWin(dd, display);
4394 ripley 818
}
819
 
820
 
821
static void menuclpwm(control m)
822
{
17050 ripley 823
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
14461 ripley 824
    SaveAsWin(dd, "win.metafile");
4394 ripley 825
}
826
 
827
static void menuclpbm(control m)
828
{
17050 ripley 829
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
8971 ripley 830
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 831
 
832
    show(xd->gawin);
833
    gsetcursor(xd->gawin, WatchCursor);
834
    copytoclipboard(xd->bm);
835
    gsetcursor(xd->gawin, ArrowCursor);
836
}
837
 
27662 murdoch 838
static void menustayontop(control m)
839
{
840
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
841
    gadesc *xd = (gadesc *) dd->deviceSpecific;
842
 
27686 murdoch 843
    BringToTop(xd->gawin, 2);
27662 murdoch 844
}
845
 
4394 ripley 846
static void menuprint(control m)
847
{
17050 ripley 848
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
19596 ripley 849
    SaveAsWin(dd, "win.print:");
4394 ripley 850
}
851
 
852
static void menuclose(control m)
853
{
17050 ripley 854
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
8971 ripley 855
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 856
 
857
    HelpClose(xd->gawin);
858
}
859
 
27686 murdoch 860
static void grpopupact(control m)
861
{
862
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
863
    gadesc *xd = (gadesc *) dd->deviceSpecific;
864
 
865
    if (ismdi())
866
    	disable(xd->grmenustayontop);
867
    else {
868
    	if (isTopmost(xd->gawin))
869
     	    check(xd->grmenustayontop);
870
    	else
871
    	    uncheck(xd->grmenustayontop);
872
    }
873
}
874
 
4394 ripley 875
/* plot history */
876
 
877
 
17050 ripley 878
#ifdef PLOTHISTORY
4394 ripley 879
 
24923 ripley 880
/* extern SEXP savedSnapshot;*/
4394 ripley 881
 
24923 ripley 882
/* NB: this puts .SavedPlots in .GlobalEnv */
4394 ripley 883
#define GROWTH 4
22471 ripley 884
#define GETDL SEXP vDL=findVar(install(".SavedPlots"), R_GlobalEnv)
885
#define SETDL defineVar(install(".SavedPlots"), vDL, R_GlobalEnv)
17125 ripley 886
/* altered in 1.4.0, as incompatible format */
887
#define PLOTHISTORYMAGIC 31416
10172 luke 888
#define pMAGIC      (INTEGER(VECTOR_ELT(vDL, 0))[0])
889
#define pNUMPLOTS   (INTEGER(VECTOR_ELT(vDL, 1))[0])
890
#define pMAXPLOTS   (INTEGER(VECTOR_ELT(vDL, 2))[0])
891
#define pCURRENTPOS (INTEGER(VECTOR_ELT(vDL, 3))[0])
892
#define pHISTORY    (VECTOR_ELT(vDL, 4))
893
#define SET_pHISTORY(v)    (SET_VECTOR_ELT(vDL, 4, v))
894
#define pCURRENT    (VECTOR_ELT(pHISTORY, pCURRENTPOS))
895
#define pCURRENTdl  (VECTOR_ELT(pCURRENT, 0))
896
#define pCURRENTgp  (INTEGER(VECTOR_ELT(pCURRENT, 1)))
17050 ripley 897
#define pCURRENTsnapshot (VECTOR_ELT(pCURRENT, 0))
4394 ripley 898
#define pCHECK      if ((TYPEOF(vDL)!=VECSXP)||\
10172 luke 899
                       (TYPEOF(VECTOR_ELT(vDL, 0))!=INTSXP) ||\
900
                       (LENGTH(VECTOR_ELT(vDL, 0))!=1) ||\
4394 ripley 901
                       (pMAGIC != PLOTHISTORYMAGIC)) {\
4649 ripley 902
                       R_ShowMessage("Plot history seems corrupted");\
4394 ripley 903
                       return;}
904
#define pMOVE(a) {pCURRENTPOS+=a;\
905
                  if(pCURRENTPOS<0) pCURRENTPOS=0;\
906
                  if(pCURRENTPOS>pNUMPLOTS-1) pCURRENTPOS=pNUMPLOTS-1;\
907
                  Replay(dd,vDL);SETDL;}
908
#define pEXIST ((vDL!=R_UnboundValue) && (vDL!=R_NilValue))
4649 ripley 909
#define pMUSTEXIST if(!pEXIST){R_ShowMessage("No plot history!");return;}
4394 ripley 910
 
911
 
912
 
913
 
914
static SEXP NewPlotHistory(int n)
915
{
17125 ripley 916
    SEXP  vDL, class;
4394 ripley 917
    int   i;
918
 
919
    PROTECT(vDL = allocVector(VECSXP, 5));
920
    for (i = 0; i < 4; i++)
10172 luke 921
	PROTECT(SET_VECTOR_ELT(vDL, i, allocVector(INTSXP, 1)));
922
    PROTECT(SET_pHISTORY (allocVector(VECSXP, n)));
4394 ripley 923
    pMAGIC = PLOTHISTORYMAGIC;
924
    pNUMPLOTS = 0;
925
    pMAXPLOTS = n;
926
    pCURRENTPOS = -1;
927
    for (i = 0; i < n; i++)
10172 luke 928
	SET_VECTOR_ELT(pHISTORY, i, R_NilValue);
17125 ripley 929
    PROTECT(class = allocVector(STRSXP, 1));
930
    SET_STRING_ELT(class, 0, mkChar("SavedPlots"));
22270 ripley 931
    classgets(vDL, class);
4394 ripley 932
    SETDL;
17125 ripley 933
    UNPROTECT(7);
4394 ripley 934
    return vDL;
935
}
936
 
937
static SEXP GrowthPlotHistory(SEXP vDL)
938
{
939
    SEXP  vOLD;
940
    int   i, oldNPlots, oldCurrent;
941
 
942
    PROTECT(vOLD = pHISTORY);
943
    oldNPlots = pNUMPLOTS;
944
    oldCurrent = pCURRENTPOS;
945
    PROTECT(vDL = NewPlotHistory(pMAXPLOTS + GROWTH));
946
    for (i = 0; i < oldNPlots; i++)
10172 luke 947
	SET_VECTOR_ELT(pHISTORY, i, VECTOR_ELT(vOLD, i));
4394 ripley 948
    pNUMPLOTS = oldNPlots;
949
    pCURRENTPOS = oldCurrent;
950
    SETDL;
951
    UNPROTECT(2);
952
    return vDL;
953
}
954
 
17050 ripley 955
static void AddtoPlotHistory(SEXP snapshot, int replace)
4394 ripley 956
{
957
    int   where;
17132 ripley 958
    SEXP  class;
4394 ripley 959
 
960
    GETDL;
17050 ripley 961
/*    if (dl == R_NilValue) {
5213 ripley 962
	R_ShowMessage("Display list is void!");
4394 ripley 963
	return;
17050 ripley 964
	} */
4394 ripley 965
    if (!pEXIST)
966
	vDL = NewPlotHistory(GROWTH);
967
    else if (!replace && (pNUMPLOTS == pMAXPLOTS))
968
	vDL = GrowthPlotHistory(vDL);
969
    PROTECT(vDL);
970
    pCHECK;
971
    if (replace)
972
	where = pCURRENTPOS;
973
    else
974
	where = pNUMPLOTS;
17050 ripley 975
    PROTECT(snapshot);
17132 ripley 976
    PROTECT(class = allocVector(STRSXP, 1));
977
    SET_STRING_ELT(class, 0, mkChar("recordedplot"));
22270 ripley 978
    classgets(snapshot, class);
17050 ripley 979
    SET_VECTOR_ELT(pHISTORY, where, snapshot);
4394 ripley 980
    pCURRENTPOS = where;
981
    if (!replace) pNUMPLOTS += 1;
982
    SETDL;
17132 ripley 983
    UNPROTECT(3);
4394 ripley 984
}
985
 
986
 
17050 ripley 987
static void Replay(NewDevDesc *dd, SEXP vDL)
4394 ripley 988
{
17050 ripley 989
    GEDevDesc *gdd = (GEDevDesc *) GetDevice(devNumber((DevDesc*) dd));
8971 ripley 990
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 991
 
17050 ripley 992
    xd->replaying = TRUE;
4394 ripley 993
    gsetcursor(xd->gawin, WatchCursor);
17050 ripley 994
    GEplaySnapshot(pCURRENT, gdd);
995
    xd->replaying = FALSE;
4394 ripley 996
    gsetcursor(xd->gawin, ArrowCursor);
997
}
998
 
999
static void menurec(control m)
1000
{
17050 ripley 1001
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
8971 ripley 1002
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1003
 
1004
    if (xd->recording) {
17050 ripley 1005
	xd->recording = FALSE;
4394 ripley 1006
	uncheck(m);
1007
    } else {
17050 ripley 1008
	xd->recording = TRUE;
4394 ripley 1009
	check(m);
1010
    }
1011
}
1012
 
1013
 
1014
static void menuadd(control m)
1015
{
17050 ripley 1016
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
1017
    GEDevDesc *gdd = (GEDevDesc *) GetDevice(devNumber((DevDesc*) dd));
8971 ripley 1018
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1019
 
17050 ripley 1020
    AddtoPlotHistory(GEcreateSnapshot(gdd), 0);
1021
    xd->needsave = FALSE;
4394 ripley 1022
}
1023
 
1024
static void menureplace(control m)
1025
{
17050 ripley 1026
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
1027
    GEDevDesc *gdd = (GEDevDesc *) GetDevice(devNumber((DevDesc*) dd));
4394 ripley 1028
 
1029
    GETDL;
1030
    pMUSTEXIST;
1031
    pCHECK;
1032
    if (pCURRENTPOS < 0) {
4649 ripley 1033
	R_ShowMessage("No plot to replace!");
4394 ripley 1034
	return;
1035
    }
17050 ripley 1036
    AddtoPlotHistory(GEcreateSnapshot(gdd), 1);
4394 ripley 1037
}
1038
 
1039
static void menunext(control m)
1040
{
17050 ripley 1041
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
8971 ripley 1042
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1043
 
1044
    GETDL;
1045
    if (xd->needsave) return;
1046
    pMUSTEXIST;
1047
    pCHECK;
17050 ripley 1048
    if (pCURRENTPOS != (pNUMPLOTS - 1)) pMOVE(1);
4394 ripley 1049
}
1050
 
1051
static void menuprev(control m)
1052
{
17050 ripley 1053
    NewDevDesc *dd = (NewDevDesc*) getdata(m);
1054
    GEDevDesc *gdd = (GEDevDesc *) GetDevice(devNumber((DevDesc*) dd));
8971 ripley 1055
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1056
 
1057
    GETDL;
1058
    pMUSTEXIST;
1059
    pCHECK;
1060
    if (pNUMPLOTS) {
1061
	if (xd->recording && xd->needsave && (dd->displayList != R_NilValue)) {
17050 ripley 1062
	    AddtoPlotHistory(GEcreateSnapshot(gdd), 0);
1063
	    xd->needsave = FALSE;
4394 ripley 1064
	}
1065
	pMOVE((xd->needsave) ? 0 : -1);
1066
    }
1067
}
1068
 
1069
static void menuclear(control m)
1070
{
22471 ripley 1071
    defineVar(install(".SavedPlots"), R_NilValue, R_GlobalEnv);
4394 ripley 1072
}
1073
 
1074
static void menugvar(control m)
1075
{
1076
    SEXP  vDL;
17050 ripley 1077
    char *v = askstring("Variable name", "");
1078
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
4394 ripley 1079
 
1080
    if (!v)
1081
	return;
1082
    vDL = findVar(install(v), R_GlobalEnv);
1083
    if (!pEXIST || !pNUMPLOTS) {
4649 ripley 1084
	R_ShowMessage("Variable doesn't exist or doesn't contain any plots!");
4394 ripley 1085
	return;
1086
    }
1087
    pCHECK;
1088
    pCURRENTPOS = 0;
1089
    Replay(dd, vDL);
1090
    SETDL;
1091
}
1092
 
1093
static void menusvar(control m)
1094
{
1095
    char *v;
1096
 
1097
    GETDL;
1098
    pMUSTEXIST;
1099
    pCHECK;
17050 ripley 1100
    v = askstring("Name of variable to save to", "");
4394 ripley 1101
    if (!v)
1102
	return;
22471 ripley 1103
    defineVar(install(v), vDL, R_GlobalEnv);
4394 ripley 1104
}
17050 ripley 1105
#endif
4394 ripley 1106
 
1107
static void menuconsole(control m)
1108
{
1109
   show(RConsole);
1110
}
1111
 
11597 ripley 1112
static void menuR(control m)
1113
{
17050 ripley 1114
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
11597 ripley 1115
    gadesc *xd = (gadesc *) dd->deviceSpecific;
1116
    check(xd->mR);
1117
    uncheck(xd->mfix);
1118
    uncheck(xd->mfit);
1119
    xd->resizing = 1;
19174 ripley 1120
    xd->resize = TRUE;
1121
    HelpExpose(m, getrect(xd->gawin));
11597 ripley 1122
}
1123
 
1124
static void menufit(control m)
1125
{
17050 ripley 1126
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
11597 ripley 1127
    gadesc *xd = (gadesc *) dd->deviceSpecific;
1128
 
1129
    uncheck(xd->mR);
1130
    check(xd->mfit);
1131
    uncheck(xd->mfix);
1132
    xd->resizing = 2;
19174 ripley 1133
    xd->resize = TRUE;
1134
    HelpExpose(m, getrect(xd->gawin));
11597 ripley 1135
}
1136
 
1137
static void menufix(control m)
1138
{
17050 ripley 1139
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
11597 ripley 1140
    gadesc *xd = (gadesc *) dd->deviceSpecific;
1141
 
1142
    uncheck(xd->mR);
1143
    uncheck(xd->mfit);
1144
    check(xd->mfix);
1145
    xd->resizing = 3;
19174 ripley 1146
    xd->resize = TRUE;
1147
    HelpExpose(m, getrect(xd->gawin));
11597 ripley 1148
}
1149
 
19174 ripley 1150
static void CHelpKeyIn(control w, int key)
4394 ripley 1151
{
17050 ripley 1152
#ifdef PLOTHISTORY
1153
    NewDevDesc *dd = (NewDevDesc *) getdata(w);
8971 ripley 1154
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1155
 
10981 guido 1156
    if (xd->replaying) return;
4394 ripley 1157
    switch (key) {
1158
      case INS:
1159
	menuadd(xd->madd);
1160
	break;
1161
      case PGUP:
1162
	menuprev(xd->mprev);
1163
	break;
1164
      case PGDN:
1165
	menunext(xd->mnext);
1166
	break;
1167
    }
17050 ripley 1168
#endif
4394 ripley 1169
}
1170
 
1171
static void NHelpKeyIn(control w,int key)
1172
{
17050 ripley 1173
    NewDevDesc *dd = (NewDevDesc *) getdata(w);
8971 ripley 1174
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1175
 
10981 guido 1176
    if (xd->replaying) return;
6994 pd 1177
    if (ggetkeystate() != CtrlKey)
4394 ripley 1178
	return;
1179
    key = 'A' + key - 1;
1180
    if (key == 'C')
1181
	menuclpbm(xd->mclpbm);
1182
    if (dd->displayList == R_NilValue)
1183
	return;
1184
    if (key == 'W')
1185
	menuclpwm(xd->mclpwm);
1186
    if (key == 'P')
1187
	menuprint(xd->mprint);
1188
}
1189
 
1190
static void mbarf(control m)
1191
{
17050 ripley 1192
#ifdef PLOTHISTORY
1193
    NewDevDesc *dd = (NewDevDesc *) getdata(m);
8971 ripley 1194
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1195
 
1196
    GETDL;
10981 guido 1197
    if (pEXIST && !xd->replaying) {
4394 ripley 1198
	enable(xd->mnext);
1199
	enable(xd->mprev);
1200
	if ((pCURRENTPOS >= 0) && (dd->displayList != R_NilValue))
1201
	    enable(xd->mreplace);
1202
	else
1203
	    disable(xd->mreplace);
1204
	enable(xd->msvar);
1205
	enable(xd->mclear);
1206
    } else {
1207
	disable(xd->mnext);
1208
	disable(xd->mprev);
1209
	disable(xd->mreplace);
1210
	disable(xd->msvar);
1211
	disable(xd->mclear);
1212
    }
10981 guido 1213
    if (!xd->replaying)
1214
	enable(xd->mgvar);
1215
    else
1216
	disable(xd->mgvar);
1217
    if ((dd->displayList != R_NilValue) && !xd->replaying) {
4394 ripley 1218
	enable(xd->madd);
1219
	enable(xd->mprint);
6430 guido 1220
	enable(xd->mpng);
6458 guido 1221
	enable(xd->mbmp);
6430 guido 1222
	enable(xd->mjpeg50);
1223
	enable(xd->mjpeg75);
1224
	enable(xd->mjpeg100);
4394 ripley 1225
	enable(xd->mwm);
1226
	enable(xd->mps);
14461 ripley 1227
	enable(xd->mpdf);
4394 ripley 1228
	enable(xd->mclpwm);
6458 guido 1229
	enable(xd->mclpbm);
4394 ripley 1230
    } else {
1231
	disable(xd->madd);
1232
	disable(xd->mprint);
1233
	disable(xd->msubsave);
6430 guido 1234
	disable(xd->mpng);
6458 guido 1235
	disable(xd->mbmp);
6430 guido 1236
	disable(xd->mjpeg50);
1237
	disable(xd->mjpeg75);
1238
	disable(xd->mjpeg100);
4394 ripley 1239
	disable(xd->mwm);
1240
	disable(xd->mps);
14461 ripley 1241
	disable(xd->mpdf);
4394 ripley 1242
	disable(xd->mclpwm);
6458 guido 1243
	disable(xd->mclpbm);
4394 ripley 1244
    }
1245
    draw(xd->mbar);
17050 ripley 1246
#endif
4394 ripley 1247
}
1248
 
1249
 
1250
        /********************************************************/
1251
	/* device_Open is not usually called directly by the 	*/
1252
	/* graphics engine;  it is usually only called from 	*/
1253
	/* the device-driver entry point.			*/
1254
	/* this function should set up all of the device-	*/
1255
	/* specific resources for a new device			*/
1256
	/* this function is given a new	structure for device-	*/
1257
	/* specific information AND it must FREE the structure 	*/
1258
	/* if anything goes seriously wrong			*/
1259
	/* NOTE that it is perfectly acceptable for this 	*/
1260
	/* function to set generic graphics parameters too	*/
1261
	/* (i.e., override the generic parameter settings	*/
1262
	/* which GInit sets up) all at the author's own risk	*/
1263
	/* of course :)						*/
1264
	/********************************************************/
1265
 
1266
#define MCHECK(m) {if(!(m)) {del(xd->gawin); return 0;}}
1267
 
11597 ripley 1268
static void devga_sbf(control c, int pos)
1269
{
17050 ripley 1270
    NewDevDesc *dd = (NewDevDesc *) getdata(c);
11597 ripley 1271
    gadesc *xd = (gadesc *) dd->deviceSpecific;
1272
    if (pos < 0) {
12256 pd 1273
	pos = -pos-1;
1274
	pos = min(pos*SF, (xd->origWidth - xd->windowWidth + SF-1));
11597 ripley 1275
	xd->xshift = -pos;
1276
    } else {
12256 pd 1277
	pos = min(pos*SF, (xd->origHeight - xd->windowHeight + SF-1));
11597 ripley 1278
	xd->yshift = -pos;
1279
    }
1280
    xd->resize = 1;
1281
    HelpExpose(c, getrect(xd->gawin));
1282
}
1283
 
1284
 
22270 ripley 1285
static int
1286
setupScreenDevice(NewDevDesc *dd, gadesc *xd, double w, double h,
24724 ripley 1287
		  Rboolean recording, int resize, int xpos, int ypos)
6430 guido 1288
{
8971 ripley 1289
    menu  m;
1290
    int   iw, ih;
27357 murdoch 1291
    int   cw, ch;
11597 ripley 1292
    double dw, dw0, dh, d;
4394 ripley 1293
 
8971 ripley 1294
    xd->kind = SCREEN;
13634 ripley 1295
    if (R_finite(user_xpinch) && user_xpinch > 0.0)
1296
	dw = dw0 = (int) (w * user_xpinch);
1297
    else
1298
	dw = dw0 = (int) (w / pixelWidth(NULL));
1299
    if (R_finite(user_ypinch) && user_ypinch > 0.0)
1300
	dh = (int) (w * user_ypinch);
1301
    else
1302
	dh = (int) (h / pixelHeight(NULL));
27357 murdoch 1303
 
1304
    if (ismdi()) {
1305
	cw = RgetMDIwidth();
1306
	ch = RgetMDIheight();
1307
    } else {
1308
	cw = devicewidth(NULL);
1309
	ch = deviceheight(NULL);
1310
    }
1311
 
12256 pd 1312
    if (resize != 3) {
27357 murdoch 1313
	if ((dw / cw) > 0.85) {
12256 pd 1314
	    d = dh / dw;
27357 murdoch 1315
	    dw = 0.85 * cw;
12256 pd 1316
	    dh = d * dw;
1317
	}
27357 murdoch 1318
	if ((dh / ch) > 0.85) {
12256 pd 1319
	    d = dw / dh;
27357 murdoch 1320
	    dh = 0.85 * ch;
12256 pd 1321
	    dw = d * dh;
1322
	}
1323
    } else {
27357 murdoch 1324
	dw = min(dw, 0.85*cw);
1325
	dh = min(dh, 0.85*ch);
8971 ripley 1326
    }
1327
    iw = dw + 0.5;
1328
    ih = dh + 0.5;
11597 ripley 1329
    if (resize == 2) xd->rescale_factor = dw/dw0;
24606 ripley 1330
    {
24724 ripley 1331
	int grx, gry;
1332
	grx = (xpos == NA_INTEGER) ? graphicsx : xpos;
1333
	gry = (ypos == NA_INTEGER) ? graphicsy : ypos;
27357 murdoch 1334
	if (grx < 0) grx = cw - iw + grx;
1335
	if (gry < 0) gry = ch - ih + gry;
24606 ripley 1336
	if (!(xd->gawin = newwindow("R Graphics",
1337
				    rect(grx, gry, iw, ih),
1338
				    Document | StandardWindow | Menubar |
1339
				    VScrollbar | HScrollbar)))
1340
	    return 0;
8971 ripley 1341
    }
12256 pd 1342
    gchangescrollbar(xd->gawin, VWINSB, 0, ih/SF-1, ih/SF, 0);
1343
    gchangescrollbar(xd->gawin, HWINSB, 0, iw/SF-1, iw/SF, 0);
6430 guido 1344
 
8971 ripley 1345
    addto(xd->gawin);
1346
    gsetcursor(xd->gawin, ArrowCursor);
1347
    if (ismdi() && (RguiMDI & RW_TOOLBAR)) {
1348
	int btsize = 24;
1349
	rect r = rect(2, 2, btsize, btsize);
1350
	control bt, tb;
22270 ripley 1351
 
8971 ripley 1352
	MCHECK(tb = newtoolbar(btsize + 4));
1353
	gsetcursor(tb, ArrowCursor);
1354
	addto(tb);
22270 ripley 1355
 
8971 ripley 1356
	MCHECK(bt = newtoolbutton(cam_image, r, menuclpwm));
1357
	MCHECK(addtooltip(bt, "Copy to the clipboard as a metafile"));
1358
	gsetcursor(bt, ArrowCursor);
1359
	setdata(bt, (void *) dd);
1360
	r.x += (btsize + 6);
22270 ripley 1361
 
8971 ripley 1362
	MCHECK(bt = newtoolbutton(print_image, r, menuprint));
1363
	MCHECK(addtooltip(bt, "Print"));
1364
	gsetcursor(bt, ArrowCursor);
1365
	setdata(bt, (void *) dd);
1366
	r.x += (btsize + 6);
6430 guido 1367
 
8971 ripley 1368
	MCHECK(bt = newtoolbutton(console_image, r, menuconsole));
1369
	MCHECK(addtooltip(bt, "Return focus to console"));
1370
	gsetcursor(bt, ArrowCursor);
1371
	setdata(bt, (void *) dd);
1372
	r.x += (btsize + 6);
6430 guido 1373
 
8971 ripley 1374
	MCHECK(xd->stoploc = newtoolbutton(stop_image, r, menustop));
1375
	MCHECK(addtooltip(xd->stoploc, "Stop locator"));
1376
	gsetcursor(bt, ArrowCursor);
1377
	setdata(xd->stoploc,(void *) dd);
1378
	hide(xd->stoploc);
1379
    } else
1380
	xd->stoploc = NULL;
6430 guido 1381
 
8971 ripley 1382
    /* First we prepare 'locator' menubar and popup */
1383
    addto(xd->gawin);
1384
    MCHECK(xd->mbarloc = newmenubar(NULL));
1385
    MCHECK(newmenu("Stop"));
1386
    MCHECK(m = newmenuitem("Stop locator", 0, menustop));
1387
    setdata(m, (void *) dd);
1388
    MCHECK(xd->locpopup = newpopup(NULL));
1389
    MCHECK(m = newmenuitem("Stop", 0, menustop));
1390
    setdata(m, (void *) dd);
1391
    MCHECK(newmenuitem("Continue", 0, NULL));
22270 ripley 1392
 
8971 ripley 1393
    /* Normal menubar */
1394
    MCHECK(xd->mbar = newmenubar(mbarf));
1395
    MCHECK(m = newmenu("File"));
1396
    MCHECK(xd->msubsave = newsubmenu(m, "Save as"));
17245 ripley 1397
    MCHECK(xd->mwm = newmenuitem("Metafile...", 0, menuwm));
1398
    MCHECK(xd->mps = newmenuitem("Postscript...", 0, menups));
1399
    MCHECK(xd->mpdf = newmenuitem("PDF...", 0, menupdf));
1400
    MCHECK(xd->mpng = newmenuitem("Png...", 0, menufilebitmap));
1401
    MCHECK(xd->mbmp = newmenuitem("Bmp...", 0, menufilebitmap));
8971 ripley 1402
    MCHECK(newsubmenu(xd->msubsave,"Jpeg"));
17245 ripley 1403
    MCHECK(xd->mjpeg50 = newmenuitem("50% quality...", 0, menufilebitmap));
1404
    MCHECK(xd->mjpeg75 = newmenuitem("75% quality...", 0, menufilebitmap));
1405
    MCHECK(xd->mjpeg100 = newmenuitem("100% quality...", 0, menufilebitmap));
8971 ripley 1406
    MCHECK(newsubmenu(m, "Copy to the clipboard"));
1407
    MCHECK(xd->mclpbm = newmenuitem("as a Bitmap\tCTRL+C", 0, menuclpbm));
1408
    MCHECK(xd->mclpwm = newmenuitem("as a Metafile\tCTRL+W", 0, menuclpwm));
1409
    addto(m);
1410
    MCHECK(newmenuitem("-", 0, NULL));
17245 ripley 1411
    MCHECK(xd->mprint = newmenuitem("Print...\tCTRL+P", 0, menuprint));
8971 ripley 1412
    MCHECK(newmenuitem("-", 0, NULL));
1413
    MCHECK(xd->mclose = newmenuitem("close Device", 0, menuclose));
17050 ripley 1414
#ifdef PLOTHISTORY
8971 ripley 1415
    MCHECK(newmenu("History"));
1416
    MCHECK(xd->mrec = newmenuitem("Recording", 0, menurec));
10541 ripley 1417
    if(recording) check(xd->mrec);
8971 ripley 1418
    MCHECK(newmenuitem("-", 0, NULL));
1419
    MCHECK(xd->madd = newmenuitem("Add\tINS", 0, menuadd));
1420
    MCHECK(xd->mreplace = newmenuitem("Replace", 0, menureplace));
1421
    MCHECK(newmenuitem("-", 0, NULL));
1422
    MCHECK(xd->mprev = newmenuitem("Previous\tPgUp", 0, menuprev));
1423
    MCHECK(xd->mnext = newmenuitem("Next\tPgDown", 0, menunext));
1424
    MCHECK(newmenuitem("-", 0, NULL));
17245 ripley 1425
    MCHECK(xd->msvar = newmenuitem("Save to variable...", 0, menusvar));
1426
    MCHECK(xd->mgvar = newmenuitem("Get from variable...", 0, menugvar));
8971 ripley 1427
    MCHECK(newmenuitem("-", 0, NULL));
1428
    MCHECK(xd->mclear = newmenuitem("Clear history", 0, menuclear));
17050 ripley 1429
#endif
11597 ripley 1430
    MCHECK(newmenu("Resize"));
1431
    MCHECK(xd->mR = newmenuitem("R mode", 0, menuR));
1432
    if(resize == 1) check(xd->mR);
1433
    MCHECK(xd->mfit = newmenuitem("Fit to window", 0, menufit));
1434
    if(resize == 2) check(xd->mfit);
1435
    MCHECK(xd->mfix = newmenuitem("Fixed size", 0, menufix));
1436
    if(resize == 3) check(xd->mfix);
8971 ripley 1437
    newmdimenu();
6430 guido 1438
 
8971 ripley 1439
    /* Normal popup */
27686 murdoch 1440
    MCHECK(xd->grpopup = newpopup(grpopupact));
1441
    setdata(xd->grpopup, (void *) dd);
8971 ripley 1442
    MCHECK(m = newmenuitem("Copy as metafile", 0, menuclpwm));
1443
    setdata(m, (void *) dd);
1444
    MCHECK(m = newmenuitem("Copy as bitmap", 0, menuclpbm));
1445
    setdata(m, (void *) dd);
1446
    MCHECK(newmenuitem("-", 0, NULL));
17245 ripley 1447
    MCHECK(m = newmenuitem("Save as metafile...", 0, menuwm));
8971 ripley 1448
    setdata(m, (void *) dd);
17245 ripley 1449
    MCHECK(m = newmenuitem("Save as postscript...", 0, menups));
8971 ripley 1450
    setdata(m, (void *) dd);
1451
    MCHECK(newmenuitem("-", 0, NULL));
27686 murdoch 1452
    MCHECK(xd->grmenustayontop = newmenuitem("Stay on top", 0, menustayontop));
1453
    setdata(xd->grmenustayontop, (void *) dd);
1454
    MCHECK(newmenuitem("-", 0, NULL));
17245 ripley 1455
    MCHECK(m = newmenuitem("Print...", 0, menuprint));
8971 ripley 1456
    setdata(m, (void *) dd);
1457
    gchangepopup(xd->gawin, xd->grpopup);
6430 guido 1458
 
22270 ripley 1459
    MCHECK(xd->bm = newbitmap(getwidth(xd->gawin), getheight(xd->gawin),
8971 ripley 1460
			      getdepth(xd->gawin)));
15641 ripley 1461
    gfillrect(xd->gawin, xd->outcolor, getrect(xd->gawin));
1462
    gfillrect(xd->bm, xd->outcolor, getrect(xd->bm));
8971 ripley 1463
    addto(xd->gawin);
1464
    setdata(xd->mbar, (void *) dd);
1465
    setdata(xd->mpng, (void *) dd);
1466
    setdata(xd->mbmp, (void *) dd);
1467
    setdata(xd->mjpeg50, (void *) dd);
1468
    setdata(xd->mjpeg75, (void *) dd);
1469
    setdata(xd->mjpeg100, (void *) dd);
1470
    setdata(xd->mps, (void *) dd);
14461 ripley 1471
    setdata(xd->mpdf, (void *) dd);
8971 ripley 1472
    setdata(xd->mwm, (void *) dd);
1473
    setdata(xd->mclpwm, (void *) dd);
1474
    setdata(xd->mclpbm, (void *) dd);
1475
    setdata(xd->mprint, (void *) dd);
1476
    setdata(xd->mclose, (void *) dd);
1477
    setdata(xd->mrec, (void *) dd);
1478
    setdata(xd->mprev, (void *) dd);
1479
    setdata(xd->mnext, (void *) dd);
1480
    setdata(xd->mgvar, (void *) dd);
1481
    setdata(xd->madd, (void *) dd);
1482
    setdata(xd->mreplace, (void *) dd);
11597 ripley 1483
    setdata(xd->mR, (void *) dd);
1484
    setdata(xd->mfit, (void *) dd);
1485
    setdata(xd->mfix, (void *) dd);
1486
    show(xd->gawin); /* twice, for a Windows bug */
8971 ripley 1487
    show(xd->gawin);
27357 murdoch 1488
    BringToTop(xd->gawin, 0);
11597 ripley 1489
    sethit(xd->gawin, devga_sbf);
8971 ripley 1490
    setresize(xd->gawin, HelpResize);
1491
    setredraw(xd->gawin, HelpExpose);
1492
    setmousedown(xd->gawin, HelpMouseClick);
1493
    setkeydown(xd->gawin, NHelpKeyIn);
1494
    setkeyaction(xd->gawin, CHelpKeyIn);
1495
    setclose(xd->gawin, HelpClose);
10541 ripley 1496
    xd->recording = recording;
11597 ripley 1497
    xd->replaying = FALSE;
1498
    xd->resizing = resize;
6430 guido 1499
 
8971 ripley 1500
    return 1;
6430 guido 1501
}
1502
 
17050 ripley 1503
static Rboolean GA_Open(NewDevDesc *dd, gadesc *xd, char *dsp,
15641 ripley 1504
			double w, double h, Rboolean recording,
24724 ripley 1505
			int resize, int canvascolor, double gamma,
1506
			int xpos, int ypos)
4394 ripley 1507
{
1508
    rect  rr;
25262 ripley 1509
    char buf[600]; /* allow for pageno formats */
4394 ripley 1510
 
1511
    if (!fontinitdone)
1512
	RFontInit();
1513
 
1514
    /* Foreground and Background Colors */
17050 ripley 1515
    xd->bg = dd->startfill = 0xffffffff; /* transparent */
1516
    xd->col = dd->startcol = R_RGB(0, 0, 0);
4394 ripley 1517
 
1518
    xd->fgcolor = Black;
17165 murrell 1519
    xd->bgcolor = xd->canvascolor = GArgb(canvascolor, gamma);
15641 ripley 1520
    xd->outcolor = myGetSysColor(COLOR_APPWORKSPACE);
11597 ripley 1521
    xd->rescale_factor = 1.0;
12778 pd 1522
    xd->fast = 1;  /* Use `cosmetic pens' if available */
11597 ripley 1523
    xd->xshift = xd->yshift = 0;
21024 ripley 1524
    xd->npage = 0;
4394 ripley 1525
    if (!dsp[0]) {
24724 ripley 1526
	if (!setupScreenDevice(dd, xd, w, h, recording, resize, xpos, ypos))
16891 ripley 1527
	    return FALSE;
19110 ripley 1528
    } else if (!strncmp(dsp, "win.print:", 10)) {
6430 guido 1529
	xd->kind = PRINTER;
19110 ripley 1530
	xd->gawin = newprinter(MM_PER_INCH * w, MM_PER_INCH * h, &dsp[10]);
4394 ripley 1531
	if (!xd->gawin)
11067 maechler 1532
	    return FALSE;
16137 ripley 1533
    } else if (!strncmp(dsp, "png:", 4) || !strncmp(dsp,"bmp:", 4)) {
1534
	if(R_OPAQUE(canvascolor))
17050 ripley 1535
	    xd->bg = dd->startfill = GArgb(canvascolor, 1.0);
16137 ripley 1536
	else
17050 ripley 1537
	    xd->bg = dd->startfill = canvascolor;
16137 ripley 1538
	/* was R_RGB(255, 255, 255); white */
6458 guido 1539
        xd->kind = (dsp[0]=='p') ? PNG : BMP;
27143 ripley 1540
	if(strlen(dsp+4) >= 512) error("filename too long in %s() call",
25262 ripley 1541
				       (dsp[0]=='p') ? "png" : "bmp");
21024 ripley 1542
	strcpy(xd->filename, dsp+4);
6430 guido 1543
	if (!Load_Rbitmap_Dll()) {
14159 ripley 1544
	    warning("Unable to load Rbitmap.dll");
1545
	    return FALSE;
6430 guido 1546
	}
1547
	/*
22270 ripley 1548
	  Observe that given actual graphapp implementation 256 is
12778 pd 1549
	  irrelevant,i.e., depth of the bitmap is the one of graphic card
6430 guido 1550
	  if required depth > 1
1551
	*/
14161 ripley 1552
	if ((xd->gawin = newbitmap(w, h, 256)) == NULL) {
14159 ripley 1553
	    warning("Unable to allocate bitmap");
1554
	    return FALSE;
21024 ripley 1555
	}
25312 ripley 1556
	snprintf(buf, 600, xd->filename, 1);
21024 ripley 1557
	if ((xd->fp = fopen(buf, "wb")) == NULL) {
14159 ripley 1558
	    del(xd->gawin);
21024 ripley 1559
	    warning("Unable to open file `%s' for writing", buf);
14159 ripley 1560
	    return FALSE;
6430 guido 1561
	}
1562
    } else if (!strncmp(dsp, "jpeg:", 5)) {
1563
        char *p = strchr(&dsp[5], ':');
17050 ripley 1564
	xd->bg = dd->startfill = GArgb(canvascolor, 1.0);
8971 ripley 1565
        xd->kind = JPEG;
11067 maechler 1566
	if (!p) return FALSE;
6430 guido 1567
	if (!Load_Rbitmap_Dll()) {
14159 ripley 1568
	    warning("Unable to load Rbitmap.dll");
1569
	    return FALSE;
6430 guido 1570
	}
1571
	*p = '\0';
1572
	xd->quality = atoi(&dsp[5]);
6458 guido 1573
	*p = ':' ;
25262 ripley 1574
	if(strlen(p+1) >= 512) error("filename too long in jpeg() call");
21024 ripley 1575
	strcpy(xd->filename, p+1);
14159 ripley 1576
	if((xd->gawin = newbitmap(w, h, 256)) == NULL) {
1577
	    warning("Unable to allocate bitmap");
1578
	    return FALSE;
22270 ripley 1579
	}
25312 ripley 1580
	snprintf(buf, 600, xd->filename, 1);
21024 ripley 1581
	if ((xd->fp = fopen(buf, "wb")) == NULL) {
14159 ripley 1582
	    del(xd->gawin);
21024 ripley 1583
	    warning("Unable to open file `%s' for writing", buf);
14159 ripley 1584
	    return FALSE;
6430 guido 1585
	}
4394 ripley 1586
    } else {
6430 guido 1587
	/*
1588
	 * win.metafile[:] in memory (for the clipboard)
1589
	 * win.metafile:filename
11067 maechler 1590
	 * anything else return FALSE
6430 guido 1591
	 */
5341 guido 1592
	char  *s = "win.metafile";
4394 ripley 1593
	int   ls = strlen(s);
1594
	int   ld = strlen(dsp);
1595
 
1596
	if (ls > ld)
11067 maechler 1597
	    return FALSE;
5341 guido 1598
	if (strncmp(dsp, s, ls) || (dsp[ls] && (dsp[ls] != ':')))
11067 maechler 1599
	    return FALSE;
27143 ripley 1600
	if(ld > ls && strlen(&dsp[ls + 1]) >= 512)
25262 ripley 1601
	    error("filename too long in win.metafile() call");
21024 ripley 1602
	strcpy(xd->filename, (ld > ls) ? &dsp[ls + 1] : "");
25312 ripley 1603
	snprintf(buf, 600, xd->filename, 1);
21024 ripley 1604
	xd->w = MM_PER_INCH * w;
1605
	xd->h =  MM_PER_INCH * h;
1606
	xd->gawin = newmetafile(buf, MM_PER_INCH * w, MM_PER_INCH * h);
6430 guido 1607
	xd->kind = METAFILE;
12778 pd 1608
	xd->fast = 0; /* use scalable line widths */
14172 ripley 1609
	if (!xd->gawin) {
1610
	    if(ld > ls)
21024 ripley 1611
		warning("Unable to open metafile `%s' for writing", buf);
14172 ripley 1612
	    else
1613
		warning("Unable to open clipboard to write metafile");
1614
	    return FALSE;
1615
	}
4394 ripley 1616
    }
6625 guido 1617
    xd->truedpi = devicepixelsy(xd->gawin);
22270 ripley 1618
    if ((xd->kind == PNG) || (xd->kind == JPEG) || (xd->kind == BMP))
6625 guido 1619
      xd->wanteddpi = 72 ;
1620
    else
12778 pd 1621
      xd->wanteddpi = xd->truedpi;
4394 ripley 1622
    if (!SetBaseFont(xd)) {
14172 ripley 1623
	warning("can't find any fonts");
4394 ripley 1624
	del(xd->gawin);
8971 ripley 1625
	if (xd->kind == SCREEN) del(xd->bm);
11067 maechler 1626
	return FALSE;
4394 ripley 1627
    }
1628
    rr = getrect(xd->gawin);
11597 ripley 1629
    xd->origWidth = xd->showWidth = xd->windowWidth = rr.width;
1630
    xd->origHeight = xd->showHeight = xd->windowHeight = rr.height;
4394 ripley 1631
    xd->clip = rr;
1632
    setdata(xd->gawin, (void *) dd);
12778 pd 1633
    xd->needsave = FALSE;
11067 maechler 1634
    return TRUE;
4394 ripley 1635
}
1636
 
1637
	/********************************************************/
1638
	/* device_StrWidth should return the width of the given */
1639
	/* string in DEVICE units (GStrWidth is responsible for */
1640
	/* converting from DEVICE to whatever units the user 	*/
1641
	/* asked for						*/
1642
	/********************************************************/
1643
 
27357 murdoch 1644
static double GA_StrWidth(char *str,
27236 murrell 1645
			  R_GE_gcontext *gc,
1646
			  NewDevDesc *dd)
4394 ripley 1647
{
8971 ripley 1648
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1649
    double a;
27236 murrell 1650
    int   size = gc->cex * gc->ps + 0.5;
4394 ripley 1651
 
27236 murrell 1652
    SetFont(gc->fontface, size, 0.0, dd);
4394 ripley 1653
    a = (double) gstrwidth(xd->gawin, xd->font, str);
1654
    return a;
1655
}
1656
 
1657
 
1658
	/********************************************************/
1659
	/* device_MetricInfo should return height, depth, and 	*/
1660
	/* width information for the given character in DEVICE	*/
1661
	/* units (GMetricInfo does the necessary conversions)	*/
1662
	/* This is used for formatting mathematical expressions	*/
1663
	/********************************************************/
1664
 
1665
	/* Character Metric Information */
1666
	/* Passing c == 0 gets font information */
1667
 
27357 murdoch 1668
static void GA_MetricInfo(int c,
27236 murrell 1669
			  R_GE_gcontext *gc,
1670
			  double* ascent, double* descent,
1671
			  double* width, NewDevDesc *dd)
4394 ripley 1672
{
1673
    int   a, d, w;
27236 murrell 1674
    int   size = gc->cex * gc->ps + 0.5;
8971 ripley 1675
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1676
 
27236 murrell 1677
    SetFont(gc->fontface, size, 0.0, dd);
4394 ripley 1678
    gcharmetric(xd->gawin, xd->font, c, &a, &d, &w);
25647 ripley 1679
    /* Some Windows systems report that space has height and depth,
1680
       so we have a kludge.  Note that 32 is space in symbol font too */
1681
    if(c == 32) {
1682
	*ascent  = 0.0;
1683
	*descent = 0.0;
1684
	*width   = (double) w;
27143 ripley 1685
 
25647 ripley 1686
    } else {
1687
	*ascent  = (double) a;
1688
	*descent = (double) d;
1689
	*width   = (double) w;
1690
    }
4394 ripley 1691
}
1692
 
1693
	/********************************************************/
1694
	/* device_Clip is given the left, right, bottom, and 	*/
1695
	/* top of a rectangle (in DEVICE coordinates).  it 	*/
1696
	/* should have the side-effect that subsequent output	*/
1697
	/* is clipped to the given rectangle			*/
1698
	/********************************************************/
1699
 
17050 ripley 1700
static void GA_Clip(double x0, double x1, double y0, double y1, NewDevDesc *dd)
4394 ripley 1701
{
8971 ripley 1702
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1703
 
1704
    xd->clip = rcanon(rpt(pt(x0, y0), pt(x1, y1)));
6625 guido 1705
    xd->clip.width  += 1;
1706
    xd->clip.height += 1;
4394 ripley 1707
}
1708
 
1709
	/********************************************************/
1710
	/* device_Resize is called whenever the device is 	*/
1711
	/* resized.  the function must update the GPar 		*/
1712
	/* parameters (left, right, bottom, and top) for the 	*/
1713
	/* new device size					*/
1714
	/* this is not usually called directly by the graphics	*/
1715
	/* engine because the detection of device resizes	*/
1716
	/* (e.g., a window resize) are usually detected by	*/
7824 ripley 1717
	/* device-specific code	(see R_ProcessEvents in ./system.c)*/
4394 ripley 1718
	/********************************************************/
1719
 
17050 ripley 1720
static void GA_Size(double *left, double *right,
1721
		     double *bottom, double *top,
1722
		     NewDevDesc *dd)
1723
{
1724
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1725
 
17050 ripley 1726
    int   iw, ih;
1727
 
1728
    iw = xd->windowWidth;
1729
    ih = xd->windowHeight;
1730
    *left = 0.0;
1731
    *top = 0.0;
1732
    *right = iw;
1733
    *bottom = ih;
1734
}
1735
 
1736
static void GA_Resize(NewDevDesc *dd)
4394 ripley 1737
{
8971 ripley 1738
    gadesc *xd = (gadesc *) dd->deviceSpecific;
17270 murrell 1739
    SEXP scale;
1740
    PROTECT(scale = allocVector(REALSXP, 1));
4394 ripley 1741
    if (xd->resize) {
22270 ripley 1742
	int   iw, ih, iw0 = dd->right - dd->left,
17050 ripley 1743
	    ih0 = dd->bottom - dd->top;
11597 ripley 1744
	double fw, fh, rf, shift;
4394 ripley 1745
 
11597 ripley 1746
	iw = xd->windowWidth;
1747
	ih = xd->windowHeight;
1748
	if(xd->resizing == 1) {
19174 ripley 1749
	    /* last mode might have been 3, so remove scrollbars */
1750
	    gchangescrollbar(xd->gawin, VWINSB, 0, ih/SF-1, ih/SF, 0);
1751
	    gchangescrollbar(xd->gawin, HWINSB, 0, iw/SF-1, iw/SF, 0);
17050 ripley 1752
	    dd->left = 0.0;
1753
	    dd->top = 0.0;
1754
	    dd->right = iw;
1755
	    dd->bottom = ih;
11780 ripley 1756
	    xd->showWidth = iw;
1757
	    xd->showHeight =  ih;
11597 ripley 1758
	} else if (xd->resizing == 2) {
19174 ripley 1759
	    /* last mode might have been 3, so remove scrollbars */
1760
	    gchangescrollbar(xd->gawin, VWINSB, 0, ih/SF-1, ih/SF, 0);
1761
	    gchangescrollbar(xd->gawin, HWINSB, 0, iw/SF-1, iw/SF, 0);
11597 ripley 1762
	    fw = (iw + 0.5)/(iw0 + 0.5);
1763
	    fh = (ih + 0.5)/(ih0 + 0.5);
1764
	    rf = min(fw, fh);
1765
	    xd->rescale_factor *= rf;
1766
	    xd->wanteddpi = xd->rescale_factor * xd->truedpi;
17270 murrell 1767
	    /* dd->ps *= rf; dd->cra[0] *= rf; dd->cra[1] *= rf; */
1768
	    REAL(scale)[0] = rf;
1769
	    GEHandleEvent(GE_ScalePS, dd, scale);
11597 ripley 1770
	    if (fw < fh) {
17050 ripley 1771
		dd->left = 0.0;
1772
		xd->showWidth = dd->right = iw;
11597 ripley 1773
		xd->showHeight =  ih0*fw;
1774
		shift = (ih - xd->showHeight)/2.0;
17050 ripley 1775
		dd->top = shift;
1776
		dd->bottom = ih0*fw + shift;
11597 ripley 1777
		xd->xshift = 0; xd->yshift = shift;
1778
	    } else {
17050 ripley 1779
		dd->top = 0.0;
1780
		xd->showHeight = dd->bottom = ih;
11597 ripley 1781
		xd->showWidth = iw0*fh;
1782
		shift = (iw - xd->showWidth)/2.0;
17050 ripley 1783
		dd->left = shift;
1784
		dd->right = iw0*fh + shift;
11597 ripley 1785
		xd->xshift = shift; xd->yshift = 0;
1786
	    }
1787
	    xd->clip = getregion(xd);
1788
	} else if (xd->resizing == 3) {
22270 ripley 1789
	    if(iw0 < iw) shift = (iw - iw0)/2.0;
11597 ripley 1790
	    else shift = min(0, xd->xshift);
17050 ripley 1791
	    dd->left = shift;
1792
	    dd->right = iw0 + shift;
11597 ripley 1793
	    xd->xshift = shift;
22270 ripley 1794
	    gchangescrollbar(xd->gawin, HWINSB, max(-shift,0)/SF,
12256 pd 1795
			     xd->origWidth/SF - 1, xd->windowWidth/SF, 0);
22270 ripley 1796
	    if(ih0 < ih) shift = (ih - ih0)/2.0;
11597 ripley 1797
	    else shift = min(0, xd->yshift);
17050 ripley 1798
	    dd->top = shift;
1799
	    dd->bottom = ih0 + shift;
11597 ripley 1800
	    xd->yshift = shift;
22270 ripley 1801
	    gchangescrollbar(xd->gawin, VWINSB, max(-shift,0)/SF,
12256 pd 1802
			     xd->origHeight/SF - 1, xd->windowHeight/SF, 0);
1803
	    xd->showWidth = xd->origWidth + min(0, xd->xshift);
1804
	    xd->showHeight = xd->origHeight + min(0,  xd->yshift);
11597 ripley 1805
	}
1806
	xd->resize = FALSE;
1807
	if (xd->kind == SCREEN) {
4394 ripley 1808
	    del(xd->bm);
1809
	    xd->bm = newbitmap(iw, ih, getdepth(xd->gawin));
1810
	    if (!xd->bm) {
4649 ripley 1811
		R_ShowMessage("Insufficient memory for resize. Killing device");
17050 ripley 1812
		KillDevice(GetDevice(devNumber((DevDesc*) dd)));
4394 ripley 1813
	    }
15641 ripley 1814
	    gfillrect(xd->gawin, xd->outcolor, getrect(xd->gawin));
1815
	    gfillrect(xd->bm, xd->outcolor, getrect(xd->bm));
4394 ripley 1816
	}
1817
    }
17270 murrell 1818
    UNPROTECT(1);
4394 ripley 1819
}
1820
 
1821
	/********************************************************/
1822
	/* device_NewPage is called whenever a new plot requires*/
1823
	/* a new page.  a new page might mean just clearing the	*/
1824
	/* device (as in this case) or moving to a new page	*/
1825
	/* (e.g., postscript)					*/
1826
	/********************************************************/
1827
 
27236 murrell 1828
static void GA_NewPage(R_GE_gcontext *gc,
1829
		       NewDevDesc *dd)
4394 ripley 1830
{
8971 ripley 1831
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1832
 
21024 ripley 1833
    xd->npage++;
6430 guido 1834
    if ((xd->kind == PRINTER) && xd->needsave)
4394 ripley 1835
	nextpage(xd->gawin);
21024 ripley 1836
    if ((xd->kind == METAFILE) && xd->needsave) {
25262 ripley 1837
	char buf[600];
21024 ripley 1838
	if (strlen(xd->filename) == 0)
1839
	    error("A clipboard metafile can store only one figure.");
1840
	else {
1841
	    del(xd->gawin);
25312 ripley 1842
	    snprintf(buf, 600, xd->filename, xd->npage);
21024 ripley 1843
	    xd->gawin = newmetafile(buf, xd->w, xd->h);
1844
	}
1845
    }
22270 ripley 1846
    if ((xd->kind == PNG || xd->kind == JPEG || xd->kind == BMP)
21024 ripley 1847
	&& xd->needsave) {
25262 ripley 1848
	char buf[600];
21024 ripley 1849
	SaveAsBitmap(dd);
25312 ripley 1850
	snprintf(buf, 600, xd->filename, xd->npage);
21024 ripley 1851
	if ((xd->fp = fopen(buf, "wb")) == NULL)
1852
	    error("Unable to open file `%s' for writing", buf);
1853
    }
8971 ripley 1854
    if (xd->kind == SCREEN) {
26125 murdoch 1855
        if(xd->buffered) SHOW;
17050 ripley 1856
#ifdef PLOTHISTORY
4394 ripley 1857
	if (xd->recording && xd->needsave)
18917 murrell 1858
	    AddtoPlotHistory(dd->savedSnapshot, 0);
4394 ripley 1859
	if (xd->replaying)
11597 ripley 1860
	    xd->needsave = FALSE;
4394 ripley 1861
	else
11597 ripley 1862
	    xd->needsave = TRUE;
17050 ripley 1863
#endif
4394 ripley 1864
    }
27236 murrell 1865
    xd->bg = gc->fill;
22270 ripley 1866
    if (!R_OPAQUE(xd->bg))
15641 ripley 1867
	xd->bgcolor = xd->canvascolor;
1868
    else
27236 murrell 1869
	xd->bgcolor = GArgb(xd->bg, gc->gamma);
15641 ripley 1870
    if (xd->kind != SCREEN) {
11597 ripley 1871
	xd->needsave = TRUE;
8971 ripley 1872
	xd->clip = getrect(xd->gawin);
22270 ripley 1873
	if(R_OPAQUE(xd->bg) || xd->kind == PNG ||
15641 ripley 1874
	   xd->kind == BMP || xd->kind == JPEG )
22270 ripley 1875
	    DRAW(gfillrect(_d, R_OPAQUE(xd->bg) ? xd->bgcolor : PNG_TRANS,
16137 ripley 1876
			   xd->clip));
18312 ripley 1877
	if(xd->kind == PNG) xd->pngtrans = ggetpixel(xd->gawin, pt(0,0));
6430 guido 1878
    } else {
11597 ripley 1879
	xd->clip = getregion(xd);
12256 pd 1880
	DRAW(gfillrect(_d, xd->bgcolor, xd->clip));
6430 guido 1881
    }
26125 murdoch 1882
    SH;
4394 ripley 1883
}
1884
 
1885
	/********************************************************/
1886
	/* device_Close is called when the device is killed	*/
1887
	/* this function is responsible for destroying any 	*/
1888
	/* device-specific resources that were created in	*/
1889
	/* device_Open and for FREEing the device-specific	*/
1890
	/* parameters structure					*/
1891
	/********************************************************/
1892
 
17050 ripley 1893
static void GA_Close(NewDevDesc *dd)
4394 ripley 1894
{
8971 ripley 1895
    gadesc *xd = (gadesc *) dd->deviceSpecific;
24923 ripley 1896
    GEDevDesc *gdd = (GEDevDesc *) GetDevice(devNumber((DevDesc*) dd));
1897
    GETDL;
4394 ripley 1898
 
24923 ripley 1899
    if (xd->kind == SCREEN) {
1900
	if(xd->recording) {
1901
	    AddtoPlotHistory(GEcreateSnapshot(gdd), 0);
1902
	    pCURRENTPOS++; /* so PgUp goes to the last saved plot
1903
			      when a windows() device is opened */
1904
	}
4394 ripley 1905
	hide(xd->gawin);
1906
	del(xd->bm);
27143 ripley 1907
	if (xd == GA_xd) GA_xd = NULL;
6458 guido 1908
    } else if ((xd->kind == PNG) || (xd->kind == JPEG) || (xd->kind == BMP)) {
6430 guido 1909
      SaveAsBitmap(dd);
22270 ripley 1910
    }
4394 ripley 1911
    del(xd->font);
1912
    del(xd->gawin);
22270 ripley 1913
/*
1914
 * this is needed since the GraphApp delayed clean-up
1915
 * ,i.e, I want free all resources NOW
24923 ripley 1916
 */
22270 ripley 1917
    doevent();
6430 guido 1918
    free(xd);
4394 ripley 1919
}
1920
 
1921
	/********************************************************/
1922
	/* device_Activate is called when a device becomes the 	*/
1923
	/* active device.  in this case it is used to change the*/
1924
	/* title of a window to indicate the active status of 	*/
1925
	/* the device to the user.  not all device types will 	*/
1926
	/* do anything						*/
1927
	/********************************************************/
1928
 
1929
static unsigned char title[20] = "R Graphics";
1930
 
17050 ripley 1931
static void GA_Activate(NewDevDesc *dd)
4394 ripley 1932
{
1933
    char  t[50];
1934
    char  num[3];
8971 ripley 1935
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1936
 
6430 guido 1937
    if (xd->replaying || (xd->kind!=SCREEN))
4394 ripley 1938
	return;
6098 pd 1939
    strcpy(t, (char *) title);
4394 ripley 1940
    strcat(t, ": Device ");
17050 ripley 1941
    sprintf(num, "%i", devNumber((DevDesc*) dd) + 1);
4394 ripley 1942
    strcat(t, num);
1943
    strcat(t, " (ACTIVE)");
1944
    settext(xd->gawin, t);
1945
}
1946
 
1947
	/********************************************************/
1948
	/* device_Deactivate is called when a device becomes	*/
1949
	/* inactive.  in this case it is used to change the 	*/
1950
	/* title of a window to indicate the inactive status of */
1951
	/* the device to the user.  not all device types will	*/
1952
	/* do anything						*/
1953
	/********************************************************/
1954
 
17050 ripley 1955
static void GA_Deactivate(NewDevDesc *dd)
4394 ripley 1956
{
1957
    char  t[50];
1958
    char  num[3];
8971 ripley 1959
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1960
 
8971 ripley 1961
    if (xd->replaying || (xd->kind != SCREEN))
4394 ripley 1962
	return;
6098 pd 1963
    strcpy(t, (char *) title);
4394 ripley 1964
    strcat(t, ": Device ");
17050 ripley 1965
    sprintf(num, "%i", devNumber((DevDesc*) dd) + 1);
4394 ripley 1966
    strcat(t, num);
1967
    strcat(t, " (inactive)");
1968
    settext(xd->gawin, t);
1969
}
1970
 
1971
	/********************************************************/
1972
	/* device_Rect should have the side-effect that a 	*/
1973
	/* rectangle is drawn with the given locations for its 	*/
1974
	/* opposite corners.  the border of the rectangle	*/
1975
	/* should be in the given "fg" colour and the rectangle	*/
1976
	/* should be filled with the given "bg" colour		*/
1977
	/* if "fg" is NA_INTEGER then no border should be drawn */
1978
	/* if "bg" is NA_INTEGER then the rectangle should not 	*/
1979
	/* be filled						*/
1980
	/* the locations are in an arbitrary coordinate system	*/
1981
	/* and this function is responsible for converting the	*/
1982
	/* locations to DEVICE coordinates using GConvert	*/
1983
	/********************************************************/
1984
 
8971 ripley 1985
static void GA_Rect(double x0, double y0, double x1, double y1,
27236 murrell 1986
		    R_GE_gcontext *gc,
1987
		    NewDevDesc *dd)
4394 ripley 1988
{
1989
    int   tmp;
8971 ripley 1990
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 1991
    rect  r;
1992
 
1993
    /* These in-place conversions are ok */
1994
    TRACEDEVGA("rect");
1995
 
1996
    if (x0 > x1) {
1997
	tmp = x0;
1998
	x0 = x1;
1999
	x1 = tmp;
2000
    }
2001
    if (y0 > y1) {
2002
	tmp = y0;
2003
	y0 = y1;
2004
	y1 = tmp;
2005
    }
2006
    r = rect((int) x0, (int) y0, (int) x1 - (int) x0, (int) y1 - (int) y0);
27236 murrell 2007
    if (R_OPAQUE(gc->fill)) {
2008
	SetColor(gc->fill, gc->gamma, dd);
4394 ripley 2009
	DRAW(gfillrect(_d, xd->fgcolor, r));
2010
 
2011
    }
27236 murrell 2012
    if (R_OPAQUE(gc->col)) {
2013
	SetColor(gc->col, gc->gamma, dd);
2014
	SetLinetype(gc->lty, gc->lwd, dd);
12778 pd 2015
	DRAW(gdrawrect(_d, xd->lwd, xd->lty, xd->fgcolor, r, 0));
4394 ripley 2016
    }
24610 ripley 2017
    SH;
4394 ripley 2018
}
2019
 
2020
	/********************************************************/
2021
	/* device_Circle should have the side-effect that a	*/
2022
	/* circle is drawn, centred at the given location, with */
2023
	/* the given radius.  the border of the circle should be*/
2024
	/* drawn in the given "col", and the circle should be	*/
2025
	/* filled with the given "border" colour.		*/
2026
	/* if "col" is NA_INTEGER then no border should be drawn*/
2027
	/* if "border" is NA_INTEGER then the circle should not */
2028
	/* be filled						*/
2029
	/* the location is in arbitrary coordinates and the 	*/
2030
	/* function is responsible for converting this to	*/
2031
	/* DEVICE coordinates.  the radius is given in DEVICE	*/
2032
	/* coordinates						*/
2033
	/********************************************************/
2034
 
17050 ripley 2035
static void GA_Circle(double x, double y, double r,
27236 murrell 2036
		      R_GE_gcontext *gc,
2037
		      NewDevDesc *dd)
4394 ripley 2038
{
2039
    int   ir, ix, iy;
8971 ripley 2040
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 2041
    rect  rr;
2042
 
2043
    TRACEDEVGA("circle");
2044
    ir = floor(r + 0.5);
18400 ripley 2045
    if (ir < 1) ir = 1;
4394 ripley 2046
    /* In-place conversion ok */
2047
 
2048
    ix = (int) x;
2049
    iy = (int) y;
2050
    rr = rect(ix - ir, iy - ir, 2 * ir, 2 * ir);
27236 murrell 2051
    if (R_OPAQUE(gc->fill)) {
2052
	SetColor(gc->fill, gc->gamma, dd);
17050 ripley 2053
	DRAW(gfillellipse(_d, xd->fgcolor, rr));
2054
    }
27236 murrell 2055
    if (R_OPAQUE(gc->col)) {
2056
	SetLinetype(gc->lty, gc->lwd, dd);
2057
	SetColor(gc->col, gc->gamma, dd);
12778 pd 2058
	DRAW(gdrawellipse(_d, xd->lwd, xd->fgcolor, rr, 0));
4394 ripley 2059
    }
24610 ripley 2060
    SH;
4394 ripley 2061
}
2062
 
2063
	/********************************************************/
2064
	/* device_Line should have the side-effect that a single*/
2065
	/* line is drawn (from x1,y1 to x2,y2)			*/
2066
	/* x1, y1, x2, and y2 are in arbitrary coordinates and	*/
2067
	/* the function is responsible for converting them to	*/
2068
	/* DEVICE coordinates using GConvert			*/
2069
	/********************************************************/
2070
 
8971 ripley 2071
static void GA_Line(double x1, double y1, double x2, double y2,
27236 murrell 2072
		    R_GE_gcontext *gc,
2073
		    NewDevDesc *dd)
4394 ripley 2074
{
2075
    int   xx1, yy1, xx2, yy2;
8971 ripley 2076
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 2077
 
2078
    /* In-place conversion ok */
2079
    TRACEDEVGA("line");
2080
    xx1 = (int) x1;
2081
    yy1 = (int) y1;
2082
    xx2 = (int) x2;
2083
    yy2 = (int) y2;
2084
 
27236 murrell 2085
    if (R_OPAQUE(gc->col)) {
2086
	SetColor(gc->col, gc->gamma, dd);
2087
	SetLinetype(gc->lty, gc->lwd, dd);
15641 ripley 2088
	DRAW(gdrawline(_d, xd->lwd, xd->lty, xd->fgcolor,
2089
		       pt(xx1, yy1), pt(xx2, yy2), 0));
25389 ripley 2090
	SH;
25387 ripley 2091
    }
4394 ripley 2092
}
2093
 
2094
	/********************************************************/
2095
	/* device_Polyline should have the side-effect that a	*/
2096
	/* series of line segments are drawn using the given x	*/
2097
	/* and y values						*/
2098
	/* the x and y values are in arbitrary coordinates and	*/
2099
	/* the function is responsible for converting them to	*/
2100
	/* DEVICE coordinates using GConvert			*/
2101
	/********************************************************/
2102
 
22270 ripley 2103
static void GA_Polyline(int n, double *x, double *y,
27236 murrell 2104
			R_GE_gcontext *gc,
2105
			NewDevDesc *dd)
4394 ripley 2106
{
15321 luke 2107
    char *vmax = vmaxget();
2108
    point *p = (point *) R_alloc(n, sizeof(point));
4394 ripley 2109
    double devx, devy;
2110
    int   i;
8971 ripley 2111
    gadesc *xd = (gadesc *) dd->deviceSpecific;
20779 ripley 2112
 
4394 ripley 2113
    TRACEDEVGA("pl");
8971 ripley 2114
    for (i = 0; i < n; i++) {
2115
	devx = x[i];
2116
	devy = y[i];
2117
	p[i].x = (int) devx;
2118
	p[i].y = (int) devy;
4605 ripley 2119
    }
27236 murrell 2120
    if (R_OPAQUE(gc->col)) {
2121
	SetColor(gc->col, gc->gamma, dd);
2122
	SetLinetype(gc->lty, gc->lwd, dd);
15641 ripley 2123
	DRAW(gdrawpolyline(_d, xd->lwd, xd->lty, xd->fgcolor, p, n, 0, 0));
20779 ripley 2124
    }
15321 luke 2125
    vmaxset(vmax);
24610 ripley 2126
    SH;
4394 ripley 2127
}
2128
 
2129
	/********************************************************/
2130
	/* device_Polygon should have the side-effect that a 	*/
2131
	/* polygon is drawn using the given x and y values	*/
2132
	/* the polygon border should be drawn in the "fg" 	*/
2133
	/* colour and filled with the "bg" colour		*/
2134
	/* if "fg" is NA_INTEGER don't draw the border		*/
2135
	/* if "bg" is NA_INTEGER don't fill the polygon		*/
2136
	/* the x and y values are in arbitrary coordinates and 	*/
2137
	/* the function is responsible for converting them to 	*/
2138
	/* DEVICE coordinates using GConvert			*/
2139
	/********************************************************/
2140
 
22270 ripley 2141
static void GA_Polygon(int n, double *x, double *y,
27236 murrell 2142
		       R_GE_gcontext *gc,
2143
		       NewDevDesc *dd)
4394 ripley 2144
{
15321 luke 2145
    char *vmax = vmaxget();
4394 ripley 2146
    point *points;
2147
    double devx, devy;
2148
    int   i;
8971 ripley 2149
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 2150
 
2151
    TRACEDEVGA("plg");
15321 luke 2152
    points = (point *) R_alloc(n , sizeof(point));
4394 ripley 2153
    if (!points)
2154
	return;
2155
    for (i = 0; i < n; i++) {
2156
	devx = x[i];
2157
	devy = y[i];
2158
	points[i].x = (int) (devx);
2159
	points[i].y = (int) (devy);
2160
    }
27236 murrell 2161
    if (R_OPAQUE(gc->fill)) {
2162
	SetColor(gc->fill, gc->gamma, dd);
4394 ripley 2163
	DRAW(gfillpolygon(_d, xd->fgcolor, points, n));
2164
    }
27236 murrell 2165
    if (R_OPAQUE(gc->col)) {
2166
	SetColor(gc->col, gc->gamma, dd);
2167
	SetLinetype(gc->lty, gc->lwd, dd);
12778 pd 2168
	DRAW(gdrawpolygon(_d, xd->lwd, xd->lty, xd->fgcolor, points, n, 0 ));
4394 ripley 2169
    }
15321 luke 2170
    vmaxset(vmax);
24610 ripley 2171
    SH;
4394 ripley 2172
}
2173
 
2174
 
2175
	/********************************************************/
2176
	/* device_Text should have the side-effect that the 	*/
2177
	/* given text is drawn at the given location		*/
7816 murrell 2178
	/* the text should be rotated according to rot (degrees)*/
4394 ripley 2179
	/* the location is in an arbitrary coordinate system	*/
2180
	/* and this function is responsible for converting the	*/
2181
	/* location to DEVICE coordinates using GConvert	*/
2182
	/********************************************************/
2183
 
2184
 
22270 ripley 2185
static void GA_Text(double x, double y, char *str,
27236 murrell 2186
		    double rot, double hadj,
2187
		    R_GE_gcontext *gc,
2188
		    NewDevDesc *dd)
4394 ripley 2189
{
2190
    int   size;
6098 pd 2191
    double pixs, xl, yl, rot1;
8971 ripley 2192
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 2193
 
27236 murrell 2194
    size = gc->cex * gc->ps + 0.5;
9180 ripley 2195
    pixs = - 1;
7816 murrell 2196
    xl = 0.0;
2197
    yl = -pixs;
6098 pd 2198
    rot1 = rot * DEG2RAD;
2199
    x += -xl * cos(rot1) + yl * sin(rot1);
2200
    y -= -xl * sin(rot1) - yl * cos(rot1);
27236 murrell 2201
    SetFont(gc->fontface, size, rot, dd);
2202
    SetColor(gc->col, gc->gamma, dd);
15641 ripley 2203
    if (R_OPAQUE(xd->fgcolor)) {
4640 ripley 2204
#ifdef NOCLIPTEXT
15641 ripley 2205
	gsetcliprect(xd->gawin, getrect(xd->gawin));
2206
	gdrawstr1(xd->gawin, xd->font, xd->fgcolor, pt(x, y), str, hadj);
2207
	if (xd->kind==SCREEN) {
2208
	    gsetcliprect(xd->bm, getrect(xd->bm));
2209
	    gdrawstr1(xd->bm, xd->font, xd->fgcolor, pt(x, y), str, hadj);
2210
	}
6192 maechler 2211
#else
15641 ripley 2212
	DRAW(gdrawstr1(_d, xd->font, xd->fgcolor, pt(x, y), str, hadj));
4640 ripley 2213
#endif
15641 ripley 2214
    }
24610 ripley 2215
    SH;
4394 ripley 2216
}
2217
 
2218
	/********************************************************/
2219
	/* device_Locator should return the location of the next*/
2220
	/* mouse click (in DEVICE coordinates;  GLocator is	*/
2221
	/* responsible for any conversions)			*/
2222
	/* not all devices will do anything (e.g., postscript)	*/
2223
	/********************************************************/
2224
 
17050 ripley 2225
static Rboolean GA_Locator(double *x, double *y, NewDevDesc *dd)
4394 ripley 2226
{
8971 ripley 2227
    gadesc *xd = (gadesc *) dd->deviceSpecific;
4394 ripley 2228
 
14172 ripley 2229
    if (xd->kind != SCREEN)
11067 maechler 2230
	return FALSE;
2231
    xd->locator = TRUE;
4394 ripley 2232
    xd->clicked = 0;
2233
    show(xd->gawin);
2234
    addto(xd->gawin);
4605 ripley 2235
    gchangemenubar(xd->mbarloc);
2236
    if (xd->stoploc) {
2237
      show(xd->stoploc);
2238
      show(xd->gawin);
2239
    }
2240
    gchangepopup(xd->gawin, xd->locpopup);
4394 ripley 2241
    gsetcursor(xd->gawin, CrossCursor);
4605 ripley 2242
    setstatus("Locator is active");
4394 ripley 2243
    while (!xd->clicked) {
26109 ripley 2244
	if(xd->buffered) SHOW;
5341 guido 2245
        WaitMessage();
7824 ripley 2246
	R_ProcessEvents();
4394 ripley 2247
    }
4605 ripley 2248
    addto(xd->gawin);
2249
    gchangemenubar(xd->mbar);
2250
    if (xd->stoploc) {
2251
      hide(xd->stoploc);
2252
      show(xd->gawin);
6192 maechler 2253
    }
4394 ripley 2254
    gsetcursor(xd->gawin, ArrowCursor);
4605 ripley 2255
    gchangepopup(xd->gawin, xd->grpopup);
4394 ripley 2256
    addto(xd->gawin);
2257
    setstatus("R Graphics");
11067 maechler 2258
    xd->locator = FALSE;
4394 ripley 2259
    if (xd->clicked == 1) {
2260
	*x = xd->px;
2261
	*y = xd->py;
11067 maechler 2262
	return TRUE;
4394 ripley 2263
    } else
11067 maechler 2264
	return FALSE;
4394 ripley 2265
}
2266
 
2267
	/********************************************************/
2268
	/* device_Mode is called whenever the graphics engine 	*/
2269
	/* starts drawing (mode=1) or stops drawing (mode=1)	*/
2270
	/* the device is not required to do anything		*/
2271
	/********************************************************/
2272
 
2273
/* Set Graphics mode - not needed for X11 */
17050 ripley 2274
static void GA_Mode(int mode, NewDevDesc *dd)
4394 ripley 2275
{
2276
}
2277
 
2278
	/********************************************************/
2279
	/* i don't know what this is for and i can't find it	*/
2280
	/* being used anywhere, but i'm loath to kill it in	*/
2281
	/* case i'm missing something important			*/
2282
	/********************************************************/
2283
 
2284
/* Hold the Picture Onscreen - not needed for X11 */
17050 ripley 2285
static void GA_Hold(NewDevDesc *dd)
4394 ripley 2286
{
2287
}
2288
 
2289
 
2290
	/********************************************************/
2291
	/* the device-driver entry point is given a device 	*/
2292
	/* description structure that it must set up.  this 	*/
2293
	/* involves several important jobs ...			*/
2294
	/* (1) it must ALLOCATE a new device-specific parameters*/
2295
	/* structure and FREE that structure if anything goes	*/
2296
	/* wrong (i.e., it won't report a successful setup to	*/
2297
	/* the graphics engine (the graphics engine is NOT	*/
2298
	/* responsible for allocating or freeing device-specific*/
2299
	/* resources or parameters)				*/
2300
	/* (2) it must initialise the device-specific resources */
2301
	/* and parameters (mostly done by calling device_Open)	*/
2302
	/* (3) it must initialise the generic graphical 	*/
2303
	/* parameters that are not initialised by GInit (because*/
2304
	/* only the device knows what values they should have)	*/
2305
	/* see Graphics.h for the official list of these	*/
2306
	/* (4) it may reset generic graphics parameters that	*/
2307
	/* have already been initialised by GInit (although you	*/
2308
	/* should know what you are doing if you do this)	*/
2309
	/* (5) it must attach the device-specific parameters	*/
2310
        /* structure to the device description structure	*/
11597 ripley 2311
	/* e.g., dd->deviceSpecific = (void *) xd;		*/
4394 ripley 2312
	/* (6) it must FREE the overall device description if 	*/
2313
	/* it wants to bail out to the top-level		*/
2314
	/* the graphics engine is responsible for allocating 	*/
2315
	/* the device description and freeing it in most cases	*/
2316
	/* but if the device driver freaks out it needs to do 	*/
2317
	/* the clean-up itself					*/
2318
	/********************************************************/
2319
 
2320
 
2321
 
22270 ripley 2322
Rboolean GADeviceDriver(NewDevDesc *dd, char *display, double width,
2323
			double height, double pointsize,
2324
			Rboolean recording, int resize, int canvas,
27526 ripley 2325
			double gamma, int xpos, int ypos, Rboolean buffered,
2326
			SEXP psenv)
4394 ripley 2327
{
2328
    /* if need to bail out with some sort of "error" then */
2329
    /* must free(dd) */
2330
 
2331
    int   ps;
8971 ripley 2332
    gadesc *xd;
4394 ripley 2333
    rect  rr;
6994 pd 2334
    int a=0, d=0, w=0;
4394 ripley 2335
 
2336
    /* allocate new device description */
8971 ripley 2337
    if (!(xd = (gadesc *) malloc(sizeof(gadesc))))
11067 maechler 2338
	return FALSE;
4394 ripley 2339
 
2340
    /* from here on, if need to bail out with "error", must also */
2341
    /* free(xd) */
2342
 
2343
    /* Font will load at first use  */
2344
 
2345
    ps = pointsize;
24205 ripley 2346
    if (ps < 6 || ps > 48)
4394 ripley 2347
	ps = 12;
2348
    ps = 2 * (ps / 2);
2349
    xd->fontface = -1;
2350
    xd->fontsize = -1;
6625 guido 2351
    xd->basefontsize = ps ;
17050 ripley 2352
    dd->startfont = 1;
2353
    dd->startps = ps;
17165 murrell 2354
    dd->startlty = LTY_SOLID;
2355
    dd->startgamma = gamma;
4394 ripley 2356
 
2357
    /* Start the Device Driver and Hardcopy.  */
2358
 
17165 murrell 2359
    if (!GA_Open(dd, xd, display, width, height, recording, resize, canvas,
24724 ripley 2360
		 gamma, xpos, ypos)) {
6430 guido 2361
	free(xd);
11067 maechler 2362
	return FALSE;
4394 ripley 2363
    }
12778 pd 2364
    dd->deviceSpecific = (void *) xd;
4394 ripley 2365
    /* Set up Data Structures  */
2366
 
17050 ripley 2367
    dd->newDevStruct = 1;
4394 ripley 2368
 
17050 ripley 2369
    dd->open = GA_Open;
2370
    dd->close = GA_Close;
2371
    dd->activate = GA_Activate;
2372
    dd->deactivate = GA_Deactivate;
2373
    dd->size = GA_Size;
2374
    dd->newPage = GA_NewPage;
2375
    dd->clip = GA_Clip;
2376
    dd->strWidth = GA_StrWidth;
2377
    dd->text = GA_Text;
2378
    dd->rect = GA_Rect;
2379
    dd->circle = GA_Circle;
2380
    dd->line = GA_Line;
2381
    dd->polyline = GA_Polyline;
2382
    dd->polygon = GA_Polygon;
2383
    dd->locator = GA_Locator;
2384
    dd->mode = GA_Mode;
2385
    dd->hold = GA_Hold;
2386
    dd->metricInfo = GA_MetricInfo;
2387
 
4394 ripley 2388
    /* set graphics parameters that must be set by device driver */
2389
    /* Window Dimensions in Pixels */
2390
    rr = getrect(xd->gawin);
17050 ripley 2391
    dd->left = (xd->kind == PRINTER) ? rr.x : 0;	/* left */
26839 ripley 2392
    dd->right = dd->left + rr.width - 0.0001;	/* right */
17050 ripley 2393
    dd->top = (xd->kind == PRINTER) ? rr.y : 0;	/* top */
26839 ripley 2394
    dd->bottom = dd->top + rr.height - 0.0001;	/* bottom */
4394 ripley 2395
 
12256 pd 2396
    if (resize == 3) { /* might have got a shrunken window */
22270 ripley 2397
	int iw = width/pixelWidth(NULL) + 0.5,
12256 pd 2398
	    ih = height/pixelHeight(NULL) + 0.5;
17050 ripley 2399
	xd->origWidth = dd->right = iw;
2400
	xd->origHeight = dd->bottom = ih;
12256 pd 2401
    }
4394 ripley 2402
 
2403
    /* Nominal Character Sizes in Pixels */
6994 pd 2404
    gcharmetric(xd->gawin, xd->font, -1, &a, &d, &w);
17050 ripley 2405
    dd->cra[0] = w * xd->rescale_factor;
2406
    dd->cra[1] = (a + d) * xd->rescale_factor;
12778 pd 2407
    /* Set basefont to full size: now allow for initial re-scale */
2408
    xd->wanteddpi = xd->truedpi * xd->rescale_factor;
11597 ripley 2409
 
4394 ripley 2410
    /* Character Addressing Offsets */
2411
    /* These are used to plot a single plotting character */
2412
    /* so that it is exactly over the plotting point */
2413
 
17050 ripley 2414
    dd->xCharOffset = 0.50;
2415
    dd->yCharOffset = 0.40;
2416
    dd->yLineBias = 0.1;
4394 ripley 2417
 
2418
    /* Inches per raster unit */
2419
 
13674 ripley 2420
    if (R_finite(user_xpinch) && user_xpinch > 0.0)
17050 ripley 2421
	dd->ipr[0] = 1.0/user_xpinch;
13674 ripley 2422
    else
17050 ripley 2423
	dd->ipr[0] = pixelWidth(xd->gawin);
13674 ripley 2424
    if (R_finite(user_ypinch) && user_ypinch > 0.0)
17050 ripley 2425
	dd->ipr[1] = 1.0/user_ypinch;
13674 ripley 2426
    else
17050 ripley 2427
	dd->ipr[1] = pixelHeight(xd->gawin);
4394 ripley 2428
 
2429
    /* Device capabilities */
2430
    /* Clipping is problematic for X11 */
2431
    /* Graphics is clipped, text is not */
2432
 
17050 ripley 2433
    dd->canResizePlot= TRUE;
2434
    dd->canChangeFont= FALSE;
2435
    dd->canRotateText= TRUE;
2436
    dd->canResizeText= TRUE;
2437
    dd->canClip= TRUE;
2438
    dd->canHAdj = 1; /* 0, 0.5, 1 */
17173 murrell 2439
    dd->canChangeGamma = TRUE;
4394 ripley 2440
 
8971 ripley 2441
    /* initialise device description (most of the work */
2442
    /* has been done in GA_Open) */
4394 ripley 2443
 
12256 pd 2444
    xd->resize = (resize == 3);
2445
    xd->locator = FALSE;
27143 ripley 2446
    xd->buffered = buffered;
27526 ripley 2447
    xd->psenv = psenv;
26109 ripley 2448
    {
2449
	SEXP timeouts = GetOption(install("windowsTimeouts"), R_NilValue);
2450
	if(isInteger(timeouts)){
27143 ripley 2451
	    xd->timeafter = INTEGER(timeouts)[0];
26109 ripley 2452
	    xd->timesince = INTEGER(timeouts)[1];
2453
	} else {
2454
	    warning("option `windowsTimeouts' should be integer");
2455
	    xd->timeafter = 100;
2456
	    xd->timesince = 500;
2457
	}
2458
    }
25660 ripley 2459
    dd->displayListOn = (xd->kind == SCREEN);
6430 guido 2460
    if (RConsole && (xd->kind!=SCREEN)) show(RConsole);
11067 maechler 2461
    return TRUE;
4394 ripley 2462
}
6098 pd 2463
 
2464
SEXP do_saveDevga(SEXP call, SEXP op, SEXP args, SEXP env)
2465
{
2466
    SEXP filename, type;
25312 ripley 2467
    char *fn, *tp, display[550];
6098 pd 2468
    int device;
17050 ripley 2469
    NewDevDesc* dd;
6098 pd 2470
 
2471
    checkArity(op, args);
2472
    device = asInteger(CAR(args));
2473
    if(device < 1 || device > NumDevices())
2474
	errorcall(call, "invalid device number");
17050 ripley 2475
    dd = ((GEDevDesc*) GetDevice(device - 1))->dev;
6098 pd 2476
    if(!dd) errorcall(call, "invalid device");
2477
    filename = CADR(args);
2478
    if (!isString(filename) || LENGTH(filename) != 1)
6192 maechler 2479
	errorcall(call, "invalid filename argument");
10172 luke 2480
    fn = CHAR(STRING_ELT(filename, 0));
6098 pd 2481
    fixslash(fn);
2482
    type = CADDR(args);
2483
    if (!isString(type) || LENGTH(type) != 1)
6994 pd 2484
	errorcall(call, "invalid type argument");
10172 luke 2485
    tp = CHAR(STRING_ELT(type, 0));
6192 maechler 2486
 
6430 guido 2487
    if(!strcmp(tp, "png")) {
2488
	SaveAsPng(dd, fn);
6458 guido 2489
    } else if (!strcmp(tp,"bmp")) {
2490
        SaveAsBmp(dd,fn);
6430 guido 2491
    } else if(!strcmp(tp, "jpeg") || !strcmp(tp,"jpg")) {
2492
      /*Default quality suggested in libjpeg*/
22270 ripley 2493
        SaveAsJpeg(dd, 75, fn);
6098 pd 2494
    } else if (!strcmp(tp, "wmf")) {
25312 ripley 2495
	if(strlen(fn) > 512) {
2496
	    askok("file path selected is too long: only 512 bytes are allowed");
2497
	    return R_NilValue;
2498
	}
6098 pd 2499
	sprintf(display, "win.metafile:%s", fn);
2500
	SaveAsWin(dd, display);
2501
    } else if (!strcmp(tp, "ps")) {
2502
	SaveAsPostscript(dd, fn);
15641 ripley 2503
    } else if (!strcmp(tp, "pdf")) {
2504
	SaveAsPDF(dd, fn);
6192 maechler 2505
    } else
2506
	errorcall(call, "unknown type");
6098 pd 2507
    return R_NilValue;
2508
}
6430 guido 2509
 
2510
 
2511
/* Rbitmap  */
2512
#define BITMAP_DLL_NAME "\\BIN\\RBITMAP.DLL\0"
2513
typedef int (*R_SaveAsBitmap)();
6458 guido 2514
static R_SaveAsBitmap R_SaveAsPng, R_SaveAsJpeg, R_SaveAsBmp;
8971 ripley 2515
static int RbitmapAlreadyLoaded = 0;
6430 guido 2516
static HINSTANCE hRbitmapDll;
2517
 
2518
static int Load_Rbitmap_Dll()
22270 ripley 2519
{
8971 ripley 2520
    if (!RbitmapAlreadyLoaded) {
2521
	char szFullPath[PATH_MAX];
2522
	strcpy(szFullPath, R_HomeDir());
2523
	strcat(szFullPath, BITMAP_DLL_NAME);
22270 ripley 2524
	if (((hRbitmapDll = LoadLibrary(szFullPath)) != NULL) &&
8971 ripley 2525
	    ((R_SaveAsPng=
22270 ripley 2526
	      (R_SaveAsBitmap)GetProcAddress(hRbitmapDll, "R_SaveAsPng"))
8971 ripley 2527
	     != NULL) &&
2528
	    ((R_SaveAsBmp=
22270 ripley 2529
	      (R_SaveAsBitmap)GetProcAddress(hRbitmapDll, "R_SaveAsBmp"))
8971 ripley 2530
	     != NULL) &&
2531
	    ((R_SaveAsJpeg=
22270 ripley 2532
	      (R_SaveAsBitmap)GetProcAddress(hRbitmapDll, "R_SaveAsJpeg"))
8971 ripley 2533
	     != NULL)) {
2534
	    RbitmapAlreadyLoaded = 1;
22270 ripley 2535
	} else {
8971 ripley 2536
	    if (hRbitmapDll != NULL) FreeLibrary(hRbitmapDll);
2537
	    RbitmapAlreadyLoaded= -1;
2538
	}
2539
    }
22270 ripley 2540
    return (RbitmapAlreadyLoaded>0);
6430 guido 2541
}
2542
 
6648 ripley 2543
void UnLoad_Rbitmap_Dll()
6430 guido 2544
{
2545
    if (RbitmapAlreadyLoaded) FreeLibrary(hRbitmapDll);
2546
    RbitmapAlreadyLoaded = 0;
2547
}
2548
 
26221 ripley 2549
static int png_rows = 0;
2550
 
2551
static unsigned long privategetpixel2(void *d,int i, int j)
2552
{
2553
    rgb c;
2554
    c = ((rgb *)d)[i*png_rows + j];
2555
    return c;
2556
}
2557
 
6430 guido 2558
/* This is the device version */
17050 ripley 2559
static void SaveAsBitmap(NewDevDesc *dd)
6430 guido 2560
{
26221 ripley 2561
    rect r, r2;
8971 ripley 2562
    gadesc *xd = (gadesc *) dd->deviceSpecific;
26221 ripley 2563
    unsigned char *data;
27143 ripley 2564
 
8971 ripley 2565
    r = ggetcliprect(xd->gawin);
26221 ripley 2566
    gsetcliprect(xd->gawin, r2 = getrect(xd->gawin));
21024 ripley 2567
    if(xd->fp) {
26905 ripley 2568
	getbitmapdata2(xd->gawin, &data);
2569
	if(data) {
2570
	    png_rows = r2.width;
26221 ripley 2571
	    if (xd->kind == PNG)
26905 ripley 2572
		R_SaveAsPng(data, xd->windowWidth, xd->windowHeight,
2573
			    privategetpixel2, 0, xd->fp,
26221 ripley 2574
			    R_OPAQUE(xd->bg) ? 0 : xd->pngtrans) ;
2575
	    else if (xd->kind == JPEG)
26905 ripley 2576
		R_SaveAsJpeg(data, xd->windowWidth, xd->windowHeight,
2577
			     privategetpixel2, 0, xd->quality, xd->fp) ;
26221 ripley 2578
	    else
26905 ripley 2579
		R_SaveAsBmp(data, xd->windowWidth, xd->windowHeight,
2580
			    privategetpixel2, 0, xd->fp);
2581
	    free(data);
2582
	} else
2583
	    warning("processing of the plot ran out of memory");
21024 ripley 2584
	fclose(xd->fp);
2585
    }
8971 ripley 2586
    gsetcliprect(xd->gawin, r);
21024 ripley 2587
    xd->fp = NULL;
6430 guido 2588
}
2589
 
21024 ripley 2590
/* These are the menu item versions */
17050 ripley 2591
static void SaveAsPng(NewDevDesc *dd,char *fn)
6430 guido 2592
{
8971 ripley 2593
    FILE *fp;
26839 ripley 2594
    rect r, r2;
2595
    unsigned char *data;
8971 ripley 2596
    gadesc *xd = (gadesc *) dd->deviceSpecific;
26839 ripley 2597
 
8971 ripley 2598
    if (!Load_Rbitmap_Dll()) {
2599
	R_ShowMessage("Impossible to load Rbitmap.dll");
2600
	return;
2601
    }
15641 ripley 2602
    if ((fp=fopen(fn, "wb")) == NULL) {
8971 ripley 2603
	char msg[MAX_PATH+32];
6430 guido 2604
 
15641 ripley 2605
	strcpy(msg, "Impossible to open ");
8971 ripley 2606
	strncat(msg, fn, MAX_PATH);
2607
	R_ShowMessage(msg);
2608
	return;
2609
    }
2610
    r = ggetcliprect(xd->bm);
26839 ripley 2611
    gsetcliprect(xd->bm, r2 = getrect(xd->bm));
26905 ripley 2612
    getbitmapdata2(xd->bm, &data);
2613
    if(data) {
2614
	png_rows = r2.width;
2615
	R_SaveAsPng(data, xd->windowWidth, xd->windowHeight,
2616
		    privategetpixel2, 0, fp, 0) ;
2617
	free(data);
26839 ripley 2618
    } else
26905 ripley 2619
	warning("processing of the plot ran out of memory");
15641 ripley 2620
    /* R_OPAQUE(xd->bg) ? 0 : xd->canvascolor) ; */
8971 ripley 2621
    gsetcliprect(xd->bm, r);
2622
    fclose(fp);
6430 guido 2623
}
2624
 
17050 ripley 2625
static void SaveAsJpeg(NewDevDesc *dd,int quality,char *fn)
6430 guido 2626
{
8971 ripley 2627
    FILE *fp;
26839 ripley 2628
    rect r, r2;
2629
    unsigned char *data;
8971 ripley 2630
    gadesc *xd = (gadesc *) dd->deviceSpecific;
26839 ripley 2631
 
8971 ripley 2632
    if (!Load_Rbitmap_Dll()) {
2633
	R_ShowMessage("Impossible to load Rbitmap.dll");
2634
	return;
2635
    }
2636
    if ((fp=fopen(fn,"wb")) == NULL) {
2637
	char msg[MAX_PATH+32];
2638
	strcpy(msg, "Impossible to open ");
2639
	strncat(msg, fn, MAX_PATH);
2640
	R_ShowMessage(msg);
2641
	return;
2642
    }
2643
    r = ggetcliprect(xd->bm);
26839 ripley 2644
    gsetcliprect(xd->bm, r2 = getrect(xd->bm));
26905 ripley 2645
    getbitmapdata2(xd->bm, &data);
2646
    if(data) {
2647
	png_rows = r2.width;
2648
	R_SaveAsJpeg(data,xd->windowWidth, xd->windowHeight,
2649
		     privategetpixel2, 0, quality, fp) ;
2650
	free(data);
26839 ripley 2651
    } else
26905 ripley 2652
	warning("processing of the plot ran out of memory");
8971 ripley 2653
    gsetcliprect(xd->bm, r);
2654
    fclose(fp);
6430 guido 2655
}
2656
 
2657
 
17050 ripley 2658
static void SaveAsBmp(NewDevDesc *dd,char *fn)
6458 guido 2659
{
8971 ripley 2660
    FILE *fp;
26839 ripley 2661
    rect r, r2;
2662
    unsigned char *data;
8971 ripley 2663
    gadesc *xd = (gadesc *) dd->deviceSpecific;
26839 ripley 2664
 
8971 ripley 2665
    if (!Load_Rbitmap_Dll()) {
2666
	R_ShowMessage("Impossible to load Rbitmap.dll");
2667
	return;
2668
    }
17050 ripley 2669
    if ((fp=fopen(fn, "wb")) == NULL) {
8971 ripley 2670
	char msg[MAX_PATH+32];
6430 guido 2671
 
17050 ripley 2672
	strcpy(msg, "Impossible to open ");
8971 ripley 2673
	strncat(msg, fn, MAX_PATH);
2674
	R_ShowMessage(msg);
2675
	return;
2676
    }
2677
    r = ggetcliprect(xd->bm);
26839 ripley 2678
    gsetcliprect(xd->bm, r2 = getrect(xd->bm));
26905 ripley 2679
 
2680
    getbitmapdata2(xd->bm, &data);
2681
    if(data) {
2682
	png_rows = r2.width;
2683
	R_SaveAsBmp(data, xd->windowWidth, xd->windowHeight,
2684
		    privategetpixel2, 0, fp) ;
2685
	free(data);
26839 ripley 2686
    } else
26905 ripley 2687
	warning("processing of the plot ran out of memory");
8971 ripley 2688
    gsetcliprect(xd->bm, r);
2689
    fclose(fp);
6458 guido 2690
}
11597 ripley 2691
 
14752 ripley 2692
#include "Startup.h"
2693
extern UImode CharacterMode;
2694
 
11597 ripley 2695
SEXP do_bringtotop(SEXP call, SEXP op, SEXP args, SEXP env)
2696
{
27357 murdoch 2697
    int dev, stay;
17050 ripley 2698
    GEDevDesc *gdd;
11597 ripley 2699
    gadesc *xd;
2700
 
2701
    checkArity(op, args);
2702
    dev = asInteger(CAR(args));
27357 murdoch 2703
    stay = asInteger(CADR(args));
2704
 
14752 ripley 2705
    if(dev == -1) { /* console */
27357 murdoch 2706
	if(CharacterMode == RGui) BringToTop(RConsole, stay);
14752 ripley 2707
    } else {
25253 ripley 2708
	if(dev < 1 || dev > R_MaxDevices || dev == NA_INTEGER)
14752 ripley 2709
	    errorcall(call, "invalid value of `which'");
17050 ripley 2710
	gdd = (GEDevDesc *) GetDevice(dev - 1);
2711
	if(!gdd) errorcall(call, "invalid device");
2712
	xd = (gadesc *) gdd->dev->deviceSpecific;
14752 ripley 2713
	if(!xd) errorcall(call, "invalid device");
27357 murdoch 2714
	if(stay && ismdi()) error("requires SDI mode");
2715
	BringToTop(xd->gawin, stay);
14752 ripley 2716
    }
11597 ripley 2717
    return R_NilValue;
2718
}