The R Project SVN R

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
13974 hornik 1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
18793 ripley 3
 *  Copyright (C) 1998-2002   Lyndon Drake
13974 hornik 4
 *                            and the R Development Core Team
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
18
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 */
20
 
18793 ripley 21
#ifdef HAVE_CONFIG_H
22
# include <config.h>
23
#endif
24
 
20443 martyn 25
/* Avoid braced-groups warning from -Wall */
26
#define G_STMT_START do
27
#define G_STMT_END   while(0)
28
 
13974 hornik 29
#include <gnome.h>
16018 martyn 30
#include <math.h>
13974 hornik 31
 
32
#include "Defn.h"
33
#include "Graphics.h"
34
#include "Rdevices.h"
35
#include "devGNOME.h"
36
#include "terminal.h"
20417 martyn 37
#include "terminal-functions.h"
13974 hornik 38
 
39
#define CURSOR		GDK_CROSSHAIR		/* Default cursor */
40
#define MM_PER_INCH	25.4			/* mm -> inch conversion */
41
 
42
/* routines from here */
43
 
44
/* Device driver actions */
16945 pd 45
static void GNOME_Activate(NewDevDesc *dd);
46
static void GNOME_Circle(double x, double y, double r,
27236 murrell 47
			 R_GE_gcontext *gc,
48
			 NewDevDesc *dd);
16945 pd 49
static void GNOME_Clip(double x0, double x1, double y0, double y1, 
50
		     NewDevDesc *dd);
51
static void GNOME_Close(NewDevDesc *dd);
52
static void GNOME_Deactivate(NewDevDesc *dd);
53
static void GNOME_Hold(NewDevDesc *dd);
54
static Rboolean GNOME_Locator(double *x, double *y, NewDevDesc *dd);
55
static void GNOME_Line(double x1, double y1, double x2, double y2,
27236 murrell 56
		       R_GE_gcontext *gc,
57
		       NewDevDesc *dd);
58
static void GNOME_MetricInfo(int c,
59
			     R_GE_gcontext *gc,
60
			     double* ascent, double* descent,
61
			     double* width, NewDevDesc *dd);
16945 pd 62
static void GNOME_Mode(int mode, NewDevDesc *dd);
27236 murrell 63
static void GNOME_NewPage(R_GE_gcontext *gc, NewDevDesc *dd);
16945 pd 64
static void GNOME_Polygon(int n, double *x, double *y, 
27236 murrell 65
			  R_GE_gcontext *gc,
66
			  NewDevDesc *dd);
16945 pd 67
static void GNOME_Polyline(int n, double *x, double *y, 
27236 murrell 68
			   R_GE_gcontext *gc,
69
			   NewDevDesc *dd);
16945 pd 70
static void GNOME_Rect(double x0, double y0, double x1, double y1,
27236 murrell 71
		       R_GE_gcontext *gc,
72
		       NewDevDesc *dd);
17102 martyn 73
static void GNOME_Resize(NewDevDesc *dd);
16945 pd 74
static void GNOME_Size(double *left, double *right,
75
		     double *bottom, double *top,
76
		     NewDevDesc *dd);
27236 murrell 77
static double GNOME_StrWidth(char *str, 
78
			     R_GE_gcontext *gc,
79
			     NewDevDesc *dd);
16945 pd 80
static void GNOME_Text(double x, double y, char *str, 
27236 murrell 81
		       double rot, double hadj, 
82
		       R_GE_gcontext *gc,
83
		       NewDevDesc *dd);
16945 pd 84
static Rboolean GNOME_Open(NewDevDesc*, gnomeDesc*, char*, double, double);
13974 hornik 85
 
86
			/* Pixel Dimensions (Inches) */
87
 
88
static double pixelWidth(void)
89
{
90
    double width, widthMM;
91
    width = gdk_screen_width();
92
    widthMM = gdk_screen_width_mm();
93
    return ((double)widthMM / (double)width) / MM_PER_INCH;
94
}
95
 
96
static double pixelHeight(void)
97
{
98
    double height, heightMM;
99
    height = gdk_screen_height();
100
    heightMM = gdk_screen_height_mm();
101
    return ((double)heightMM / (double)height) / MM_PER_INCH;
102
}
103
 
104
/* font stuff */
105
 
16018 martyn 106
static char *fontname_R6 = "-adobe-helvetica-%s-%s-*-*-*-%d-*-*-*-*-*-*";
107
static char *symbolname = "-adobe-symbol-*-*-*-*-*-%d-*-*-*-*-*-*";
108
static char *fixedname = "fixed";
13974 hornik 109
 
16018 martyn 110
static char *slant[] = {"r", "o"};
111
static char *weight[] = {"medium", "bold"};
13974 hornik 112
 
16018 martyn 113
static char *fontname = NULL;
114
static GHashTable *font_htab = NULL;
115
 
19237 martyn 116
static int RDeviceNumber(NewDevDesc *dd)
117
{
118
    return devNumber((DevDesc*) dd) + 1;
119
}
120
 
16018 martyn 121
static GdkFont *RGTKLoadFont(char *font)
13974 hornik 122
{
16018 martyn 123
  GdkFont *tmp_font;
124
 
125
  g_return_val_if_fail(font_htab, NULL);
126
  tmp_font = g_hash_table_lookup(font_htab, (gpointer) font);
13974 hornik 127
 
16018 martyn 128
  if(!tmp_font)
129
    {
130
      if((tmp_font = gdk_font_load(font)))
131
	{
132
	  g_hash_table_insert(font_htab, (gpointer) g_strdup(font),
133
			      (gpointer) tmp_font);
134
	}
135
    }
136
 
137
  return tmp_font;
13974 hornik 138
}
139
 
