1
/* gcw-driver - PLplot Gnome Canvas Widget device driver.
3
Copyright (C) 2004, 2005 Thomas J. Duck
4
Copyright (C) 2004 Rafael Laboissiere
10
This library is free software; you can redistribute it and/or
11
modify it under the terms of the GNU Lesser General Public
12
License as published by the Free Software Foundation; either
13
version 2.1 of the License, or (at your option) any later version.
15
This library is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
Lesser General Public License for more details.
20
You should have received a copy of the GNU Lesser General Public
21
License along with this library; if not, write to the Free Software
22
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
28
This is the Gnome Canvas Widget driver, written by Thomas J. Duck
29
following the heritage of the PLplot Gnome driver by Rafael Laboissiere.
30
Like all PLplot drivers, this operates in standalone mode by default.
31
However, this driver can also be used to write to a user-supplied
34
Please see the PLplot documentation for more information.
39
Truetype text is supplied using the PLPLOT_CANVAS_HACKTEXT item,
40
which was cloned from gnome-print. This text item was chosen because
41
it rotates and scales under a zoom correctly and easily.
43
It would be better to use GNOME_CANVAS_TEXT, but currently
44
(4 March 2005) it doesn't rotate or scale under a zoom on the
45
GnomeCanvas. GNOME_CANVAS_TEXT uses Pango, and rotations were only
46
recently implemented in the Pango API (i.e., Fall 2004). If the
47
Pango API is used directly, the bounding box doesn't rotate with the
48
text on GnomeCanvas, which results in clipping. It is likely that
49
GnomeCanvas is not querying the bounding box from Pango correctly,
50
and is not directing Pango to scale. So, GnomeCanvas needs to be
51
updated to deal with Pango properly.
53
Another problem is that drawing polylines on the Gnome Canvas sometimes
54
results in an 'attempt to put segment in horiz list twice' error.
55
The workaround here is to plot single line segments only, but this
56
results in a performance hit. This problem will need to be corrected
62
PLplot test suite problems:
64
1) Example x10c does not clip the text (there is no text clipping).
66
2) Example x17c, the strip chart demo, doesn't do a strip chart
67
(try the xwin driver to see how it should work). Strip charts
68
are fundamentally incompatible with the tabbed window design of
69
the GCW driver. Use the PlplotCanvas to create animations
76
#include "plplotcanvas-hacktext.h"
80
#include "plfreetype.h"
81
#include "plfci-truetype.h"
83
/* Font lookup table that is constructed in plD_FreeType_init*/
84
extern FCI_to_FontName_Table FontLookup[N_TrueTypeLookup];
86
extern void plD_FreeType_init(PLStream *pls);
87
extern void plD_FreeType_Destroy(PLStream *pls);
89
#endif /* HAVE_FREETYPE */
93
char* plD_DEVICE_INFO_gcw = "gcw:Gnome Canvas Widget:1:gcw:10:gcw";
95
/* Global driver options */
98
static PLINT text = 1;
100
static PLINT text = 0;
103
static PLINT hrshsym = 0;
104
static PLINT replot = 1;
106
static DrvOpt gcw_options[] =
108
{"text", DRV_INT, &text, "Use truetype fonts (text=0|1)"},
109
{"hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)"},
110
{"replot", DRV_INT, &replot, "Allow replotting to other devices (replot=0|1)"},
111
{NULL, DRV_INT, NULL, NULL}
115
/*********************
116
* Utility functions *
117
*********************/
119
guint32 plcolor_to_rgba(PLColor color, guchar alpha)
122
((int)(color.r) << 24)
123
+ ((int)(color.g) << 16)
124
+ ((int)(color.b) << 8)
129
/*--------------------------------------------------------------------------*\
130
* plD_dispatch_init_gcw()
132
* Initializes the dispatch table.
133
\*--------------------------------------------------------------------------*/
135
void plD_open_gcw(PLStream *pls);
136
void plD_init_gcw(PLStream *);
137
void plD_line_gcw(PLStream *, short, short, short, short);
138
void plD_polyline_gcw(PLStream *, short *, short *, PLINT);
139
void plD_eop_gcw(PLStream *);
140
void plD_bop_gcw(PLStream *);
141
void plD_tidy_gcw(PLStream *);
142
void plD_state_gcw(PLStream *, PLINT);
143
void plD_esc_gcw(PLStream *, PLINT, void *);
145
void plD_dispatch_init_gcw( PLDispatchTable *pdt )
149
gcw_debug("<plD_dispatch_init_gcw>\n");
152
#ifndef ENABLE_DYNDRIVERS
153
pdt->pl_MenuStr = "Gnome Canvas Widget";
154
pdt->pl_DevName = "gcw";
156
pdt->pl_type = plDevType_Interactive;
158
pdt->pl_init = (plD_init_fp) plD_init_gcw;
159
pdt->pl_line = (plD_line_fp) plD_line_gcw;
160
pdt->pl_polyline = (plD_polyline_fp) plD_polyline_gcw;
161
pdt->pl_eop = (plD_eop_fp) plD_eop_gcw;
162
pdt->pl_bop = (plD_bop_fp) plD_bop_gcw;
163
pdt->pl_tidy = (plD_tidy_fp) plD_tidy_gcw;
164
pdt->pl_state = (plD_state_fp) plD_state_gcw;
165
pdt->pl_esc = (plD_esc_fp) plD_esc_gcw;
168
gcw_debug("</plD_dispatch_init_gcw>\n");
173
/*--------------------------------------------------------------------------*\
176
* Initializes the device.
178
* This routine is invoked by a call to plinit.
180
\*--------------------------------------------------------------------------*/
182
void plD_init_gcw(PLStream *pls)
186
PLINT width, height, tmp;
188
PLColor bgcolor = pls->cmap0[0];
191
gcw_debug("<plD_init_gcw>\n");
194
/* Parse the driver options */
195
plParseDrvOpts(gcw_options);
197
/* Set up the stream */
198
pls->termin = 1; /* Is an interactive terminal */
199
pls->dev_flush = 1; /* Handle our own flushes */
200
pls->plbuf_write = replot; /* Use plot buffer to replot to another device */
202
pls->dev_clear = 0; /* Handle plclear() */
203
pls->dev_fill0 = 1; /* Handle solid fills */
205
/* Create the device */
206
if((dev = g_malloc(sizeof(GcwPLdev))) == NULL)
207
plexit("GCW driver <plD_init_gcw>: Cannot create device");
210
/* Set text handling */
213
pls->dev_text = TRUE;
214
pls->dev_unicode = TRUE;
215
if(hrshsym) pls->dev_hrshsym = 1;
217
/* Initialize freetype */
218
plD_FreeType_init(pls);
221
pls->dev_text = FALSE;
222
pls->dev_unicode = FALSE;
225
pls->dev_text = FALSE;
226
pls->dev_unicode = FALSE;
229
/* Set up pixmap support */
230
dev->use_pixmap = (gboolean)(!pls->nopixmap);
231
dev->pixmap_has_data = FALSE;
233
/* Initialize the device colors */
234
dev->color = plcolor_to_rgba(pls->cmap0[pls->icol0],0xFF);
235
dev->bgcolor.red=(guint16)(bgcolor.r/255.*65535);
236
dev->bgcolor.green=(guint16)(bgcolor.b/255.*65535);
237
dev->bgcolor.blue=(guint16)(bgcolor.g/255.*65535);
239
/* Set the device canvas and window pointers */
241
dev->background = NULL;
243
dev->colormap = NULL;
245
dev->notebook = NULL;
246
dev->statusbar = NULL;
249
/* Initialize the Canvas groups. All of the plplot plotting
250
* commands are drawn to the hidden group. When the page is finalized,
251
* the group is made visible, and the old group destroyed. The persistent
252
* group is never erased, and always plotted at the very front.
254
dev->group_visible=NULL;
255
dev->group_hidden=NULL;
256
dev->group_persistent=NULL;
258
/* Assume that pladv should completeley refresh the page */
259
dev->use_persistence = FALSE;
261
/* Set the initialization state monitors to FALSE */
262
dev->plstate_width = FALSE;
263
dev->plstate_color0 = FALSE;
264
dev->plstate_color1 = FALSE;
269
/* Set up the physical device in the next series of commands. It is very
270
* important to do this properly, because many PLplot routines depend on
271
* physical coordinates (e.g., dashed lines, hatched areas, the
272
* replot mechanism, hidden line removal, etc.
274
* Note that coordinates in the driver are measured in device units,
275
* which correspond to the pixel size on a typical screen. The coordinates
276
* reported and received from the PLplot core, however, are in virtual
277
* coordinates, which is just a scaled version of the device coordinates.
278
* This strategy is used so that the calculations in the PLplot
279
* core are performed at reasonably high resolution.
282
if (pls->xlength > 0 && pls->ylength > 0) {
283
/* xlength and length are the dimensions specified using -geometry
284
* on the command line, in device coordinates.
286
width = pls->xlength;
287
height = pls->ylength;
290
width = (PLINT)(CANVAS_WIDTH*DEVICE_PIXELS_PER_IN);
291
height = (PLINT)(CANVAS_HEIGHT*DEVICE_PIXELS_PER_IN);
294
/* If portrait mode, apply a rotation and set freeaspect */
296
plsdiori((PLFLT)(4 - ORIENTATION));
300
/* Setup the page size for this device. Very important for any driver! */
301
gcw_set_device_size(width,height);
303
/* Install a canvas... unless plsc->hack is set, which is a driver-specific
304
* hack that indicates a PLESC_DEVINIT escape call will provide us with a
305
* canvas to use. This hack is used by the PlplotCanvas.
308
dev->allow_resize = FALSE; /* The size is set an should not be changed */
309
gcw_install_canvas(NULL);
311
else dev->allow_resize = TRUE; /* Resizing allowed for canvasses
312
* provided via PLESC_DEVINIT */
316
gcw_debug("</plD_init_gcw>\n");
321
/*--------------------------------------------------------------------------*\
324
* Draw a polyline in the current color.
325
\*--------------------------------------------------------------------------*/
327
void plD_polyline_gcw(PLStream *pls, short *x, short *y, PLINT npts)
329
GcwPLdev* dev = pls->dev;
330
GnomeCanvasPoints* points;
331
GnomeCanvasPoints pts;
332
GnomeCanvasGroup* group;
333
GnomeCanvasItem* item;
344
gcw_debug("<plD_polyline_gcw />\n");
347
if(!GNOME_IS_CANVAS(dev->canvas))
348
plexit("GCW driver <plD_polyline_gcw>: Canvas not found");
349
canvas = dev->canvas;
351
if(dev->use_persistence) group = dev->group_persistent;
352
else group = dev->group_hidden;
354
if(dev->use_pixmap && !dev->use_persistence) { /* Write to bg pixmap */
356
if((gdkpoints = (GdkPoint*)malloc(npts*sizeof(GdkPoint)))==NULL)
357
plabort("GCW driver <plD_polyline_gcw>: Could not create gdkpoints");
360
for(i=0;i<npts;i++) {
361
gdkpoints[i].x = (gint)(x[i]/VSCALE);
362
gdkpoints[i].y = (gint)(dev->height-y[i]/VSCALE);
365
else { /* Swap x and y for portrait mode */
366
for(i=0;i<npts;i++) {
367
gdkpoints[i].x = (gint)(dev->height-y[i]/VSCALE);
368
gdkpoints[i].y = (gint)(dev->width-x[i]/VSCALE);
372
gdk_draw_lines(dev->background,dev->gc,gdkpoints,npts);
374
dev->pixmap_has_data = TRUE;
378
else { /* Draw Canvas lines */
380
/* Put the data in a points structure */
381
if( (points = gnome_canvas_points_new(npts)) == NULL )
382
plabort("GCW driver <plD_polyline_gcw>: Cannot create points");
384
for ( i = 0; i < npts; i++ ) {
385
points->coords[2*i] = (gdouble)(x[i]/VSCALE);
386
points->coords[2*i + 1] = (gdouble)(-y[i]/VSCALE);
389
else { /* Swap x and y for portrait mode */
390
for ( i = 0; i < npts; i++ ) {
391
points->coords[2*i] = (gdouble)(dev->height-y[i]/VSCALE);
392
points->coords[2*i + 1] = (gdouble)(-x[i]/VSCALE);
396
/* Get the pen width and color */
401
/* Workaround for the 'attempt to put segment in horiz list twice'
402
* from libgnomecanvas:
404
* Plot a series of line segments rather than a single polyline.
406
* This slows rendering down a considerable amount. However, it is
407
* unclear what else can be done. Libgnomecanvas should be able to
408
* deal with all valid data; bizarre plotting errors happen along with
411
* Note that instead of allocating a series of points structures,
412
* we just refer to the original one from a separate struct
413
* (GnomeCanvas does not hold a reference to the points structure).
418
pts.coords = points->coords;
420
for(i=0;i<npts-1;i++) {
421
pts.coords=&(points->coords[2*i]);
423
if(!GNOME_IS_CANVAS_ITEM(
424
item=gnome_canvas_item_new(group,
425
GNOME_TYPE_CANVAS_LINE,
426
"cap_style", GDK_CAP_ROUND,
427
"join-style", GDK_JOIN_ROUND,
429
"fill-color-rgba", color,
430
"width-units", width,
433
plwarn("GCW driver <plD_polyline_gcw>: Canvas item not created.");
437
/* Free the points structure */
438
gnome_canvas_points_free(points);
443
/*--------------------------------------------------------------------------*\
446
* Draw a line in the current color from (x1,y1) to (x2,y2).
447
\*--------------------------------------------------------------------------*/
449
void plD_line_gcw(PLStream *pls, short x1, short y1, short x2, short y2)
455
gcw_debug("<plD_line_gcw />\n");
463
plD_polyline_gcw(pls, x, y, (PLINT) 2);
467
/*--------------------------------------------------------------------------*\
471
\*--------------------------------------------------------------------------*/
473
void plD_eop_gcw(PLStream *pls)
475
GcwPLdev* dev = pls->dev;
479
GnomeCanvasItem* item;
480
GnomeCanvasGroup* group;
495
if(!GNOME_IS_CANVAS(dev->canvas))
496
plexit("GCW driver <plD_eop_gcw>: Canvas not found");
497
canvas = dev->canvas;
499
/* Ignore if there is no hidden group. This means BOP has not been
502
if(!GNOME_IS_CANVAS_GROUP(dev->group_hidden)) return;
505
gcw_debug("<plD_eop_gcw>\n");
508
if(dev->use_persistence) group = dev->group_persistent;
509
else group = dev->group_hidden;
511
/* Retrieve the device width and height of the canvas */
512
width = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"canvas-width");
513
height = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"canvas-height");
515
if(dev->pixmap_has_data) {
517
/* Render the pixmap to a pixbuf on the canvas. */
518
if(!GDK_IS_PIXBUF(pixbuf=gdk_pixbuf_get_from_drawable(NULL,
524
plwarn("GCW driver <plD_eop_gcw>: Can't draw pixmap into pixbuf.");
526
else { /* Pixbuf creation succeeded */
528
if(!GNOME_IS_CANVAS_ITEM(
529
item = gnome_canvas_item_new(dev->group_hidden,
530
GNOME_TYPE_CANVAS_PIXBUF,
533
"y", (gdouble)(-height+1.),
534
"width", (gdouble)(width),
535
"height", (gdouble)(height),
538
plwarn("GCW driver <plD_eop_gcw>: Canvas item not created.");
541
/* Free the pixbuf */
542
g_object_unref(pixbuf);
547
/* Use a rectangle for the background instead (faster) */
548
if(!GNOME_IS_CANVAS_ITEM(
549
item = gnome_canvas_item_new(
551
GNOME_TYPE_CANVAS_RECT,
553
"y1", (gdouble)(-height),
554
"x2", (gdouble)(width),
556
"fill-color-rgba", plcolor_to_rgba(pls->cmap0[0],0xFF),
560
plabort("GCW driver <pld_eop_gcw>: Canvas item not created");
564
/* Move the persistent group to the front */
565
gnome_canvas_item_raise_to_top(GNOME_CANVAS_ITEM(dev->group_persistent));
567
/* Move the background to the back */
568
if(GNOME_IS_CANVAS_ITEM(item)) gnome_canvas_item_lower_to_bottom(item);
570
/* Make the hidden group visible */
571
gnome_canvas_item_show(GNOME_CANVAS_ITEM(dev->group_hidden));
573
/* Destroy the old visible group */
574
if(GNOME_IS_CANVAS_GROUP(dev->group_visible)) {
575
gtk_object_destroy((GtkObject*)(dev->group_visible));
576
dev->group_visible = NULL;
579
/* Clear the background pixmap */
580
if(!dev->use_persistence && dev->pixmap_has_data) gcw_clear_background();
582
/* Name the hidden group as visible */
583
dev->group_visible = dev->group_hidden;
584
dev->group_hidden=NULL;
586
/* Update the canvas */
587
canvas->need_update = 1;
588
gnome_canvas_update_now(canvas);
591
* Copy the plot buffer for future reference, otherwise it is
592
* thrown out. We will also need to store the colormaps.
594
if(pls->plbuf_write) {
596
pls->plbuf_write = FALSE;
597
pls->plbuf_read = TRUE;
599
/* Remove the old tempfile, if it exists */
600
if( (f=g_object_get_data(G_OBJECT(canvas),"plotbuf")) != NULL ) {
602
g_object_set_data(G_OBJECT(canvas),"plotbuf",NULL);
605
/* Copy the plot buffer to a tempfile */
606
if((f=tmpfile())==NULL) {
607
plwarn("GCW driver <plD_eop_gcw>: Could not create tempfile.");
610
rewind(pls->plbufFile);
611
while(count = fread(&tmp, sizeof(U_CHAR), 1, pls->plbufFile)) {
612
if(fwrite(&tmp, sizeof(U_CHAR), 1, f)!=count) {
613
plwarn("GCW driver <plD_eop_gcw>: Could not write to tempfile.");
619
/* Attach the tempfile to the canvas */
620
g_object_set_data(G_OBJECT(canvas),"plotbuf",(gpointer)f);
622
pls->plbuf_write = TRUE;
623
pls->plbuf_read = FALSE;
626
if((icol=(gint*)malloc(sizeof(gint)))==NULL)
627
plwarn("GCW driver <plD_eop_gcw>: Insufficient memory.");
628
else *icol = pls->icol0;
629
g_object_set_data(G_OBJECT(canvas),"icol0",(gpointer)icol);
630
if((ncol=(gint*)malloc(sizeof(gint)))==NULL)
631
plwarn("GCW driver <plD_eop_gcw>: Insufficient memory.");
632
else *ncol = pls->ncol0;
633
g_object_set_data(G_OBJECT(canvas),"ncol0",(gpointer)ncol);
634
if((cmap=(PLColor *) calloc(1, pls->ncol0 * sizeof(PLColor)))==NULL) {
635
plwarn("GCW driver <plD_eop_gcw>: Insufficient memory.");
638
for (i = 0; i < pls->ncol0; i++)
639
pl_cpcolor(&cmap[i], &pls->cmap0[i]);
641
g_object_set_data(G_OBJECT(canvas),"cmap0",(gpointer)cmap);
644
if((icol=(gint*)malloc(sizeof(gint)))==NULL)
645
plwarn("GCW driver <plD_eop_gcw>: Insufficient memory.");
647
else *icol = pls->icol1;
648
g_object_set_data(G_OBJECT(canvas),"icol1",(gpointer)icol);
649
if((ncol=(gint*)malloc(sizeof(gint)))==NULL)
650
plwarn("GCW driver <plD_eop_gcw>: Insufficient memory.");
651
else *ncol = pls->ncol1;
652
g_object_set_data(G_OBJECT(canvas),"ncol1",(gpointer)ncol);
653
if((cmap=(PLColor *) calloc(1, pls->ncol1 * sizeof(PLColor)))==NULL) {
654
plwarn("GCW driver <plD_eop_gcw>: Insufficient memory.");
657
for (i = 0; i < pls->ncol1; i++)
658
pl_cpcolor(&cmap[i], &pls->cmap1[i]);
660
g_object_set_data(G_OBJECT(canvas),"cmap1",(gpointer)cmap);
664
/* If the driver is creating its own canvasses, set dev->canvas to be
665
* NULL now in order to force creation of a new canvas when the next
666
* drawing call is made. The new canvas will be placed in a new
669
if(dev->window!=NULL) {
671
dev->group_visible = NULL;
672
dev->group_hidden = NULL;
673
dev->group_persistent = NULL;
677
gcw_debug("</plD_eop_gcw>\n");
682
/*--------------------------------------------------------------------------*\
685
* Set up for the next page.
687
\*--------------------------------------------------------------------------*/
689
void plD_bop_gcw(PLStream *pls)
691
GcwPLdev* dev = pls->dev;
694
if(!GNOME_IS_CANVAS(dev->canvas)) {
695
if(pls->hack) return; /* Wait for a canvas via DEVINIT */
696
else gcw_install_canvas(NULL);
698
canvas = dev->canvas;
701
gcw_debug("<plD_bop_gcw>\n");
704
/* Replay escape calls that come in before PLESC_DEVINIT. Some of them
705
* required a Canvas that didn't exist yet.
707
if(dev->plstate_width) plD_state_gcw(pls, PLSTATE_WIDTH);
708
if(dev->plstate_color0) plD_state_gcw(pls, PLSTATE_COLOR0);
709
if(dev->plstate_color1) plD_state_gcw(pls, PLSTATE_COLOR1);
710
dev->plstate_width = FALSE;
711
dev->plstate_color0 = FALSE;
712
dev->plstate_color1 = FALSE;
714
/* Creat a new hidden group; all new drawing will be to this group */
715
if(!GNOME_IS_CANVAS_ITEM(
716
dev->group_hidden = GNOME_CANVAS_GROUP(gnome_canvas_item_new(
717
gnome_canvas_root(canvas),
718
gnome_canvas_clipgroup_get_type(),
723
plexit("GCW driver <plD_bop_gcw>: Canvas group cannot be created");
726
/* Set the clip to NULL */
727
g_object_set(G_OBJECT(dev->group_hidden),"path",NULL,NULL);
729
/* Hide this group until drawing is done */
730
gnome_canvas_item_hide(GNOME_CANVAS_ITEM(dev->group_hidden));
733
gcw_debug("</plD_bop_gcw>\n");
738
/*--------------------------------------------------------------------------*\
741
* Close graphics file
742
\*--------------------------------------------------------------------------*/
744
void plD_tidy_gcw(PLStream *pls)
746
GcwPLdev* dev = pls->dev;
749
gcw_debug("<plD_tidy_gcw>\n");
754
FT_Data *FT=(FT_Data *)pls->FT;
755
plscmap0n(FT->ncol0_org);
756
plD_FreeType_Destroy(pls);
760
if(dev->window!=NULL) {
765
gcw_debug("</plD_tidy_gcw>\n");
770
/*--------------------------------------------------------------------------*\
773
* Handle change in PLStream state (color, pen width, fill attribute, etc).
775
* Note that PLplot sometimes tries to change states before the device is
776
* fully initialized (i.e., via PLESC_DEVINIT). We must keep track of
777
* such attempts, and invoke the state change during the next call to
780
\*--------------------------------------------------------------------------*/
782
void plD_state_gcw(PLStream *pls, PLINT op)
784
GcwPLdev* dev = pls->dev;
785
char opname[20],msg[100];
788
if(op==PLSTATE_WIDTH) strcpy(opname,"PLSTATE_WIDTH");
789
else if(op==PLSTATE_COLOR0) strcpy(opname,"PLSTATE_COLOR0");
790
else if(op==PLSTATE_COLOR1) strcpy(opname,"PLSTATE_COLOR1");
791
else if(op==PLSTATE_FILL) strcpy(opname,"PLSTATE_FILL");
792
else if(op==PLSTATE_CMAP0) strcpy(opname,"PLSTATE_CMAP0");
793
else if(op==PLSTATE_CMAP1) strcpy(opname,"PLSTATE_CMAP1");
794
else strcpy(opname,"unknown");
795
sprintf(msg,"<plD_state_gcw />: %s\n",opname);
802
if(GNOME_IS_CANVAS(dev->canvas)) {
803
if(dev->use_pixmap) {
804
gdk_gc_set_line_attributes(dev->gc, pls->width,
810
else dev->plstate_width = TRUE;
814
if(GNOME_IS_CANVAS(dev->canvas)) {
815
dev->color = plcolor_to_rgba(pls->cmap0[pls->icol0],0xFF);
816
if(dev->use_pixmap) gcw_set_gdk_color();
818
else dev->plstate_color0 = TRUE;
822
if(GNOME_IS_CANVAS(dev->canvas)) {
823
dev->color = plcolor_to_rgba(pls->cmap1[pls->icol1],0xFF);
824
if(dev->use_pixmap) gcw_set_gdk_color();
826
else dev->plstate_color1 = TRUE;
844
/*--------------------------------------------------------------------------*\
847
* Fills the polygon defined by the given points. Used for shade
848
* plotting. Only solid fills are allowed.
849
\*--------------------------------------------------------------------------*/
851
static void fill_polygon (PLStream* pls)
853
GnomeCanvasPoints* points;
854
GnomeCanvasGroup* group;
855
GnomeCanvasItem* item;
856
GcwPLdev* dev = pls->dev;
866
gcw_debug("<fill_polygon />\n");
869
if(!GNOME_IS_CANVAS(dev->canvas))
870
plexit("GCW driver <fill_polygon>: Canvas not found");
871
canvas = dev->canvas;
873
if(dev->use_persistence) group = dev->group_persistent;
874
else group = dev->group_hidden;
876
if(dev->use_pixmap && !dev->use_persistence) { /* Write to a pixmap */
878
if((gdkpoints = (GdkPoint*)malloc(pls->dev_npts*sizeof(GdkPoint)))==NULL)
879
plabort("GCW driver <fill_polygon>: Could not create gdkpoints");
882
for(i=0;i<pls->dev_npts;i++) {
883
gdkpoints[i].x = (gint)(pls->dev_x[i]/VSCALE);
884
gdkpoints[i].y = (gint)(dev->height-pls->dev_y[i]/VSCALE);
887
else { /* Swap x and y for portrait mode */
888
for(i=0;i<pls->dev_npts;i++) {
889
gdkpoints[i].x = (gint)(dev->height-pls->dev_y[i]/VSCALE);
890
gdkpoints[i].y = (gint)(dev->width-pls->dev_x[i]/VSCALE);
894
gdk_draw_polygon(dev->background,dev->gc,TRUE,gdkpoints,pls->dev_npts);
896
dev->pixmap_has_data = TRUE;
900
else { /* Use Gnome Canvas polygons */
902
if( (points = gnome_canvas_points_new (pls->dev_npts)) == NULL )
903
plabort("GCW driver <fill_polygon>: Could not create points");
906
for (i=0; i<pls->dev_npts; i++) {
907
points->coords[2*i] = (gdouble)(pls->dev_x[i]/VSCALE);
908
points->coords[2*i + 1] = (gdouble)(-pls->dev_y[i]/VSCALE);
911
else { /* Swap x and y for portrait mode */
912
for (i=0; i<pls->dev_npts; i++) {
913
points->coords[2*i] = (gdouble)(dev->height-pls->dev_y[i]/VSCALE);
914
points->coords[2*i + 1] = (gdouble)(-pls->dev_x[i]/VSCALE);
918
if(!GNOME_IS_CANVAS_ITEM(
919
item = gnome_canvas_item_new (group,
920
GNOME_TYPE_CANVAS_POLYGON,
922
"fill-color-rgba",dev->color,
923
/* "outline-color-rgba",dev->color, */
926
plwarn("GCW driver <fill_polygon>: Canvas item not created.");
929
gnome_canvas_points_free(points);
932
/* Draw a thin outline for each polygon; note that doing this
933
* using the "outline-color-rgba" property above can result in
938
plD_polyline_gcw(pls,pls->dev_x,pls->dev_y,pls->dev_npts);
944
/*--------------------------------------------------------------------------*\
947
* Handles call to draw text on the canvas when the HAS_TEXT escape funtion
950
* This routine is unicode enabled, and requires freetype.
951
\*--------------------------------------------------------------------------*/
953
void proc_str(PLStream *pls, EscText *args)
955
PLFLT *t = args->xform; /* Transform matrix for string */
957
GnomeCanvasGroup* group;
958
GcwPLdev* dev = pls->dev;
961
PLUNICODE fci; /* The unicode font characterization integer */
962
guchar *fontname = NULL;
966
GnomeGlyphList *glyphlist;
969
gdouble affine_baseline[6] = {0.,0.,0.,0.,0.,0.}; /* Affine transforms */
970
gdouble affine_translate[6] = {0.,0.,0.,0.,0.,0.};
971
gdouble affine_rotate[6] = {0.,0.,0.,0.,0.,0.};
972
gdouble affine_plplot[6] = {0.,0.,0.,0.,0.,0.};
974
GnomeCanvasItem* item[200]; /* List of string segments */
975
gdouble width[200],height[200]; /* Height and width of string segment */
976
gdouble up_list[200]; /* Indicates sub/sup position of string segment */
977
gdouble up=0,scale=1; /* Used to create superscripts and subscripts */
979
ArtDRect bbox; /* Bounding box for each segment to get width & height */
981
const PLUNICODE *text; /* The text and pointers to it */
982
guint i=0,Ntext; /* The text index and maximum length */
984
char esc; /* The escape character */
986
guint N=0; /* The number of text segments */
987
gdouble total_width=0,sum_width=0;
993
gcw_debug("<proc_str>\n");
996
if(!GNOME_IS_CANVAS(dev->canvas))
997
plexit("GCW driver <proc_str>: Canvas not found");
998
canvas = dev->canvas;
1000
if(dev->use_persistence) group = dev->group_persistent;
1001
else group = dev->group_hidden;
1003
/* Retrieve the escape character */
1006
/* Put the transform matrix values in the order expected by libart.
1007
* Note that the plplot transform matrix only has a rotation and shear;
1008
* plplot's rotation direction and shear are opposite from that expected
1009
* by libart, hence the negative signs below.
1011
affine_plplot[0] = t[0]; /* cos(theta) */
1012
affine_plplot[1] = -t[2]; /* sin(theta) */
1013
affine_plplot[2] = -t[1]; /* a cos(theta) - sin(theta) */
1014
affine_plplot[3] = t[3]; /* a sin(theta) + cos(theta) */
1016
/* Font size: size is in pixels but chrht is in mm. Why the extra factor? */
1017
font_size = (gint)(pls->chrht*DEVICE_PIXELS_PER_MM*1.5);
1019
/* Determine the default font */
1021
fontname = plP_FCI2FontName(fci, FontLookup, N_TrueTypeLookup);
1022
if (fontname == NULL) {
1023
plabort("GCW driver <proc_str>: FCI inconsistent with TrueTypeLookup");
1026
/* Retrieve the font face */
1027
face = gnome_font_face_find_from_filename(fontname,0);
1029
/* Get the unicode string */
1030
text = args->unicode_array;
1031
Ntext = (guint)(args->unicode_array_len);
1033
/* Process the string: Break it into segments of constant font and size,
1034
* making sure we process control characters as we come to them. Save
1035
* the extra information that will allow us to place the text on the
1040
/* Process the next character */
1042
if(text[i] & PL_FCI_MARK) { /* Is it a font characterization index? */
1044
/* Determine the font name */
1045
fontname = plP_FCI2FontName(text[i], FontLookup, N_TrueTypeLookup);
1046
if (fontname == NULL) {
1047
plabort("GCW driver <proc_str>: FCI inconsistent with "
1051
/* Retrieve the font face */
1052
gnome_font_unref(face); /* We already have a face */
1053
face = gnome_font_face_find_from_filename(fontname,0);
1055
i++; /* Move ahead to the next character */
1060
if(text[i] == esc) { /* Check for escape sequences */
1062
/* Process escape sequence */
1064
i++; /* Move on to next character */
1066
plwarn("GCW driver <proc_str>: Invalid escape sequence "
1067
"provided in text.");
1073
case '#': /* <esc><esc>; this should translate to a hash */
1074
break; /* Watch out for it later */
1076
/* Move to lower sub/sup position */
1079
if(up>0.) scale *= 1.25; /* Subscript scaling parameter */
1080
else scale *= 0.8; /* Subscript scaling parameter */
1081
up -= font_size / 2.;
1084
/* Move to higher sub/sup position */
1087
if(up<0.) scale *= 1.25; /* Subscript scaling parameter */
1088
else scale *= 0.8; /* Subscript scaling parameter */
1089
up += font_size / 2.;
1092
/* Ignore the next sequences */
1103
plwarn("GCW driver <proc_str>: '+', '-', and 'b' text "
1104
"escape sequences not processed.");
1107
} /* switch(text[i]) */
1109
if(text[i]!='#') i++; /* Move ahead to the next character */
1111
} /* if(text[i] == esc) */
1112
} /* if(text[i] & PL_FCI_MARK) */
1115
if(i==Ntext) continue; /* End of string */
1117
/* Save the sub/sup position */
1121
font = gnome_font_face_get_font_default(face,font_size*scale);
1122
/* printf("\n\nfont name = %s\n\n",gnome_font_get_name(font)); */
1124
/* Create the glyphlist for this text segment */
1125
glyphlist = gnome_glyphlist_new ();
1126
gnome_glyphlist_font(glyphlist, font);
1127
gnome_glyphlist_color(glyphlist,dev->color);
1128
gnome_glyphlist_advance(glyphlist, TRUE);
1129
gnome_glyphlist_kerning(glyphlist, 0.);
1130
gnome_glyphlist_letterspace(glyphlist, 0.);
1133
gnome_font_unref(font);
1135
/* Move along to the next escape or FCI character, stuffing
1136
* everything else into the glyphlist.
1139
while(i<Ntext && !(text[i] & PL_FCI_MARK)) {
1141
/* Differentiate between ## and escape sequences */
1143
if( !(i>0 && text[i-1]==esc) ) break;
1146
gnome_glyphlist_glyph(glyphlist,
1147
gnome_font_lookup_default(font,text[i]));
1153
/* Determine the bounding box of the text */
1154
gnome_glyphlist_bbox(glyphlist,NULL,0,&bbox);
1155
width[N] = bbox.x1-bbox.x0;
1156
height[N] = bbox.y1-bbox.y0;
1158
/* Keep track of the total string width so that we can justify it */
1159
total_width += width[N];
1160
if(N!=0) total_width += 2; /* Add a little extra space */
1162
/* Create the canvas text item */
1163
if(!GNOME_IS_CANVAS_ITEM(
1164
item[N] = gnome_canvas_item_new (group,
1165
PLPLOT_TYPE_CANVAS_HACKTEXT,
1166
"glyphlist",glyphlist,
1167
"fill-color-rgba",dev->color,
1172
plabort("GCW driver <proc_str>: Canvas item not created");
1175
/* Free the glyphlist */
1176
gnome_glyphlist_unref(glyphlist);
1178
/* Advance to next string segment */
1183
/* Don't overflow buffer */
1184
if(N==200 && i<Ntext)
1185
plabort("GCW driver <proc_str>: too many text segments");
1187
} /* while(i<Ntext) */
1189
/* We have all of the string segments. Place each on the canvas
1194
/* Calculate and apply the affine transforms */
1195
art_affine_rotate(affine_rotate,90.*(pls->diorot-pls->portrait));
1196
if(!pls->portrait) {
1197
art_affine_translate(affine_baseline,
1198
-total_width*args->just + sum_width,
1199
height[0]/2.5-up_list[i]);
1200
art_affine_translate(affine_translate,
1201
args->x/VSCALE,-args->y/VSCALE);
1203
else { /* Swap x and y for portrait mode */
1204
art_affine_translate(affine_baseline,
1205
-total_width*args->just + sum_width,
1206
height[0]/2.5-up_list[i]);
1207
art_affine_translate(affine_translate,
1208
dev->height-args->y/VSCALE,-args->x/VSCALE);
1210
gnome_canvas_item_affine_relative(item[i],affine_translate);
1211
gnome_canvas_item_affine_relative(item[i],affine_rotate);
1212
gnome_canvas_item_affine_relative(item[i],affine_plplot);
1213
gnome_canvas_item_affine_relative(item[i],affine_baseline);
1215
/* Keep track of the position in the string */
1216
sum_width += width[i];
1217
if(i!=N-1) sum_width += 2; /* Add a little extra space */
1221
gcw_debug("</proc_str>\n");
1226
/*--------------------------------------------------------------------------*\
1231
\*--------------------------------------------------------------------------*/
1233
void plD_esc_gcw(PLStream *pls, PLINT op, void *ptr)
1235
GcwPLdev* dev = pls->dev;
1238
char opname[20], msg[100];
1239
if(op==PLESC_DEVINIT) strcpy(opname,"PLESC_DEVINIT");
1240
else if(op==PLESC_CLEAR) strcpy(opname,"PLESC_CLEAR");
1241
else if(op==PLESC_FILL) strcpy(opname,"PLESC_FILL");
1242
else if(op==PLESC_HAS_TEXT) strcpy(opname,"PLESC_HAS_TEXT");
1243
else if(op==PLESC_GRAPH) strcpy(opname,"PLESC_GRAPH");
1244
else strcpy(opname,"unknown");
1245
sprintf(msg,"<plD_esc_gcw />: %s\n",opname);
1252
gcw_init_canvas(GNOME_CANVAS(ptr));
1263
case PLESC_HAS_TEXT:
1264
proc_str(pls, ptr); /* Draw the text */