Rev 119 | Blame | Last modification | View Log | Download | RSS feed
/** R : A Computer Language for Statistical Data Analysis* Copyright (C) 1998-2001 Lyndon Drake* and the R Development Core Team** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#ifndef GTK2#ifdef HAVE_CONFIG_H#include <config.h>#endif#ifdef HAVE_RINT#define R_rint(x) rint(x)#else#define R_rint(x) ((int) x + 0.5)#endif#include <gtk/gtk.h>#include <R.h>#include <Rinternals.h>#include <Rgraphics.h>#include <Rdevices.h>#include <R_ext/GraphicsDevice.h>#include <R_ext/GraphicsEngine.h>#include "devGTK.h"#include "gdkrotated.h"/*#define DEBUG_GTK*/#define CURSOR GDK_CROSSHAIR /* Default cursor */#define MM_PER_INCH 25.4 /* mm -> inch conversion */#define IS_100DPI ((int) (1./pixelHeight() + 0.5) == 100)static unsigned int adobe_sizes = 0x0403165D;#define ADOBE_SIZE(I) ((I) > 7 && (I) < 35 && (adobe_sizes & (1<<((I)-8))))/* routines from here *//* Device driver actions */static void GTK_Activate(NewDevDesc *dd);static void GTK_Circle(double x, double y, double r,R_GE_gcontext *gc,NewDevDesc *dd);static void GTK_Clip(double x0, double x1, double y0, double y1,NewDevDesc *dd);static void GTK_Close(NewDevDesc *dd);static void GTK_Deactivate(NewDevDesc *dd);static void GTK_Hold(NewDevDesc *dd);static Rboolean GTK_Locator(double *x, double *y, NewDevDesc *dd);static void GTK_Line(double x1, double y1, double x2, double y2,R_GE_gcontext *gc,NewDevDesc *dd);static void GTK_MetricInfo(int c,R_GE_gcontext *gc,double* ascent, double* descent,double* width, NewDevDesc *dd);static void GTK_Mode(int mode, NewDevDesc *dd);static void GTK_NewPage(R_GE_gcontext *gc, NewDevDesc *dd);static void GTK_Polygon(int n, double *x, double *y,R_GE_gcontext *gc,NewDevDesc *dd);static void GTK_Polyline(int n, double *x, double *y,R_GE_gcontext *gc,NewDevDesc *dd);static void GTK_Rect(double x0, double y0, double x1, double y1,R_GE_gcontext *gc,NewDevDesc *dd);static void GTK_Size(double *left, double *right,double *bottom, double *top,NewDevDesc *dd);static double GTK_StrWidth(char *str,R_GE_gcontext *gc,NewDevDesc *dd);static void GTK_Text(double x, double y, char *str,double rot, double hadj,R_GE_gcontext *gc,NewDevDesc *dd);static Rboolean GTK_Open(NewDevDesc*, gtkDesc*, char*, double, double);static gint initialize(NewDevDesc *dd);/* Pixel Dimensions (Inches) */static double pixelWidth(void){double width, widthMM;width = gdk_screen_width();widthMM = gdk_screen_width_mm();return ((double)widthMM / (double)width) / MM_PER_INCH;}static double pixelHeight(void){double height, heightMM;height = gdk_screen_height();heightMM = gdk_screen_height_mm();return ((double)heightMM / (double)height) / MM_PER_INCH;}/* font stuff */static char *fontname_R6 = "-*-helvetica-%s-%s-*-*-%d-*-*-*-*-*-*-*";static char *symbolname = "-adobe-symbol-*-*-*-*-%d-*-*-*-*-*-*-*";static char *slant[] = {"r", "o"};static char *weight[] = {"medium", "bold"};static GHashTable *font_htab = NULL;struct _FontMetricCache {gint ascent[255];gint descent[255];gint width[255];gint font_ascent;gint font_descent;gint max_width;};#define SMALLEST 2static GdkFont* RGTKLoadFont (gint face, gint size){gchar *fontname;GdkFont *tmp_font;gint pixelsize;if (face < 1 || face > 5)face = 1;if (size < SMALLEST) size = SMALLEST;/* Here's a 1st class fudge: make sure that the Adobe design sizes8, 10, 11, 12, 14, 17, 18, 20, 24, 25, 34 can be obtained viaan integer "size" at 100 dpi, namely 6, 7, 8, 9, 10, 12, 13,14, 17, 18, 24 points. It's almost y = x * 100/72, but notquite. The constants were found using lm(). --pd */if (IS_100DPI) size = R_rint (size * 1.43 - 0.4);/* 'size' is the requested size, 'pixelsize' the size of theactually allocated font*/pixelsize = size;if(face == 5)fontname = g_strdup_printf(symbolname, pixelsize);elsefontname = g_strdup_printf(fontname_R6,weight[(face-1)%2],slant[((face-1)/2)%2],pixelsize);#ifdef DEBUG_GTKRprintf("loading:\n%s\n", fontname);#endiftmp_font = gdk_font_load(fontname);#ifdef DEBUG_GTKif (tmp_font) Rprintf("success\n"); else Rprintf("failure\n");#endifif (!tmp_font) {static int near[]={14,14,14,17,17,18,20,20,20,20,24,24,24,25,25,25,25};/* 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 *//* If ADOBE_SIZE(pixelsize) is true at this point thenthe user's system does not have the standard ADOBE font setso we just have to use a "fixed" font.If we can't find a "fixed" font then something is seriouslywrong */if (ADOBE_SIZE (pixelsize)) {tmp_font = gdk_font_load ("fixed");if (!tmp_font)error("Could not find any X11 fonts\nCheck that the Font Path is correct.");}if (pixelsize < 8)pixelsize = 8;else if (pixelsize == 9)pixelsize = 8;else if (pixelsize >= 13 && pixelsize < 30)pixelsize = near[size-13];elsepixelsize = 34;g_free(fontname);if(face == 5)fontname = g_strdup_printf(symbolname, pixelsize);elsefontname = g_strdup_printf(fontname_R6,weight[(face-1)%2],slant[((face-1)/2)%2],pixelsize);#ifdef DEBUG_GTKRprintf("loading:\n%s\n", fontname);#endiftmp_font = gdk_font_load (fontname);#ifdef DEBUG_GTKif (tmp_font) Rprintf("success\n"); else Rprintf("failure\n");#endif}if(tmp_font) {g_hash_table_insert(font_htab, (gpointer) g_strdup(fontname),(gpointer) tmp_font);if (fabs( (pixelsize - size)/(double)size ) > 0.2)warning("GTK used font size %d when %d was requested",pixelsize, size);}g_free(fontname);return tmp_font;}static void SetFont(NewDevDesc *dd, gint face, gint size){GdkFont *tmp_font;gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;if (face < 1 || face > 5) face = 1;if (!gtkd->usefixed &&(size != gtkd->fontsize || face != gtkd->fontface)) {tmp_font = RGTKLoadFont(face, size);if(tmp_font) {gtkd->font = tmp_font;gtkd->fontface = face;gtkd->fontsize = size;gdk_gc_set_font(gtkd->wgc, tmp_font);} elseerror("X11 font at size %d could not be loaded", size);}}static gint SetBaseFont(gtkDesc *gtkd){gtkd->fontface = 1;gtkd->fontsize = 12;gtkd->usefixed = 0;if(font_htab == NULL) {font_htab = g_hash_table_new (g_str_hash, g_str_equal);}gtkd->font = RGTKLoadFont (gtkd->fontface, gtkd->fontsize);if(gtkd->wgc == NULL)return(1);if(gtkd->font != NULL) {gdk_gc_set_font(gtkd->wgc, gtkd->font);return 1;}gtkd->usefixed = 1;gtkd->font = gdk_font_load ("fixed");if(gtkd->font != NULL) {gdk_gc_set_font(gtkd->wgc, gtkd->font);return 1;}return 0;}/* set the r, g, b, and pixel values of gcol to color */static void SetColor(GdkColor *gcol, int color){int red, green, blue;red = R_RED(color);green = R_GREEN(color);blue = R_BLUE(color);gcol->red = 0;gcol->green = 0;gcol->blue = 0;gcol->pixel = gdk_rgb_xpixel_from_rgb((red << 16)|(green << 8)|(blue));}/* set the line type */static void SetLineType(NewDevDesc *dd, int newlty, double nlwd){static gint8 dashlist[8];gint i, j, newlwd;gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;newlwd = nlwd;if(newlty != gtkd->lty || newlwd != gtkd->lwd) {gtkd->lty = newlty;gtkd->lwd = newlwd;if(newlty == 0) {if(newlwd <= 1)newlwd = 0;gdk_gc_set_line_attributes(gtkd->wgc, newlwd,GDK_LINE_SOLID,GDK_CAP_BUTT,GDK_JOIN_ROUND);}else {if(newlwd < 1)newlwd = 1;for(i = 0; (i < 8) && (newlty != 0); i++) {j = newlty & 15;if(j == 0) j = 1;j = j * newlwd;if(j > 255) j = 255;dashlist[i] = j;newlty = newlty >> 4;}/* set dashes */gdk_gc_set_dashes(gtkd->wgc, 0, dashlist, i);gdk_gc_set_line_attributes(gtkd->wgc, newlwd,GDK_LINE_ON_OFF_DASH,GDK_CAP_BUTT,GDK_JOIN_ROUND);}}}/* signal functions */static gint realize_event(GtkWidget *widget, gpointer data){NewDevDesc *dd;dd = (NewDevDesc *) data;g_return_val_if_fail(dd != NULL, FALSE);return(initialize(dd));}static gint initialize(NewDevDesc *dd){gtkDesc *gtkd;gtkd = (gtkDesc *) dd->deviceSpecific;g_return_val_if_fail(gtkd != NULL, FALSE);g_return_val_if_fail(gtkd->drawing != NULL, FALSE);g_return_val_if_fail(GTK_IS_DRAWING_AREA(gtkd->drawing), FALSE);/* create gc */gtkd->wgc = gdk_gc_new(gtkd->drawing->window);/* set the cursor */gtkd->gcursor = gdk_cursor_new(GDK_CROSSHAIR);gdk_window_set_cursor(gtkd->drawing->window, gtkd->gcursor);/* set window bg */gdk_window_set_background(gtkd->drawing->window, >kd->gcol_bg);if(gtkd->wgc)gdk_gc_set_foreground(gtkd->wgc, >kd->gcol_bg);/* create offscreen drawable */if(gtkd->windowWidth > 0 && gtkd->windowHeight > 0) {gtkd->pixmap = gdk_pixmap_new(gtkd->drawing->window,gtkd->windowWidth, gtkd->windowHeight,-1);gdk_draw_rectangle(gtkd->pixmap, gtkd->wgc, TRUE, 0, 0,gtkd->windowWidth, gtkd->windowHeight);}return FALSE;}static gint configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data){NewDevDesc *dd;gtkDesc *gtkd;dd = (NewDevDesc *) data;g_return_val_if_fail(dd != NULL, FALSE);gtkd = (gtkDesc *) dd->deviceSpecific;g_return_val_if_fail(gtkd != NULL, FALSE);g_return_val_if_fail(gtkd->drawing != NULL, FALSE);g_return_val_if_fail(GTK_IS_DRAWING_AREA(gtkd->drawing), FALSE);/* check for resize */if((GTK_WIDGET_REALIZED(gtkd->drawing)) && ((gtkd->windowWidth != event->width) || (gtkd->windowHeight != event->height))) {gtkd->windowWidth = event->width;gtkd->windowHeight = event->height;gtkd->resize = TRUE;}return FALSE;}static void GTK_resize(NewDevDesc *dd);static gint expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data){NewDevDesc *dd;gtkDesc *gtkd;dd = (NewDevDesc *) data;g_return_val_if_fail(dd != NULL, FALSE);gtkd = (gtkDesc *) dd->deviceSpecific;g_return_val_if_fail(gtkd != NULL, FALSE);g_return_val_if_fail(gtkd->drawing != NULL, FALSE);g_return_val_if_fail(GTK_IS_DRAWING_AREA(gtkd->drawing), FALSE);if(gtkd->wgc == NULL)realize_event(gtkd->drawing, dd);else if(gtkd->resize != 0) {GTK_resize(dd);}else if (gtkd->pixmap) {gdk_draw_pixmap(gtkd->drawing->window, gtkd->wgc, gtkd->pixmap,event->area.x, event->area.y, event->area.x, event->area.y,event->area.width, event->area.height);}else {GEplayDisplayList((GEDevDesc*) Rf_GetDevice(Rf_devNumber((DevDesc*)dd)));}return FALSE;}static gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data){NewDevDesc *dd;dd = (NewDevDesc *) data;g_return_val_if_fail(dd != NULL, FALSE);Rf_KillDevice((DevDesc*) Rf_GetDevice(Rf_devNumber((DevDesc*) dd)));return TRUE;}/*static void tb_activate_cb(GtkWidget *widget, gpointer data){NewDevDesc *dd;dd = (NewDevDesc *) data;g_return_if_fail(dd != NULL);selectDevice(Rf_devNumber((DevDesc*)dd));}static void tb_close_cb(GtkWidget *widget, gpointer data){NewDevDesc *dd;dd = (NewDevDesc *) data;g_return_if_fail(dd != NULL);Rf_KillDevice((DevDesc*) Rf_GetDevice(Rf_devNumber((DevDesc*) dd)));}*//* create window etc */static Rboolean GTK_Open(NewDevDesc *dd, gtkDesc *gtkd, char *dsp, double w, double h){gint iw, ih;/* initialise pointers */gtkd->drawing = NULL;gtkd->wgc = NULL;gtkd->gcursor = NULL;/* initialise colour */gdk_rgb_init();gtk_widget_push_visual(gdk_rgb_get_visual());gtk_widget_push_colormap(gdk_rgb_get_cmap());/* create window etc */gtkd->windowWidth = iw = w / pixelWidth();gtkd->windowHeight = ih = h / pixelHeight();gtkd->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);gtk_window_set_policy(GTK_WINDOW(gtkd->window), TRUE, TRUE, FALSE);gtk_widget_realize(gtkd->window);/* create drawingarea */gtkd->drawing = gtk_drawing_area_new();gtk_widget_set_events(gtkd->drawing,GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);/* connect to signal handlers, etc */gtk_signal_connect(GTK_OBJECT(gtkd->drawing), "realize",(GtkSignalFunc) realize_event, (gpointer) dd);/* drawingarea properties */gtk_widget_set_usize(gtkd->drawing, iw, ih);/* setup background color *//* gtkd->bg = dd->bg = R_RGB(255, 255, 255); */SetColor(>kd->gcol_bg, R_RGB(255, 255, 255)); /* FIXME canvas color *//* place and realize the drawing area */gtk_container_add(GTK_CONTAINER(gtkd->window), gtkd->drawing);gtk_widget_realize(gtkd->drawing);/* connect to signal handlers, etc */gtk_signal_connect(GTK_OBJECT(gtkd->drawing), "configure_event",(GtkSignalFunc) configure_event, (gpointer) dd);gtk_signal_connect(GTK_OBJECT(gtkd->drawing), "expose_event",(GtkSignalFunc) expose_event, (gpointer) dd);gtk_signal_connect(GTK_OBJECT(gtkd->window), "delete_event",(GtkSignalFunc) delete_event, (gpointer) dd);/* show everything */gtk_widget_show_all(gtkd->window);/* initialise line params */gtkd->lty = -1;gtkd->lwd = -1;/* create offscreen drawable */gtkd->pixmap = gdk_pixmap_new(gtkd->drawing->window,gtkd->windowWidth, gtkd->windowHeight,-1);gdk_gc_set_foreground(gtkd->wgc, >kd->gcol_bg);gdk_draw_rectangle(gtkd->pixmap, gtkd->wgc, TRUE, 0, 0,gtkd->windowWidth, gtkd->windowHeight);/* let other widgets use the default colour settings */gtk_widget_pop_visual();gtk_widget_pop_colormap();/* Set base font */if(!SetBaseFont(gtkd)) {Rprintf("can't find X11 font\n");return FALSE;}/* we made it! */return TRUE;}static double GTK_StrWidth(char *str,R_GE_gcontext *gc,NewDevDesc *dd){int size;gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;size = gc->cex * gc->ps + 0.5;SetFont(dd, gc->fontface, size);return (double) gdk_string_width(gtkd->font, str);}static void GTK_MetricInfo(int c,R_GE_gcontext *gc,double* ascent, double* descent,double* width, NewDevDesc *dd){gint size;gint lbearing, rbearing, iascent, idescent, iwidth;gint maxwidth;gchar tmp[2];gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;size = gc->cex * gc->ps + 0.5;SetFont(dd, gc->fontface, size);if(c == 0) {maxwidth = 0;for(c = 0; c <= 255; c++) {g_snprintf(tmp, 2, "%c", (gchar) c);iwidth = gdk_string_width(gtkd->font, tmp);if (iwidth > maxwidth)maxwidth = iwidth;}*ascent = (double) gtkd->font->ascent;*descent = (double) gtkd->font->descent;*width = (double) maxwidth;}else {g_snprintf(tmp, 2, "%c", (gchar) c);gdk_string_extents(gtkd->font, tmp,&lbearing, &rbearing,&iwidth, &iascent, &idescent);*ascent = (double) iascent;*descent = (double) idescent;*width = (double) iwidth;/* This formula does NOT work for spaces*width = (double) (lbearing+rbearing);*/}}/* set clipping */static void GTK_Clip(double x0, double x1, double y0, double y1, NewDevDesc *dd){gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;return;gtkd->clip.x = dd->clipLeft = (int) MIN(x0, x1);gtkd->clip.width = abs( (int) x0 - (int) x1) + 1;dd->clipRight = dd->clipLeft + gtkd->clip.width;gtkd->clip.y = dd->clipBottom = (int) MIN(y0, y1);gtkd->clip.height = abs( (int) y0 - (int) y1) + 1;dd->clipTop = dd->clipBottom + gtkd->clip.height;/* Setting the clipping rectangle works when drawing to a windowbut not to the backing pixmap. This is a GTK+ bug that isunlikely to be fixed in this version (9 Jul 2002) - MTP*/gdk_gc_set_clip_rectangle(gtkd->wgc, >kd->clip);}static void GTK_Size(double *left, double *right,double *bottom, double *top,NewDevDesc *dd){gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;*left = 0.0;*right = gtkd->windowWidth;*bottom = gtkd->windowHeight;*top = 0.0;}static void GTK_resize(NewDevDesc *dd){gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;if (gtkd->resize != 0) {dd->left = 0.0;dd->right = gtkd->windowWidth;dd->bottom = gtkd->windowHeight;dd->top = 0.0;gtkd->resize = 0;if(gtkd->pixmap)gdk_pixmap_unref(gtkd->pixmap);if(gtkd->windowWidth > 0 && gtkd->windowHeight > 0) {gtkd->pixmap = gdk_pixmap_new(gtkd->drawing->window,gtkd->windowWidth, gtkd->windowHeight,-1);if(gtkd->wgc) {gdk_gc_set_foreground(gtkd->wgc, >kd->gcol_bg);gdk_draw_rectangle(gtkd->pixmap, gtkd->wgc, TRUE, 0, 0,gtkd->windowWidth, gtkd->windowHeight);}}GEplayDisplayList((GEDevDesc*) Rf_GetDevice(Rf_devNumber((DevDesc*)dd)));}}/* clear the drawing area */static void GTK_NewPage(R_GE_gcontext *gc,NewDevDesc *dd){gtkDesc *gtkd;g_return_if_fail(dd != NULL);gtkd = (gtkDesc *) dd->deviceSpecific;g_return_if_fail(gtkd != NULL);g_return_if_fail(gtkd->drawing != NULL);g_return_if_fail(GTK_IS_DRAWING_AREA(gtkd->drawing));if(gtkd->drawing->window == NULL)return;gtkd->fill = R_OPAQUE(gc->fill) ? gc->fill : R_RGB(255,255,255);SetColor(>kd->gcol_bg, gtkd->fill);gdk_window_set_background(gtkd->drawing->window, >kd->gcol_bg);gdk_window_clear(gtkd->drawing->window);if(gtkd->wgc) {gdk_gc_set_foreground(gtkd->wgc, >kd->gcol_bg);gdk_draw_rectangle(gtkd->pixmap, gtkd->wgc, TRUE, 0, 0,gtkd->windowWidth, gtkd->windowHeight);}}/* kill off the window etc */static void GTK_Close(NewDevDesc *dd){gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;if(gtkd->window)gtk_widget_destroy(gtkd->window);if(gtkd->pixmap)gdk_pixmap_unref(gtkd->pixmap);gdk_flush();free(gtkd);}#define title_text_inactive "R graphics device %d"#define title_text_active "R graphics device %d - Active"static void GTK_Activate(NewDevDesc *dd){gtkDesc *gtkd;gint devnum;gchar *title_text;gtkd = (gtkDesc *) dd->deviceSpecific;g_return_if_fail(gtkd != NULL);if(!gtkd->window)return;devnum = Rf_devNumber((DevDesc*)dd) + 1;title_text = g_strdup_printf(title_text_active, devnum);gtk_window_set_title(GTK_WINDOW(gtkd->window), title_text);g_free(title_text);}static void GTK_Deactivate(NewDevDesc *dd){gtkDesc *gtkd;gint devnum;gchar *title_text;gtkd = (gtkDesc *) dd->deviceSpecific;g_return_if_fail(gtkd != NULL);if(!gtkd->window)return;devnum = Rf_devNumber((DevDesc*)dd) + 1;title_text = g_strdup_printf(title_text_inactive, devnum);gtk_window_set_title(GTK_WINDOW(gtkd->window), title_text);g_free(title_text);}/* drawing stuff */static void GTK_Rect(double x0, double y0, double x1, double y1,R_GE_gcontext *gc,NewDevDesc *dd){double tmp;gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;GdkColor gcol_fill, gcol_outline;if(!gtkd->drawing->window)return;if(x0 > x1) {tmp = x0;x0 = x1;x1 = tmp;}if(y0 > y1) {tmp = y0;y0 = y1;y1 = tmp;}if (R_OPAQUE(gc->fill)) {SetColor(&gcol_fill, gc->fill);gdk_gc_set_foreground(gtkd->wgc, &gcol_fill);SetLineType(dd, gc->lty, gc->lwd);gdk_draw_rectangle(gtkd->drawing->window,gtkd->wgc, TRUE,(gint) x0, (gint) y0,(gint) x1 - (gint) x0,(gint) y1 - (gint) y0);gdk_draw_rectangle(gtkd->pixmap,gtkd->wgc, TRUE,(gint) x0, (gint) y0,(gint) x1 - (gint) x0,(gint) y1 - (gint) y0);}if (R_OPAQUE(gc->col)) {SetColor(&gcol_outline, gc->col);gdk_gc_set_foreground(gtkd->wgc, &gcol_outline);SetLineType(dd, gc->lty, gc->lwd);gdk_draw_rectangle(gtkd->drawing->window,gtkd->wgc, FALSE,(gint) x0, (gint) y0,(gint) x1 - (gint) x0,(gint) y1 - (gint) y0);gdk_draw_rectangle(gtkd->pixmap,gtkd->wgc, FALSE,(gint) x0, (gint) y0,(gint) x1 - (gint) x0,(gint) y1 - (gint) y0);}}static void GTK_Circle(double x, double y, double r,R_GE_gcontext *gc,NewDevDesc *dd){GdkColor gcol_fill, gcol_outline;gint ix, iy, ir;gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;if(!gtkd->drawing->window)return;ix = x - r;iy = y - r;ir = 2 * floor(r + 0.5);if (R_OPAQUE(gc->fill)) {SetColor(&gcol_fill, gc->fill);gdk_gc_set_foreground(gtkd->wgc, &gcol_fill);gdk_draw_arc(gtkd->drawing->window,gtkd->wgc, TRUE,ix, iy, ir, ir,0, 23040);gdk_draw_arc(gtkd->pixmap,gtkd->wgc, TRUE,ix, iy, ir, ir,0, 23040);}if (R_OPAQUE(gc->col)) {SetColor(&gcol_outline, gc->col);gdk_gc_set_foreground(gtkd->wgc, &gcol_outline);SetLineType(dd, gc->lty, gc->lwd);gdk_draw_arc(gtkd->drawing->window,gtkd->wgc, FALSE,ix, iy, ir, ir,0, 23040);gdk_draw_arc(gtkd->pixmap,gtkd->wgc, FALSE,ix, iy, ir, ir,0, 23040);}}static void GTK_Line(double x1, double y1, double x2, double y2,R_GE_gcontext *gc,NewDevDesc *dd){gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;GdkColor gcol_fill;gint ix1, iy1, ix2, iy2;if(!gtkd->drawing->window)return;ix1 = (gint) x1; iy1 = (gint) y1;ix2 = (gint) x2; iy2 = (gint) y2;if (R_OPAQUE(gc->col)) {SetColor(&gcol_fill, gc->col);gdk_gc_set_foreground(gtkd->wgc, &gcol_fill);SetLineType(dd, gc->lty, gc->lwd);gdk_draw_line(gtkd->drawing->window,gtkd->wgc, ix1, iy1, ix2, iy2);gdk_draw_line(gtkd->pixmap,gtkd->wgc, ix1, iy1, ix2, iy2);}}static void GTK_Polyline(int n, double *x, double *y,R_GE_gcontext *gc,NewDevDesc *dd){gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;GdkColor gcol_fill;GdkPoint *points;int i;if(!gtkd->drawing->window)return;points = g_new0(GdkPoint, n);for(i = 0; i < n; i++) {points[i].x = (gint16) x[i];points[i].y = (gint16) y[i];}if (R_OPAQUE(gc->col)) {SetColor(&gcol_fill, gc->col);gdk_gc_set_foreground(gtkd->wgc, &gcol_fill);SetLineType(dd, gc->lty, gc->lwd);gdk_draw_lines(gtkd->drawing->window,gtkd->wgc, points, n);gdk_draw_lines(gtkd->pixmap,gtkd->wgc, points, n);}g_free(points);}static void GTK_Polygon(int n, double *x, double *y,R_GE_gcontext *gc,NewDevDesc *dd){gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;GdkColor gcol_fill, gcol_outline;GdkPoint *points;int i;if(!gtkd->drawing->window)return;points = g_new0(GdkPoint, n + 1);for(i = 0; i < n; i++) {points[i].x = (gint16) x[i];points[i].y = (gint16) y[i];}if (R_OPAQUE(gc->fill)) {SetColor(&gcol_fill, gc->fill);gdk_gc_set_foreground(gtkd->wgc, &gcol_fill);gdk_draw_polygon(gtkd->drawing->window,gtkd->wgc, TRUE, points, n);gdk_draw_polygon(gtkd->pixmap,gtkd->wgc, TRUE, points, n);}if (R_OPAQUE(gc->col)) {SetColor(&gcol_outline, gc->col);gdk_gc_set_foreground(gtkd->wgc, &gcol_outline);SetLineType(dd, gc->lty, gc->lwd);gdk_draw_polygon(gtkd->drawing->window,gtkd->wgc, FALSE, points, n);gdk_draw_polygon(gtkd->pixmap,gtkd->wgc, FALSE, points, n);}g_free(points);}static void GTK_Text(double x, double y, char *str,double rot, double hadj,R_GE_gcontext *gc,NewDevDesc *dd){gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;GdkColor gcol_fill;gint size;double rrot = DEG2RAD * rot;if(!gtkd->drawing->window)return;size = gc->cex * gc->ps + 0.5;SetFont(dd, gc->fontface, size);gdk_gc_set_font(gtkd->wgc, gtkd->font);if (R_OPAQUE(gc->col)) {SetColor(&gcol_fill, gc->col);gdk_gc_set_foreground(gtkd->wgc, &gcol_fill);gdk_draw_text_rot(gtkd->drawing->window,gtkd->font, gtkd->wgc,(int) x, (int) y,gtkd->windowWidth, gtkd->windowHeight,str, strlen(str), rrot);gdk_draw_text_rot(gtkd->pixmap,gtkd->font, gtkd->wgc,(int) x, (int) y,gtkd->windowWidth, gtkd->windowHeight,str, strlen(str), rrot);}}typedef struct _GTK_locator_info GTK_locator_info;struct _GTK_locator_info {guint x;guint y;gboolean button1;};static void locator_button_press(GtkWidget *widget,GdkEventButton *event,gpointer user_data){GTK_locator_info *info;info = (GTK_locator_info *) user_data;info->x = event->x;info->y = event->y;if(event->button == 1)info->button1 = TRUE;elseinfo->button1 = FALSE;gtk_main_quit();}static Rboolean GTK_Locator(double *x, double *y, NewDevDesc *dd){gtkDesc *gtkd = (gtkDesc *) dd->deviceSpecific;GTK_locator_info *info;guint handler_id;gboolean button1;info = g_new(GTK_locator_info, 1);/* Flush any pending events */while(gtk_events_pending())gtk_main_iteration();/* connect signal */handler_id = gtk_signal_connect(GTK_OBJECT(gtkd->drawing), "button-press-event",(GtkSignalFunc) locator_button_press, (gpointer) info);/* run the handler */gtk_main();*x = (double) info->x;*y = (double) info->y;button1 = info->button1;/* clean up */gtk_signal_disconnect(GTK_OBJECT(gtkd->drawing), handler_id);g_free(info);if(button1)return TRUE;return FALSE;}static void GTK_Mode(gint mode, NewDevDesc *dd){#ifdef XSYNCif(mode == 0)gdk_flush();#elsegdk_flush();#endif}static void GTK_Hold(NewDevDesc *dd){}/* Device driver entry point */RbooleanGTKDeviceDriver(DevDesc *odd, char *display, double width,double height, double pointsize){NewDevDesc *dd;int ps;gchar tmp[2];gint c, rbearing, lbearing;double max_rbearing, min_lbearing;gtkDesc *gtkd;dd = (NewDevDesc*) odd;if(!(gtkd = (gtkDesc *) malloc(sizeof(gtkDesc))))return FALSE;dd->deviceSpecific = (void *) gtkd;/* font loading */ps = pointsize;if(ISNAN(ps) || ps < 6 || ps > 24) ps = 12;ps = 2 * (ps / 2);gtkd->fontface = -1;gtkd->fontsize = -1;dd->startfont = 1;dd->startps = ps;dd->startcol = R_RGB(0, 0, 0);dd->startfill = NA_INTEGER;dd->startlty = LTY_SOLID;dd->startgamma = 1;/* device driver start */if(!GTK_Open(dd, gtkd, display, width, height)) {free(gtkd);return FALSE;}dd->newDevStruct = 1;/* setup data structure */dd->open = GTK_Open;dd->close = GTK_Close;dd->activate = GTK_Activate;dd->deactivate = GTK_Deactivate;dd->size = GTK_Size;dd->newPage = GTK_NewPage;dd->clip = GTK_Clip;dd->strWidth = GTK_StrWidth;dd->text = GTK_Text;dd->rect = GTK_Rect;dd->circle = GTK_Circle;dd->line = GTK_Line;dd->polyline = GTK_Polyline;dd->polygon = GTK_Polygon;dd->locator = GTK_Locator;dd->mode = GTK_Mode;dd->hold = GTK_Hold;dd->metricInfo = GTK_MetricInfo;dd->left = 0;dd->right = gtkd->windowWidth;dd->bottom = gtkd->windowHeight;dd->top = 0;/* nominal character sizes */max_rbearing = 0;min_lbearing = 10000; /* just a random big number */for(c = 0; c <= 255; c++) {g_snprintf(tmp, 2, "%c", (gchar) c);gdk_string_extents(gtkd->font, tmp,&lbearing, &rbearing,NULL, NULL, NULL);if(lbearing < min_lbearing || c == 0)min_lbearing = lbearing;if(rbearing > max_rbearing)max_rbearing = rbearing;}dd->cra[0] = max_rbearing - min_lbearing;dd->cra[1] = (double) gtkd->font->ascent + (double) gtkd->font->descent;/* character addressing offsets */dd->xCharOffset = 0.4900;dd->yCharOffset = 0.3333;dd->yLineBias = 0.1;/* inches per raster unit */dd->ipr[0] = pixelWidth();dd->ipr[1] = pixelHeight();/* device capabilities */dd->canResizePlot= TRUE;dd->canChangeFont= FALSE;dd->canRotateText= TRUE;dd->canResizeText= TRUE;dd->canClip = FALSE; /* See comment in GTK_Clip */dd->canHAdj = 0;/* not better? {0, 0.5, 1} */dd->canChangeGamma = FALSE;/* gtk device description stuff */gtkd->cex = 1.0;gtkd->srt = 0.0;gtkd->resize = FALSE;dd->displayListOn = TRUE;/* finish */return TRUE;}RbooleanGTKDeviceFromWidget(DevDesc *odd, char *widget, double width, double height, double pointsize){NewDevDesc *dd = (NewDevDesc *) odd;double ps = pointsize;gint iw, ih, w, h;GtkWidget *drawing = (GtkWidget *) widget;gtkDesc *gtkd;gchar tmp[2];gint c, rbearing, lbearing;double max_rbearing, min_lbearing;GTK_DRAWING_AREA(drawing);if(!(gtkd = (gtkDesc *) malloc(sizeof(gtkDesc))))return FALSE;w = width; h = height;gtkd->window = NULL;gtkd->pixmap = NULL;gtkd->drawing = NULL;gtkd->wgc = NULL;gtkd->gcursor = NULL;gtkd->resize = 1;/* font loading */ps = pointsize;if(ISNAN(ps) < 6 || ps > 24) ps = 12;ps = 2 * (ps / 2);gtkd->fontface = -1;gtkd->fontsize = -1;dd->startfont = 1;dd->startps = ps;dd->startcol = R_RGB(0, 0, 0);dd->startfill = NA_INTEGER;dd->startlty = LTY_SOLID;dd->startgamma = 1;/* device driver start */{#if 0GtkArg args[2];args[0].name = "GtkWidget::width";args[1].name = "GtkWidget::height";gtk_object_getv(GTK_OBJECT(drawing), 2, args);w = GTK_VALUE_INT(args[0]);if(w < 0)w = 0;h = GTK_VALUE_INT(args[1]);if(h < 0)h = 0;#endifgtkd->drawing = drawing;if(GTK_WIDGET_REALIZED(gtkd->drawing))gtk_widget_add_events(gtkd->drawing,GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);elsegtk_widget_set_events(gtkd->drawing,GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);gdk_rgb_init();gtk_widget_push_visual(gdk_rgb_get_visual());gtk_widget_push_colormap(gdk_rgb_get_cmap());gtkd->windowWidth = iw = w / pixelWidth();gtkd->windowHeight = ih = h / pixelHeight();/* connect to signal handlers, etc */gtk_signal_connect(GTK_OBJECT(gtkd->drawing), "realize",(GtkSignalFunc) realize_event, (gpointer) dd);SetColor(>kd->gcol_bg, R_RGB(255, 255, 255)); /*FIXME canvas color*//* connect to signal handlers, etc */gtk_signal_connect(GTK_OBJECT(gtkd->drawing), "configure_event",(GtkSignalFunc) configure_event, (gpointer) dd);gtk_signal_connect(GTK_OBJECT(gtkd->drawing), "expose_event",(GtkSignalFunc) expose_event, (gpointer) dd);dd->deviceSpecific = (void *) gtkd;/* initialise line params */gtkd->lty = -1;gtkd->lwd = -1;/* let other widgets use the default colour settings */gtk_widget_pop_visual();gtk_widget_pop_colormap();/* Set base font */if(!SetBaseFont(gtkd)) {Rprintf("can't find X11 font\n");return FALSE;}}dd->newDevStruct = 1;/* setup data structure */dd->open = GTK_Open;dd->close = GTK_Close;dd->activate = GTK_Activate;dd->deactivate = GTK_Deactivate;dd->size = GTK_Size;dd->newPage = GTK_NewPage;dd->clip = GTK_Clip;dd->strWidth = GTK_StrWidth;dd->text = GTK_Text;dd->rect = GTK_Rect;dd->circle = GTK_Circle;dd->line = GTK_Line;dd->polyline = GTK_Polyline;dd->polygon = GTK_Polygon;dd->locator = GTK_Locator;dd->mode = GTK_Mode;dd->hold = GTK_Hold;dd->metricInfo = GTK_MetricInfo;dd->left = 0;dd->right = gtkd->windowWidth;dd->bottom = gtkd->windowHeight;dd->top = 0;/* nominal character sizes */max_rbearing = 0;min_lbearing = 10000; /* just a random big number */for(c = 0; c <= 255; c++) {g_snprintf(tmp, 2, "%c", (gchar) c);gdk_string_extents(gtkd->font, tmp,&lbearing, &rbearing,NULL, NULL, NULL);if(lbearing < min_lbearing || c == 0)min_lbearing = lbearing;if(rbearing > max_rbearing)max_rbearing = rbearing;}dd->cra[0] = max_rbearing - min_lbearing;dd->cra[1] = (double) gtkd->font->ascent + (double) gtkd->font->descent;/* character addressing offsets */dd->xCharOffset = 0.4900;dd->yCharOffset = 0.3333;dd->yLineBias = 0.1;/* inches per raster unit */dd->ipr[0] = pixelWidth();dd->ipr[1] = pixelHeight();/* device capabilities */dd->canResizePlot= TRUE;dd->canChangeFont= FALSE;dd->canRotateText= TRUE;dd->canResizeText= TRUE;dd->canClip = FALSE; /* See comment in GTK_Clip */dd->canHAdj = 0;/* not better? {0, 0.5, 1} */dd->canChangeGamma = FALSE;/* gtk device description stuff */gtkd->cex = 1.0;gtkd->srt = 0.0;gtkd->resize = TRUE;dd->displayListOn = TRUE;/* finish */return TRUE;}#endif /* ifndef GTK2 */