16018 martyn 140
static gboolean SetBaseFont (gnomeDesc *gd)
13974 hornik 141
{
16018 martyn 142
  gd->fontface = 1;
143
  gd->fontsize = 12;
144
  gd->usefixed = 0;
145
 
146
  if (font_htab == NULL)
147
    {
148
      font_htab = g_hash_table_new(g_str_hash, g_str_equal);
149
    }
13974 hornik 150
 
16018 martyn 151
  fontname = g_strdup_printf(fontname_R6, weight[0], slant[0], 
152
			     gd->fontsize * 10);
153
  gd->font = RGTKLoadFont(fontname);
154
  g_free(fontname);
13974 hornik 155
 
16018 martyn 156
  if(gd->font)
157
    return TRUE;
13974 hornik 158
 
16018 martyn 159
  gd->usefixed = 1;
160
  gd->font = RGTKLoadFont(fixedname);
13974 hornik 161
 
16018 martyn 162
  if(gd->font)
163
    return TRUE;
164
 
165
  return FALSE;
166
}
13974 hornik 167
 
16018 martyn 168
#define SMALLEST 8
169
#define LARGEST 24
13974 hornik 170
 
16945 pd 171
static void SetFont(NewDevDesc *dd, gint face, gint size)
16018 martyn 172
{
173
  GdkFont *tmp_font;
174
  gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
175
 
176
  if (face < 1 || face > 5)
177
    face = 1;
178
 
179
  size = 2 * size / 2;
180
  if (size < SMALLEST)
181
    size = SMALLEST;
182
  else if (size > LARGEST)
183
    size = LARGEST;
184
 
185
  if (!gd->usefixed)
186
    {
187
      if (face == 5)
188
	fontname = g_strdup_printf(symbolname, 10 * size);
189
      else
190
	fontname = g_strdup_printf(fontname_R6,
191
				   weight[(face-1)%2],
192
				   slant[((face-1)/2)%2],
193
				   10 * size);
194
 
195
      tmp_font = RGTKLoadFont(fontname);
196
      g_free(fontname);
197
 
198
      if (tmp_font)
199
	{
200
	  gd->font = tmp_font;
201
	  gd->fontface = face;
202
	  gd->fontsize = size;
203
	}
204
      else
205
	{
206
	  error("gdk font at size %d could not be loaded", size);
207
	}
208
    }
13974 hornik 209
}
210
 
16018 martyn 211
/* Get GNOME color specification from R color  */
212
static guint32 Color_RGBA(int color)
213
{
214
  int red, green, blue, alpha;
215
 
216
  red = R_RED(color);
217
  green = R_GREEN(color);
218
  blue = R_BLUE(color);
219
  alpha = 255 - R_ALPHA(color);
220
 
221
  return GNOME_CANVAS_COLOR_A(red, green, blue, alpha);
222
}
223
 
17088 martyn 224
/* line type 
225
   fixme: not currently used 
16945 pd 226
static void SetLineType(NewDevDesc *dd, int newlty, int newlwd)
16018 martyn 227
{
228
  static gchar dashlist[8];
229
  gint i, j;
230
  gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
231
 
232
  if (newlty != gd->lty || newlwd != gd->lwd)
233
    {
234
      if (newlty == 0)
235
	{
236
	  if(newlwd <= 1)
237
	    newlwd = 0;
238
 
239
	  gdk_gc_set_line_attributes(gd->wgc, newlwd,
240
				     GDK_LINE_SOLID,
241
				     GDK_CAP_BUTT,
242
				     GDK_JOIN_ROUND);
243
	}
244
      else
245
	{
246
	  if (newlwd < 1)
247
	    newlwd = 1;
248
 
249
	  for(i = 0; (i < 8) && (newlty != 0); i++) {
250
	    j = newlty & 15;
251
	    if(j == 0) j = 1;
252
	    j = j * newlwd;
253
	    if(j > 255) j = 255;
254
	    dashlist[i] = j;
255
	    newlty = newlty >> 4;
256
	  }
257
 
17088 martyn 258
	  // set dashes
16018 martyn 259
	  gdk_gc_set_dashes(gd->wgc, 0, dashlist, i);
260
	  gdk_gc_set_line_attributes(gd->wgc, newlwd,
261
				     GDK_LINE_ON_OFF_DASH,
262
				     GDK_CAP_BUTT,
263
				     GDK_JOIN_ROUND);
264
	}
265
    }
266
}
17088 martyn 267
*/
16018 martyn 268
 
13974 hornik 269
/* signal functions */
270
 
16018 martyn 271
static gint configure_event(GtkWidget *widget, GdkEventConfigure *event,
272
			    gpointer data) 
13974 hornik 273
{
16945 pd 274
  NewDevDesc *dd;
16018 martyn 275
  gnomeDesc *gd;
13974 hornik 276
 
16945 pd 277
  dd = (NewDevDesc *) data;
16018 martyn 278
  g_return_val_if_fail(dd != NULL, FALSE);
13974 hornik 279
 
16018 martyn 280
  gd = (gnomeDesc *) dd->deviceSpecific;
281
  g_return_val_if_fail(gd != NULL, FALSE);
282
  g_return_val_if_fail(gd->canvas != NULL, FALSE);
283
  g_return_val_if_fail(GNOME_IS_CANVAS(gd->canvas), FALSE);
13974 hornik 284
 
17102 martyn 285
  if((gd->windowWidth != gd->canvas->allocation.width) ||
286
     (gd->windowHeight != gd->canvas->allocation.height))
16018 martyn 287
    {
17102 martyn 288
      gd->windowWidth = gd->canvas->allocation.width;
289
      gd->windowHeight = gd->canvas->allocation.height;
16018 martyn 290
      gd->resize = TRUE;
13974 hornik 291
    }
292
 
16018 martyn 293
  return FALSE;
13974 hornik 294
}
295
 
17102 martyn 296
static gint expose_event(GtkWidget *widget, GdkEventExpose *event,
297
			 gpointer data)
298
{
299
  NewDevDesc *dd;
300
  gnomeDesc *gd;
301
 
302
  dd = (NewDevDesc *) data;
303
  g_return_val_if_fail(dd, FALSE);
304
  gd = (gnomeDesc *) dd->deviceSpecific;
305
 
306
  if (gd->resize)
307
    {
308
      GNOME_Resize(dd); 
309
    }
310
 
17115 murrell 311
  GEplayDisplayList((GEDevDesc*) GetDevice(devNumber((DevDesc*)dd)));
17102 martyn 312
 
313
  return FALSE;
314
}
315
 
13974 hornik 316
static gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) 
317
{
16945 pd 318
    NewDevDesc *dd;
19237 martyn 319
    gchar *cmd;
13974 hornik 320
 
16945 pd 321
    dd = (NewDevDesc *) data;
13974 hornik 322
    g_return_val_if_fail(dd != NULL, FALSE);
323
 
19237 martyn 324
    cmd = g_strdup_printf("dev.off(%d)\n", RDeviceNumber(dd));
325
    R_gtk_terminal_run(cmd);
326
    g_free(cmd);
13974 hornik 327
 
328
    return TRUE;
329
}
330
 
16018 martyn 331
static void toolbar_activate_cb(GtkWidget *widget, gpointer data) 
13974 hornik 332
{
16945 pd 333
    NewDevDesc *dd;
19237 martyn 334
    gchar *cmd;
13974 hornik 335
 
16945 pd 336
    g_return_if_fail(data);
337
    dd = (NewDevDesc *) data;
13974 hornik 338
 
19237 martyn 339
    cmd = g_strdup_printf("dev.set(%d)\n", RDeviceNumber(dd));
340
    R_gtk_terminal_run(cmd);
341
    g_free(cmd);
13974 hornik 342
}
343
 
16018 martyn 344
static void toolbar_close_cb(GtkWidget *widget, gpointer data) 
13974 hornik 345
{
16945 pd 346
    NewDevDesc *dd;
19237 martyn 347
    gchar *cmd;
13974 hornik 348
 
19237 martyn 349
    g_return_if_fail(data);
350
 
16945 pd 351
    dd = (NewDevDesc *) data;
19237 martyn 352
    cmd = g_strdup_printf("dev.off(%d)\n", RDeviceNumber(dd));
353
    R_gtk_terminal_run(cmd);
354
    g_free(cmd);
13974 hornik 355
}
356
 
16018 martyn 357
static void
358
save_ok (GtkWidget *ok_button, gpointer data)
359
{
19237 martyn 360
    NewDevDesc *dd;
361
    GtkFileSelection *fsel = GTK_FILE_SELECTION(data);
362
    char *filename = gtk_file_selection_get_filename(fsel);
363
    char *cmd;
16018 martyn 364
 
19237 martyn 365
    if (!filename)
366
	return;
367
 
368
    dd = gtk_object_get_user_data(GTK_OBJECT(fsel));
369
    cmd = g_strdup_printf("dev.set(%d)\ndev.print(file=\"%s\")\n",
370
			  RDeviceNumber(dd), filename);
371
    R_gtk_terminal_run(cmd);
372
 
373
    g_free(cmd);
374
    gtk_widget_destroy (GTK_WIDGET(fsel));
375
 
16018 martyn 376
}
377
 
378
 
379
static void toolbar_save_as_cb(GtkWidget *widget, gpointer data)
380
{
19237 martyn 381
    NewDevDesc *dd = (NewDevDesc*) data;
382
    gnomeDesc *gd = (gnomeDesc*) dd->deviceSpecific;
16018 martyn 383
 
19237 martyn 384
    GtkFileSelection *fsel = 
385
	GTK_FILE_SELECTION(gtk_file_selection_new("Save as PostScript"));
16018 martyn 386
 
19237 martyn 387
    gtk_object_set_user_data(GTK_OBJECT(fsel), dd);
388
    gtk_file_selection_set_filename(fsel, "Rplots.ps");
389
 
390
    gtk_signal_connect(GTK_OBJECT(fsel->ok_button), "clicked",
391
		       GTK_SIGNAL_FUNC(save_ok), fsel);
392
    gtk_signal_connect_object(GTK_OBJECT(fsel->cancel_button), "clicked",
393
			      GTK_SIGNAL_FUNC(gtk_widget_destroy),
394
			      GTK_OBJECT(fsel));
16018 martyn 395
 
396
 
19237 martyn 397
    gtk_window_position(GTK_WINDOW(fsel), GTK_WIN_POS_MOUSE);
398
    gtk_window_set_transient_for(GTK_WINDOW(fsel), GTK_WINDOW(gd->window));
399
    gtk_widget_show(GTK_WIDGET(fsel));
16018 martyn 400
}
401
 
402
static void toolbar_print_cb (GtkWidget *widget, gpointer data)
403
{
19237 martyn 404
    NewDevDesc *dd = (NewDevDesc*) data;
405
    gchar *cmd;
406
 
27117 ripley 407
    cmd = g_strdup_printf("dev.set(%d)\ndev.print()",
19237 martyn 408
			  RDeviceNumber(dd));
409
    R_gtk_terminal_run(cmd);
410
    g_free(cmd);
16018 martyn 411
}
412
 
13974 hornik 413
static GnomeUIInfo graphics_toolbar[] =
414
{
19237 martyn 415
    { GNOME_APP_UI_ITEM, "Activate", "Make this window the current device",
20443 martyn 416
      (gpointer) toolbar_activate_cb, NULL, NULL, GNOME_APP_PIXMAP_STOCK,
19237 martyn 417
      GNOME_STOCK_PIXMAP_JUMP_TO, 0, (GdkModifierType) 0, NULL },
418
    GNOMEUIINFO_SEPARATOR,
419
    { GNOME_APP_UI_ITEM, "Save As", "Save as a PostScript file", 
20443 martyn 420
      (gpointer) toolbar_save_as_cb, NULL, NULL, 
19237 martyn 421
      GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_PIXMAP_SAVE_AS, 0, 
422
      (GdkModifierType) 0, NULL },
423
    { GNOME_APP_UI_ITEM, "Print", "Print graphics", 
20443 martyn 424
      (gpointer) toolbar_print_cb, NULL, NULL, 
19237 martyn 425
      GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_PIXMAP_PRINT, 0, 
426
      (GdkModifierType) 0, NULL },
427
    GNOMEUIINFO_SEPARATOR,
428
    { GNOME_APP_UI_ITEM, "Close", "Close this graphics device",
20443 martyn 429
      (gpointer) toolbar_close_cb, NULL, NULL,
19237 martyn 430
      GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_PIXMAP_CLOSE, 0, 
431
      (GdkModifierType) 0, NULL },
432
    GNOMEUIINFO_END
13974 hornik 433
};
434
 
27236 murrell 435
static void GNOME_NewPage(R_GE_gcontext *gc,
436
			  NewDevDesc *dd)
16018 martyn 437
{
438
  GnomeCanvasItem *group;
439
  gnomeDesc *gd = (gnomeDesc*) dd->deviceSpecific;
440
 
441
  g_return_if_fail(gd->canvas);
442
 
443
  if (gd->group)
444
    {
445
      gtk_object_destroy(GTK_OBJECT(gd->group));
446
      gd->plotarea = NULL;
447
    }
448
 
449
  /* create group for plotting objects */
450
  group = gnome_canvas_item_new (gnome_canvas_root(GNOME_CANVAS(gd->canvas)),
451
				 gnome_canvas_group_get_type(),
452
				 "x", (double) 0.0,
453
				 "y", (double) 0.0,
454
				 NULL);
455
  gd->group = GNOME_CANVAS_GROUP(group);
456
 
457
  /* create plotting area */
458
  gd->plotarea = 
459
    gnome_canvas_item_new (gd->group,
460
			   gnome_canvas_rect_get_type(),
461
			   "x1", (double) 0.0,
462
			   "y1", (double) 0.0,
463
			   "x2", (double) gd->windowWidth,
464
			   "y2", (double) gd->windowHeight,
465
			   NULL);
17102 martyn 466
 
27236 murrell 467
  if (R_OPAQUE(gc->fill))
17102 martyn 468
    {
469
      gnome_canvas_item_set(gd->plotarea,
27236 murrell 470
			    "fill_color_rgba", Color_RGBA(gc->fill),
17102 martyn 471
			    NULL);
472
    }
473
  else
474
    {
475
      gnome_canvas_item_set(gd->plotarea,
476
			    "fill_color", "white",
477
			    NULL);
478
    }
16018 martyn 479
}
480
 
13974 hornik 481
/* create window etc */
482
static Rboolean
16945 pd 483
GNOME_Open(NewDevDesc *dd, gnomeDesc *gd, char *dsp, double w, double h)
13974 hornik 484
{
485
    gint iw, ih;
27236 murrell 486
    R_GE_gcontext gc;
13974 hornik 487
 
488
    /*gdk_rgb_set_install(TRUE); */
489
    /* gdk_rgb_set_verbose(TRUE);*/
490
    gdk_rgb_init();
491
    gtk_widget_push_visual(gdk_rgb_get_visual());
492
    gtk_widget_push_colormap(gdk_rgb_get_cmap());
493
 
494
    /* initialise pointers */
16018 martyn 495
    gd->canvas = NULL;
496
    gd->group = NULL;
497
    gd->plotarea = NULL;
13974 hornik 498
 
16018 martyn 499
    if(!SetBaseFont(gd))
500
      {
501
	Rprintf("can't find X11 font\n");
502
	return FALSE;
503
      }
13974 hornik 504
 
505
    /* create window etc */
16018 martyn 506
    gd->windowWidth = iw = w / pixelWidth();
507
    gd->windowHeight = ih = h / pixelHeight();
13974 hornik 508
 
16018 martyn 509
    gd->window = gnome_app_new("R.gnome.graphics", "R Graphics");
13974 hornik 510
 
16018 martyn 511
    gtk_window_set_policy(GTK_WINDOW(gd->window), TRUE, TRUE, FALSE);
512
    gtk_widget_realize(gd->window);
13974 hornik 513
 
514
    /* create toolbar */
16018 martyn 515
    gnome_app_create_toolbar_with_data(GNOME_APP(gd->window), graphics_toolbar,
516
				       (gpointer) dd);
13974 hornik 517
 
518
    /* create canvas */
16018 martyn 519
    gd->canvas = gnome_canvas_new_aa();
520
    gtk_widget_set_usize(gd->canvas, iw, ih);
521
    gnome_canvas_set_scroll_region(GNOME_CANVAS(gd->canvas), 0, 0, iw, ih);
13974 hornik 522
 
16018 martyn 523
    /* create new plot area on canvas */
17102 martyn 524
    /* FIXME: We should have a canvas color argument */
17165 murrell 525
    /* gamma is defaulted to 1 here */
27236 murrell 526
    /* 
527
     * WARNING: Many graphical parameters are unset - if GNOME_NewPage
528
     * starts to make use of more than fill and gamma then more 
529
     * parameters need setting here.
530
     */
531
    gc.fill = R_RGB(255,255,255);
532
    gc.gamma = 1;
533
    GNOME_NewPage(&gc, dd);
13974 hornik 534
 
535
    /* place and realize the canvas */
16018 martyn 536
    gnome_app_set_contents(GNOME_APP(gd->window), gd->canvas);
537
    gtk_widget_realize(gd->canvas);
13974 hornik 538
 
539
    /* same as XSynch() */
540
    gdk_flush();
541
 
542
    /* set the cursor */
16018 martyn 543
    gd->gcursor = gdk_cursor_new(GDK_CROSSHAIR);
544
    gdk_window_set_cursor(gd->canvas->window, gd->gcursor);
17102 martyn 545
 
13974 hornik 546
    /* connect to delete signal handler, etc */
17102 martyn 547
    gtk_signal_connect(GTK_OBJECT(gd->window), "configure_event",
13974 hornik 548
		       (GtkSignalFunc) configure_event, (gpointer) dd);
17102 martyn 549
    gtk_signal_connect(GTK_OBJECT(gd->canvas), "expose_event",
550
		       (GtkSignalFunc) expose_event, (gpointer) dd);
16018 martyn 551
    gtk_signal_connect(GTK_OBJECT(gd->window), "delete_event",
13974 hornik 552
		       (GtkSignalFunc) delete_event, (gpointer) dd);
553
 
554
    /* show everything */
16018 martyn 555
    gtk_widget_show_all(gd->window);
13974 hornik 556
 
557
    /* let other widgets use the default settings */
558
    gtk_widget_pop_visual();
559
    gtk_widget_pop_colormap();
560
 
561
    /* we made it! */
562
    return TRUE;
563
}
564
 
27236 murrell 565
static double GNOME_StrWidth(char *str, 
566
			     R_GE_gcontext *gc,
567
			     NewDevDesc *dd)
13974 hornik 568
{
16018 martyn 569
  int size;
570
  gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
571
 
27236 murrell 572
  size = gc->cex * gc->ps + 0.5;
573
  SetFont(dd, gc->fontface, size);
16018 martyn 574
 
575
  return (double) gdk_string_width(gd->font, str);
13974 hornik 576
}
577
 
27236 murrell 578
static void GNOME_MetricInfo(int c, 
579
			     R_GE_gcontext *gc,
580
			     double* ascent, double* descent,
581
			     double* width, NewDevDesc *dd)
13974 hornik 582
{
16018 martyn 583
  gint size;
584
  gint lbearing, rbearing, iascent, idescent, iwidth;
585
  gint maxwidth;
586
  gchar tmp[2];
587
  gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
588
 
27236 murrell 589
  size = gc->cex * gc->ps + 0.5;
590
  SetFont(dd, gc->fontface, size);
16018 martyn 591
 
592
  if(c == 0) {
593
    maxwidth = 0;
594
 
595
    for(c = 0; c <= 255; c++) {
596
      g_snprintf(tmp, 2, "%c", (gchar) c);
597
      iwidth = gdk_string_width(gd->font, tmp);
598
      if (iwidth > maxwidth)
599
	maxwidth = iwidth;
600
    }
601
 
602
    *ascent = (double) gd->font->ascent;
603
    *descent = (double) gd->font->descent;
604
    *width = (double) maxwidth;
605
  }
606
  else {
607
    g_snprintf(tmp, 2, "%c", (gchar) c);
608
    gdk_string_extents(gd->font, tmp,
609
		       &lbearing, &rbearing,
610
		       &iwidth, &iascent, &idescent);
611
 
612
    *ascent = (double) iascent;
613
    *descent = (double) idescent;
614
    *width = (double) (lbearing+rbearing);
615
  }
13974 hornik 616
}
617
 
17102 martyn 618
static void GNOME_Resize(NewDevDesc *dd)
619
{
620
  gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
621
 
622
  if (gd->resize) {
623
    dd->left = 0.0;
624
    dd->right =  gd->windowWidth;
625
    dd->bottom = gd->windowHeight;
626
    dd->top = 0.0;
627
    gd->resize = FALSE;
628
 
629
    gtk_widget_set_usize(gd->canvas, gd->windowWidth, gd->windowHeight);
630
    gnome_canvas_set_scroll_region(GNOME_CANVAS(gd->canvas), 0, 0,
631
				   gd->windowWidth, gd->windowHeight);
632
    gnome_canvas_item_set(gd->plotarea,
633
			  "x2", (double) gd->windowWidth,
634
			  "y2", (double) gd->windowHeight,
635
			  NULL);
636
  }
637
}
638
 
16945 pd 639
static void GNOME_Clip(double x0, double x1, double y0, double y1, NewDevDesc *dd)
13974 hornik 640
{
16018 martyn 641
  /* 
642
     Clipping is theoretically possible: there is an implementation of
643
     it in gnome-print-preview, which has a class called clipped-group. As
644
     the name suggests, this is a canvas group which defines a
645
     clipping path for the items it contains.  The notes say that this
646
     will be folded into the standard GnomeCanvasGroup, so I'm waiting
647
     for this to happen instead of duplicating it here. MTP 6/9/2001
648
  */
13974 hornik 649
}
650
 
16945 pd 651
static void GNOME_Size(double *left, double *right,
652
		     double *bottom, double *top,
653
		     NewDevDesc *dd)
13974 hornik 654
{
16018 martyn 655
    gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
13974 hornik 656
 
16945 pd 657
    *left = 0.0;
658
    *right =  gd->windowWidth;
659
    *bottom = gd->windowHeight;
660
    *top = 0.0;
13974 hornik 661
}
662
 
663
 
664
/* kill off the window etc */
16945 pd 665
static void GNOME_Close(NewDevDesc *dd)
13974 hornik 666
{
16018 martyn 667
    gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
13974 hornik 668
 
16018 martyn 669
    gtk_widget_destroy(gd->window);
13974 hornik 670
 
16018 martyn 671
    free(gd);
13974 hornik 672
}
673
 
674
#define title_text_inactive "R graphics device %d"
675
#define title_text_active "R graphics device %d - Active"
676
 
16945 pd 677
static void GNOME_Activate(NewDevDesc *dd)
13974 hornik 678
{
16018 martyn 679
    gnomeDesc *gd;
13974 hornik 680
    gint devnum, numdigits;
681
    gchar *title_text;
682
    gint text_len;
683
 
684
    text_len = strlen(title_text_active);
685
 
16018 martyn 686
    gd = (gnomeDesc *) dd->deviceSpecific;
13974 hornik 687
 
16018 martyn 688
    g_return_if_fail(gd != NULL);
13974 hornik 689
 
16945 pd 690
    devnum = devNumber((DevDesc*)dd);
13974 hornik 691
    devnum++;
692
    numdigits = floor(R_Log10(devnum)) + 1;
693
    title_text = g_malloc(text_len + numdigits + 1);
694
 
695
    g_return_if_fail(title_text != NULL);
696
 
18798 martyn 697
    g_snprintf(title_text, text_len + numdigits + 1, title_text_active, devnum);
13974 hornik 698
    *(title_text + text_len + numdigits) = '\0';
699
 
16018 martyn 700
    gtk_window_set_title(GTK_WINDOW(gd->window), title_text);
13974 hornik 701
 
702
    g_free(title_text);
703
}
704
 
16945 pd 705
static void GNOME_Deactivate(NewDevDesc *dd)
13974 hornik 706
{
16018 martyn 707
    gnomeDesc *gd;
13974 hornik 708
    gint devnum, numdigits;
709
    gchar *title_text;
710
    gint text_len;
711
 
712
    text_len = strlen(title_text_inactive);
713
 
16018 martyn 714
    gd = (gnomeDesc *) dd->deviceSpecific;
13974 hornik 715
 
16018 martyn 716
    g_return_if_fail(gd != NULL);
13974 hornik 717
 
16945 pd 718
    devnum = devNumber((DevDesc*)dd);
13974 hornik 719
    devnum++;
720
    numdigits = floor(R_Log10(devnum)) + 1;
721
    title_text = g_malloc(text_len + numdigits + 1);
722
 
723
    g_return_if_fail(title_text != NULL);
724
 
18798 martyn 725
    g_snprintf(title_text, text_len + numdigits + 1, title_text_inactive, devnum);
13974 hornik 726
    *(title_text + text_len + numdigits) = '\0';
727
 
16018 martyn 728
    gtk_window_set_title(GTK_WINDOW(gd->window), title_text);
13974 hornik 729
 
730
    g_free(title_text);
731
}
732
 
733
/* drawing stuff */
734
 
735
static void GNOME_Rect(double x0, double y0, double x1, double y1,
27236 murrell 736
		       R_GE_gcontext *gc,
737
		       NewDevDesc *dd)
13974 hornik 738
{
739
    double tmp;
16018 martyn 740
    gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
13974 hornik 741
    GnomeCanvasItem *item;
742
 
743
    if(x0 > x1) {
744
	tmp = x0;
745
	x0 = x1;
746
	x1 = tmp;
747
    }
748
    if(y0 > y1) {
749
	tmp = y0;
750
	y0 = y1;
751
	y1 = tmp;
752
    }
753
 
16018 martyn 754
    item = gnome_canvas_item_new(gd->group,
13974 hornik 755
				 gnome_canvas_rect_get_type(),
756
				 "x1", x0,
757
				 "y1", y0,
758
				 "x2", x1,
759
				 "y2", y1,
27236 murrell 760
				 "width_units", (double) gc->lwd,
13974 hornik 761
				 NULL);
762
 
27236 murrell 763
    if(gc->fill != NA_INTEGER) {
13974 hornik 764
	gnome_canvas_item_set(item,
27236 murrell 765
			      "fill_color_rgba", Color_RGBA(gc->fill),
13974 hornik 766
			      NULL);
767
    }
27236 murrell 768
    if(gc->col != NA_INTEGER) {
13974 hornik 769
	gnome_canvas_item_set(item,
27236 murrell 770
			      "outline_color_rgba", Color_RGBA(gc->col),
13974 hornik 771
			      NULL);
772
    }
773
}
774
 
16945 pd 775
static void GNOME_Circle(double x, double y, double r,
27236 murrell 776
			 R_GE_gcontext *gc,
777
			 NewDevDesc *dd)
13974 hornik 778
{
779
    double x1, y1, x2, y2;
16018 martyn 780
    gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
13974 hornik 781
    GnomeCanvasItem *item;
782
 
783
    x1 = x - r;  y1 = y - r;
784
    x2 = x + r;  y2 = y + r;
785
 
16018 martyn 786
    item = gnome_canvas_item_new(gd->group,
13974 hornik 787
				 gnome_canvas_ellipse_get_type(),
788
				 "x1", x1,
789
				 "y1", y1,
790
				 "x2", x2,
791
				 "y2", y2,
27236 murrell 792
				 "width_units", (double) gc->lwd,
13974 hornik 793
				 NULL);
794
 
27236 murrell 795
    if(gc->fill != NA_INTEGER) {
13974 hornik 796
	gnome_canvas_item_set(item,
27236 murrell 797
			      "fill_color_rgba", Color_RGBA(gc->fill),
13974 hornik 798
			      NULL);
799
    }
27236 murrell 800
    if(gc->col != NA_INTEGER) {
13974 hornik 801
	gnome_canvas_item_set(item,
27236 murrell 802
			      "outline_color_rgba", Color_RGBA(gc->col),
13974 hornik 803
			      NULL);
804
    }
805
}
806
 
807
static void GNOME_Line(double x1, double y1, double x2, double y2,
27236 murrell 808
		       R_GE_gcontext *gc,
809
		       NewDevDesc *dd)
13974 hornik 810
{
811
    GnomeCanvasPoints *points;
16018 martyn 812
    gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
13974 hornik 813
    GnomeCanvasItem *item;
814
 
815
    points = gnome_canvas_points_new(2);
816
    points->coords[0] = x1;
817
    points->coords[1] = y1;
818
    points->coords[2] = x2;
819
    points->coords[3] = y2;
820
 
16018 martyn 821
    item = gnome_canvas_item_new(gd->group,
13974 hornik 822
				 gnome_canvas_line_get_type(),
823
				 "points", points,
27236 murrell 824
				 "width_units", (double) gc->lwd,
13974 hornik 825
				 NULL);
826
 
27236 murrell 827
    if(gc->col != NA_INTEGER) {
828
	if(gc->lty != 0) {
16945 pd 829
	    /*SetLineType(GNOME_CANVAS_LINE(item)->gc, lty, lwd);*/
13974 hornik 830
	    gnome_canvas_item_set(item,
831
				  "line_style", GDK_LINE_ON_OFF_DASH,
832
				  NULL);
833
	}
834
	gnome_canvas_item_set(item,
27236 murrell 835
			      "fill_color_rgba", Color_RGBA(gc->col),
13974 hornik 836
			      NULL);
837
    }
838
 
839
    gnome_canvas_points_free(points);
840
}
841
 
16945 pd 842
static void GNOME_Polyline(int n, double *x, double *y, 
27236 murrell 843
			   R_GE_gcontext *gc,
844
			   NewDevDesc *dd)
13974 hornik 845
{
846
    int i;
847
    GnomeCanvasPoints *points;
16018 martyn 848
    gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
13974 hornik 849
    GnomeCanvasItem *item;
850
 
851
    points = gnome_canvas_points_new(n);
852
 
853
    for(i = 0; i < n; i++) {
17088 martyn 854
	points->coords[i * 2] = x[i];
855
	points->coords[i * 2 + 1] = y[i];
13974 hornik 856
    }
857
 
16018 martyn 858
    item = gnome_canvas_item_new(gd->group,
13974 hornik 859
				 gnome_canvas_line_get_type(),
860
				 "points", points,
27236 murrell 861
				 "width_units", (double) gc->lwd,
13974 hornik 862
				 NULL);
863
 
27236 murrell 864
    if(gc->col != NA_INTEGER) {
865
	if(gc->lty != 0) {
16018 martyn 866
	  /*
16945 pd 867
	    SetLineType(&stipple, lty, lwd);
13974 hornik 868
	    gnome_canvas_item_set(item,
869
				  "fill_stipple", &stipple,
870
				  NULL);
16018 martyn 871
	    */
872
	    gnome_canvas_item_set(item,
873
				  "line_style", GDK_LINE_ON_OFF_DASH,
874
				  NULL);
13974 hornik 875
	}
876
	gnome_canvas_item_set(item,
27236 murrell 877
			      "fill_color_rgba", Color_RGBA(gc->col),
13974 hornik 878
			      NULL);
879
    }
880
 
881
    gnome_canvas_points_free(points);
882
}
883
 
16945 pd 884
static void GNOME_Polygon(int n, double *x, double *y, 
27236 murrell 885
			  R_GE_gcontext *gc,
886
			  NewDevDesc *dd)
13974 hornik 887
{
888
    int i;
889
    GnomeCanvasPoints *points;
16018 martyn 890
    gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
13974 hornik 891
    GnomeCanvasItem *item;
892
 
893
    points = gnome_canvas_points_new(n);
894
 
895
    for(i = 0; i < n; i++) {
17088 martyn 896
	points->coords[i * 2] = floor(x[i]);
897
	points->coords[i * 2 + 1] = floor(y[i]);
13974 hornik 898
    }
899
 
16018 martyn 900
    item = gnome_canvas_item_new(gd->group,
13974 hornik 901
				 gnome_canvas_polygon_get_type(),
902
				 "points", points,
903
				 "width_units", (double) 2,
904
				 NULL);
905
 
27236 murrell 906
    if(gc->fill != NA_INTEGER) {
13974 hornik 907
	gnome_canvas_item_set(item,
27236 murrell 908
			      "fill_color_rgba", Color_RGBA(gc->fill),
13974 hornik 909
			      NULL);
910
    }
27236 murrell 911
    if(gc->col != NA_INTEGER) {
13974 hornik 912
	gnome_canvas_item_set(item,
27236 murrell 913
			      "outline_color_rgba", Color_RGBA(gc->col),
13974 hornik 914
			      NULL);
915
    }
916
 
917
    gnome_canvas_points_free(points);
918
}
919
 
16945 pd 920
static void GNOME_Text(double x, double y, char *str, 
27236 murrell 921
		       double rot, double hadj, 
922
		       R_GE_gcontext *gc,
923
		       NewDevDesc *dd)
13974 hornik 924
{
16018 martyn 925
  double affine[6] = {1, 0, 0, 1, 0, 0};
926
  double rrot = DEG2RAD * rot;
927
  GnomeCanvasItem *item;
928
  gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
929
  int size;
13974 hornik 930
 
27236 murrell 931
  size = gc->cex * gc->ps + 0.5;
932
  SetFont(dd, gc->fontface, size);
13974 hornik 933
 
16018 martyn 934
  if (rot != 0)
935
    {
936
      affine[0] = affine[3] = cos(rrot);
937
      affine[1] = -sin(rrot);
938
      affine[2] = -affine[1];
939
      affine[4] = x;
940
      affine[5] = y;
941
      x = y = 0.0;
942
    }
943
 
944
  item = gnome_canvas_item_new (gd->group,
945
				gnome_canvas_text_get_type(),
946
				"text", str,
947
				"anchor", GTK_ANCHOR_SOUTH_WEST,
948
				"x", x,
949
				"y", y,
950
				"font_gdk", gd->font,
27236 murrell 951
				"fill_color_rgba", Color_RGBA(gc->col),
16018 martyn 952
				NULL);
953
 
954
  if (rot != 0)
955
    gnome_canvas_item_affine_relative (item, affine);
13974 hornik 956
}
957
 
958
/* locator */
16018 martyn 959
 
17088 martyn 960
static gint locator_cb (GtkWidget * canvas, GdkEventButton * event,
961
			gpointer data)
16018 martyn 962
{
963
  GdkEventButton *info = (GdkEventButton*) data;
964
 
965
  *info = *event;
966
  gtk_main_quit();
17088 martyn 967
 
968
  return FALSE;
16018 martyn 969
}
970
 
16945 pd 971
static Rboolean GNOME_Locator(double *x, double *y, NewDevDesc *dd)
13974 hornik 972
{
16018 martyn 973
  gnomeDesc *gd = (gnomeDesc *) dd->deviceSpecific;
974
  GdkEventButton info;
975
  guint handler_id;
976
 
977
  /* Flush any pending events */
978
  while (gtk_events_pending())
979
    gtk_main_iteration();
980
 
981
  /* connect signal */
982
  handler_id = gtk_signal_connect (GTK_OBJECT (gd->canvas),
983
				   "button-press-event",
984
				   GTK_SIGNAL_FUNC (locator_cb), &info);
985
 
986
  /* run the handler */
987
  gtk_main();
988
 
989
  *x = (double) info.x;
990
  *y = (double) info.y;
991
 
992
  /* clean up */
993
  gtk_signal_disconnect(GTK_OBJECT(gd->canvas), handler_id);
994
 
995
  if (info.button == 1)
996
    {
997
      gdk_beep();
998
      return TRUE;
999
    }
1000
  else
13974 hornik 1001
    return FALSE;
1002
}
1003
 
1004
/* useless stuff */
1005
 
16945 pd 1006
static void GNOME_Mode(gint mode, NewDevDesc *dd)
13974 hornik 1007
{
1008
}
1009
 
16945 pd 1010
static void GNOME_Hold(NewDevDesc *dd)
13974 hornik 1011
{
1012
}
1013
 
1014
 
1015
/* Device driver entry point */
16945 pd 1016
Rboolean GnomeDeviceDriver(DevDesc *odd, char *display, 
13974 hornik 1017
			   double width, double height, double pointsize)
1018
{
1019
    int ps;
16018 martyn 1020
    gnomeDesc *gd;
16945 pd 1021
    NewDevDesc *dd;
13974 hornik 1022
 
16018 martyn 1023
    if(!(gd = (gnomeDesc *) malloc(sizeof(gnomeDesc))))
13974 hornik 1024
	return FALSE;
1025
 
20053 martyn 1026
    dd = (NewDevDesc*) odd;  
16018 martyn 1027
    dd->deviceSpecific = (void *) gd;
13974 hornik 1028
 
1029
    /* FIXME: font loading */
1030
    ps = pointsize;
1031
    if(ps < 6 || ps > 24) ps = 12;
1032
    ps = 2 * (ps / 2);
16018 martyn 1033
    gd->fontface = -1;
1034
    gd->fontsize = -1;
17165 murrell 1035
 
1036
    dd->startfont = 1; 
16945 pd 1037
    dd->startps = ps;
1038
    dd->startcol = 0;
1039
    dd->startfill = NA_INTEGER;
17165 murrell 1040
    dd->startlty = LTY_SOLID; 
1041
    dd->startgamma = 1;
1042
 
13974 hornik 1043
    /* device driver start */
16018 martyn 1044
    if(!GNOME_Open(dd, gd, display, width, height)) {
1045
	free(gd);
13974 hornik 1046
	return FALSE;
1047
    }
1048
 
16945 pd 1049
    dd->newDevStruct = 1;
1050
 
13974 hornik 1051
    /* setup data structure */
16945 pd 1052
    dd->open = GNOME_Open;
1053
    dd->close = GNOME_Close;
1054
    dd->activate = GNOME_Activate;
1055
    dd->deactivate = GNOME_Deactivate;
1056
    dd->size = GNOME_Size;
1057
    dd->newPage = GNOME_NewPage;
1058
    dd->clip = GNOME_Clip;
1059
    dd->strWidth = GNOME_StrWidth;
1060
    dd->text = GNOME_Text;
1061
    dd->rect = GNOME_Rect;
1062
    dd->circle = GNOME_Circle;
1063
    dd->line = GNOME_Line;
1064
    dd->polyline = GNOME_Polyline;
1065
    dd->polygon = GNOME_Polygon;
1066
    dd->locator = GNOME_Locator;
1067
    dd->mode = GNOME_Mode;
1068
    dd->hold = GNOME_Hold;
16952 murrell 1069
    dd->metricInfo = GNOME_MetricInfo;
13974 hornik 1070
 
16945 pd 1071
    dd->left = 0;
1072
    dd->right = gd->windowWidth;
1073
    dd->bottom = gd->windowHeight;
1074
    dd->top = 0;
13974 hornik 1075
 
1076
    /* FIXME: nominal character sizes */
16945 pd 1077
    dd->cra[0] = 10;
1078
    dd->cra[1] = 10;
13974 hornik 1079
 
1080
    /* FIXME: character addressing offsets */
16945 pd 1081
    dd->xCharOffset = 0.4900;
1082
    dd->yCharOffset = 0.3333;
1083
    dd->yLineBias = 0.1;
13974 hornik 1084
 
1085
    /* inches per raster unit */
16945 pd 1086
    dd->ipr[0] = pixelWidth();
1087
    dd->ipr[1] = pixelHeight();
13974 hornik 1088
 
1089
    /* device capabilities */
16945 pd 1090
    dd->canResizePlot= TRUE;
1091
    dd->canChangeFont= FALSE; /* FIXME: surely this is possible */
1092
    dd->canRotateText= TRUE;
1093
    dd->canResizeText= TRUE;
1094
    dd->canClip = FALSE;/* FIXME: really? */
1095
    dd->canHAdj = 0;/* not better? {0, 0.5, 1} */
17165 murrell 1096
    dd->canChangeGamma = FALSE;
13974 hornik 1097
 
1098
    /* x11 device description stuff */
16018 martyn 1099
    gd->cex = 1.0;
1100
    gd->srt = 0.0;
1101
    gd->resize = FALSE;
13974 hornik 1102
 
1103
    dd->displayListOn = TRUE;
1104
 
1105
    /* finish */
1106
    return TRUE;
1107
}
16018 martyn 1108
 
1109
 
1110
 
1111
 
1112
 
1113
 
1114
 
1115
 
1116
 
1117
 
1118
 
1119
 
1120
 
1121
 
1122
 
1123
 
1124
 
1125
 
1126