PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
gcw.c
Go to the documentation of this file.
1 // gcw-driver - PLplot Gnome Canvas Widget device driver.
2 //
3 // Copyright (C) 2004, 2005 Thomas J. Duck
4 // Copyright (C) 2004 Rafael Laboissiere
5 // All rights reserved.
6 //
7 //
8 // NOTICE
9 //
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.
14 //
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.
19 //
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
23 // USA
24 //
25 //
26 // DESCRIPTION
27 //
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
32 // GnomeCanvas.
33 //
34 // Please see the PLplot documentation for more information.
35 //
36 //
37 // DEVELOPMENT NOTES
38 //
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.
42 //
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.
52 //
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
57 // in the GnomeCanvas.
58 //
59 //
60 // KNOWN BUGS
61 //
62 // PLplot test suite problems:
63 //
64 // 1) Example x10c does not clip the text (there is no text clipping).
65 //
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
70 // instead.
71 //
72 
73 #include <sys/stat.h>
74 
75 #include "gcw.h"
76 #include "plplotcanvas-hacktext.h"
77 
78 #ifdef PL_HAVE_FREETYPE
79 
80 #include "plfreetype.h"
81 #include "plfci-truetype.h"
82 
83 // Font lookup table that is constructed in plD_FreeType_init
84 extern FCI_to_FontName_Table FontLookup[N_TrueTypeLookup];
85 
86 #endif // PL_HAVE_FREETYPE
87 
88 
89 // Device info
90 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_gcw = "gcw:Gnome Canvas Widget:1:gcw:10:gcw\n";
91 
92 // Global driver options
93 
94 #ifdef PL_HAVE_FREETYPE
95 static PLINT text = 1;
96 #else
97 static PLINT text = 0;
98 #endif
99 
100 static PLINT hrshsym = 0;
101 static PLINT replot = 1;
102 
103 static DrvOpt gcw_options[] =
104 {
105  { "text", DRV_INT, &text, "Use truetype fonts (text=0|1)" },
106  { "hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)" },
107  { "replot", DRV_INT, &replot, "Allow replotting to other devices (replot=0|1)" },
108  { NULL, DRV_INT, NULL, NULL }
109 };
110 
111 
112 //********************
113 // Utility functions *
114 //*******************
115 
116 guint32 plcolor_to_rgba( PLColor color, guchar alpha )
117 {
118  return
119  ( (int) ( color.r ) << 24 )
120  + ( (int) ( color.g ) << 16 )
121  + ( (int) ( color.b ) << 8 )
122  + alpha;
123 }
124 
125 
126 //--------------------------------------------------------------------------
127 // plD_dispatch_init_gcw()
128 //
129 // Initializes the dispatch table.
130 //--------------------------------------------------------------------------
131 
132 void plD_open_gcw( PLStream *pls );
133 void plD_init_gcw( PLStream * );
134 void plD_line_gcw( PLStream *, short, short, short, short );
135 void plD_polyline_gcw( PLStream *, short *, short *, PLINT );
136 void plD_eop_gcw( PLStream * );
137 void plD_bop_gcw( PLStream * );
138 void plD_tidy_gcw( PLStream * );
139 void plD_state_gcw( PLStream *, PLINT );
140 void plD_esc_gcw( PLStream *, PLINT, void * );
141 
143 {
144 #ifdef DEBUG_GCW_1
145  gcw_debug( "<plD_dispatch_init_gcw>\n" );
146 #endif
147 
148 #ifndef ENABLE_DYNDRIVERS
149  pdt->pl_MenuStr = "Gnome Canvas Widget";
150  pdt->pl_DevName = "gcw";
151 #endif
153  pdt->pl_seq = 1;
157  pdt->pl_eop = (plD_eop_fp) plD_eop_gcw;
158  pdt->pl_bop = (plD_bop_fp) plD_bop_gcw;
161  pdt->pl_esc = (plD_esc_fp) plD_esc_gcw;
162 
163 #ifdef DEBUG_GCW_1
164  gcw_debug( "</plD_dispatch_init_gcw>\n" );
165 #endif
166 }
167 
168 
169 //--------------------------------------------------------------------------
170 // plD_init_gcw()
171 //
172 // Initializes the device.
173 //
174 // This routine is invoked by a call to plinit.
175 //
176 //--------------------------------------------------------------------------
177 
178 void plD_init_gcw( PLStream *pls )
179 {
180  GcwPLdev* dev;
181 
182  PLINT width, height, tmp;
183 
184  PLColor bgcolor = pls->cmap0[0];
185 
186 #ifdef DEBUG_GCW_1
187  gcw_debug( "<plD_init_gcw>\n" );
188 #endif
189 
190  // Parse the driver options
191  plParseDrvOpts( gcw_options );
192 
193  // Set up the stream
194  pls->termin = 1; // Is an interactive terminal
195  pls->dev_flush = 1; // Handle our own flushes
196  pls->plbuf_write = replot; // Use plot buffer to replot to another device
197  pls->width = 1;
198  pls->dev_clear = 0; // Handle plclear()
199  pls->dev_fill0 = 1; // Handle solid fills
200 
201  // Create the device
202  if ( ( dev = g_malloc( sizeof ( GcwPLdev ) ) ) == NULL )
203  plexit( "GCW driver <plD_init_gcw>: Cannot create device" );
204  pls->dev = dev;
205 
206  // Set text handling
207 #ifdef PL_HAVE_FREETYPE
208  if ( text )
209  {
210  pls->dev_text = TRUE;
211  pls->dev_unicode = TRUE;
212  if ( hrshsym )
213  pls->dev_hrshsym = 1;
214 
215  // Initialize freetype
216  plD_FreeType_init( pls );
217  }
218  else
219  {
220  pls->dev_text = FALSE;
221  pls->dev_unicode = FALSE;
222  }
223 #else
224  pls->dev_text = FALSE;
225  pls->dev_unicode = FALSE;
226 #endif
227 
228  // Set up pixmap support
229  dev->use_pixmap = (gboolean) ( !pls->nopixmap );
230  dev->pixmap_has_data = FALSE;
231 
232  // Initialize the device colors
233  dev->color = plcolor_to_rgba( pls->cmap0[pls->icol0], 0xFF );
234  dev->bgcolor.red = (guint16) ( bgcolor.r / 255. * 65535 );
235  dev->bgcolor.green = (guint16) ( bgcolor.b / 255. * 65535 );
236  dev->bgcolor.blue = (guint16) ( bgcolor.g / 255. * 65535 );
237 
238  // Set the device canvas and window pointers
239  dev->canvas = NULL;
240  dev->background = NULL;
241  dev->gc = NULL;
242  dev->colormap = NULL;
243  dev->window = NULL;
244  dev->notebook = NULL;
245  dev->statusbar = NULL;
246  dev->filew = NULL;
247 
248  // Initialize the Canvas groups. All of the plplot plotting
249  // commands are drawn to the hidden group. When the page is finalized,
250  // the group is made visible, and the old group destroyed. The persistent
251  // group is never erased, and always plotted at the very front.
252  //
253  dev->group_visible = NULL;
254  dev->group_hidden = NULL;
255  dev->group_persistent = NULL;
256 
257  // Assume that pladv should completeley refresh the page
258  dev->use_persistence = FALSE;
259 
260  // Set the initialization state monitors to FALSE
261  dev->plstate_width = FALSE;
262  dev->plstate_color0 = FALSE;
263  dev->plstate_color1 = FALSE;
264 
265  // Initialize gtk
266  gtk_init( 0, NULL );
267 
268  // Set up the physical device in the next series of commands. It is very
269  // important to do this properly, because many PLplot routines depend on
270  // physical coordinates (e.g., dashed lines, hatched areas, the
271  // replot mechanism, hidden line removal, etc.
272  //
273  // Note that coordinates in the driver are measured in device units,
274  // which correspond to the pixel size on a typical screen. The coordinates
275  // reported and received from the PLplot core, however, are in virtual
276  // coordinates, which is just a scaled version of the device coordinates.
277  // This strategy is used so that the calculations in the PLplot
278  // core are performed at reasonably high resolution.
279  //
280  //
281  if ( pls->xlength > 0 && pls->ylength > 0 )
282  {
283  // xlength and length are the dimensions specified using -geometry
284  // on the command line, in device coordinates.
285  //
286  width = pls->xlength;
287  height = pls->ylength;
288  }
289  else
290  {
291  width = (PLINT) ( CANVAS_WIDTH * DEVICE_PIXELS_PER_IN );
292  height = (PLINT) ( CANVAS_HEIGHT * DEVICE_PIXELS_PER_IN );
293  }
294 
295  // If portrait mode, apply a rotation and set freeaspect
296  if ( pls->portrait )
297  {
298  plsdiori( (PLFLT) ( 4 - ORIENTATION ) );
299  pls->freeaspect = 1;
300  }
301 
302  // Setup the page size for this device. Very important for any driver!
303  gcw_set_device_size( width, height );
304 
305  // Install a canvas... unless plsc->hack is set, which is a driver-specific
306  // hack that indicates a PLESC_DEVINIT escape call will provide us with a
307  // canvas to use. This hack is used by the PlplotCanvas.
308  //
309  if ( !pls->hack )
310  {
311  dev->allow_resize = FALSE; // The size is set an should not be changed
312  gcw_install_canvas( NULL );
313  }
314  else
315  dev->allow_resize = TRUE; // Resizing allowed for canvasses
316  // provided via PLESC_DEVINIT
317 
318 
319 #ifdef DEBUG_GCW_1
320  gcw_debug( "</plD_init_gcw>\n" );
321 #endif
322 }
323 
324 
325 //--------------------------------------------------------------------------
326 // plD_polyline_gcw()
327 //
328 // Draw a polyline in the current color.
329 //--------------------------------------------------------------------------
330 
331 void plD_polyline_gcw( PLStream *pls, short *x, short *y, PLINT npts )
332 {
333  GcwPLdev * dev = pls->dev;
334  GnomeCanvasPoints * points;
335  GnomeCanvasPoints pts;
336  GnomeCanvasGroup * group;
337  GnomeCanvasItem * item;
338  GnomeCanvas * canvas;
339 
340  GdkPoint * gdkpoints;
341 
342  PLINT i;
343 
344  gdouble width;
345  guint32 color;
346 
347 #ifdef DEBUG_GCW_2
348  gcw_debug( "<plD_polyline_gcw />\n" );
349 #endif
350 
351  if ( !GNOME_IS_CANVAS( dev->canvas ) )
352  plexit( "GCW driver <plD_polyline_gcw>: Canvas not found" );
353  canvas = dev->canvas;
354 
355  if ( dev->use_persistence )
356  group = dev->group_persistent;
357  else
358  group = dev->group_hidden;
359 
360  if ( dev->use_pixmap && !dev->use_persistence ) // Write to bg pixmap
361 
362  {
363  if ( ( gdkpoints = (GdkPoint *) malloc( npts * sizeof ( GdkPoint ) ) ) == NULL )
364  {
365  plabort( "GCW driver <plD_polyline_gcw>: Could not create gdkpoints" );
366  return;
367  }
368 
369  if ( !pls->portrait )
370  {
371  for ( i = 0; i < npts; i++ )
372  {
373  gdkpoints[i].x = (gint) ( x[i] / VSCALE );
374  gdkpoints[i].y = (gint) ( dev->height - y[i] / VSCALE );
375  }
376  }
377  else // Swap x and y for portrait mode
378  {
379  for ( i = 0; i < npts; i++ )
380  {
381  gdkpoints[i].x = (gint) ( dev->height - y[i] / VSCALE );
382  gdkpoints[i].y = (gint) ( dev->width - x[i] / VSCALE );
383  }
384  }
385 
386  gdk_draw_lines( dev->background, dev->gc, gdkpoints, npts );
387 
388  dev->pixmap_has_data = TRUE;
389 
390  free( gdkpoints );
391  }
392  else // Draw Canvas lines
393 
394  {
395  // Put the data in a points structure
396  if ( ( points = gnome_canvas_points_new( npts ) ) == NULL )
397  {
398  plabort( "GCW driver <plD_polyline_gcw>: Cannot create points" );
399  return;
400  }
401  if ( !pls->portrait )
402  {
403  for ( i = 0; i < npts; i++ )
404  {
405  points->coords[2 * i] = (gdouble) ( x[i] / VSCALE );
406  points->coords[2 * i + 1] = (gdouble) ( -y[i] / VSCALE );
407  }
408  }
409  else // Swap x and y for portrait mode
410  {
411  for ( i = 0; i < npts; i++ )
412  {
413  points->coords[2 * i] = (gdouble) ( dev->height - y[i] / VSCALE );
414  points->coords[2 * i + 1] = (gdouble) ( -x[i] / VSCALE );
415  }
416  }
417 
418  // Get the pen width and color
419  width = pls->width;
420  color = dev->color;
421 
422 
423  // Workaround for the 'attempt to put segment in horiz list twice'
424  // from libgnomecanvas:
425  //
426  // Plot a series of line segments rather than a single polyline.
427  //
428  // This slows rendering down a considerable amount. However, it is
429  // unclear what else can be done. Libgnomecanvas should be able to
430  // deal with all valid data; bizarre plotting errors happen along with
431  // this error.
432  //
433  // Note that instead of allocating a series of points structures,
434  // we just refer to the original one from a separate struct
435  // (GnomeCanvas does not hold a reference to the points structure).
436  //
437 
438  pts.num_points = 2;
439  pts.ref_count = 1;
440  pts.coords = points->coords;
441 
442  for ( i = 0; i < npts - 1; i++ )
443  {
444  pts.coords = &( points->coords[2 * i] );
445 
446  if ( !GNOME_IS_CANVAS_ITEM(
447  item = gnome_canvas_item_new( group,
448  GNOME_TYPE_CANVAS_LINE,
449  "cap_style", GDK_CAP_ROUND,
450  "join-style", GDK_JOIN_ROUND,
451  "points", &pts,
452  "fill-color-rgba", color,
453  "width-units", width,
454  NULL )
455  ) )
456  {
457  plwarn( "GCW driver <plD_polyline_gcw>: Canvas item not created." );
458  }
459  }
460 
461  // Free the points structure
462  gnome_canvas_points_free( points );
463  }
464 }
465 
466 
467 //--------------------------------------------------------------------------
468 // plD_line_gcw()
469 //
470 // Draw a line in the current color from (x1,y1) to (x2,y2).
471 //--------------------------------------------------------------------------
472 
473 void plD_line_gcw( PLStream *pls, short x1, short y1, short x2, short y2 )
474 {
475  short x[2];
476  short y[2];
477 
478 #ifdef DEBUG_GCW_2
479  gcw_debug( "<plD_line_gcw />\n" );
480 #endif
481 
482  x[0] = x1;
483  x[1] = x2;
484  y[0] = y1;
485  y[1] = y2;
486 
487  plD_polyline_gcw( pls, x, y, (PLINT) 2 );
488 }
489 
490 
491 //--------------------------------------------------------------------------
492 // plD_eop_gcw()
493 //
494 // End of page.
495 //--------------------------------------------------------------------------
496 
497 void plD_eop_gcw( PLStream *pls )
498 {
499  GcwPLdev * dev = pls->dev;
500  GnomeCanvas * canvas;
501 
502  GdkPixbuf * pixbuf;
503  GnomeCanvasItem * item;
504  GnomeCanvasGroup* group;
505 
506  gdouble dx, dy;
507 
508  gint count = 1, n;
509 
510  void *save_state;
511 
512  PLINT width, height;
513 
514  if ( !GNOME_IS_CANVAS( dev->canvas ) )
515  plexit( "GCW driver <plD_eop_gcw>: Canvas not found" );
516  canvas = dev->canvas;
517 
518  // Ignore if there is no hidden group. This means BOP has not been
519  // called yet.
520  //
521  if ( !GNOME_IS_CANVAS_GROUP( dev->group_hidden ) )
522  return;
523 
524 #ifdef DEBUG_GCW_1
525  gcw_debug( "<plD_eop_gcw>\n" );
526 #endif
527 
528  if ( dev->use_persistence )
529  group = dev->group_persistent;
530  else
531  group = dev->group_hidden;
532 
533  // Retrieve the device width and height of the canvas
534  width = *(PLINT *) g_object_get_data( G_OBJECT( canvas ), "canvas-width" );
535  height = *(PLINT *) g_object_get_data( G_OBJECT( canvas ), "canvas-height" );
536 
537  if ( dev->pixmap_has_data )
538  {
539  // Render the pixmap to a pixbuf on the canvas.
540  if ( !GDK_IS_PIXBUF( pixbuf = gdk_pixbuf_get_from_drawable( NULL,
541  dev->background,
542  dev->colormap,
543  0, 0,
544  0, 0,
545  width, height ) ) )
546  {
547  plwarn( "GCW driver <plD_eop_gcw>: Can't draw pixmap into pixbuf." );
548  }
549  else // Pixbuf creation succeeded
550 
551  {
552  if ( !GNOME_IS_CANVAS_ITEM(
553  item = gnome_canvas_item_new( dev->group_hidden,
554  GNOME_TYPE_CANVAS_PIXBUF,
555  "pixbuf", pixbuf,
556  "x", 1.,
557  "y", (gdouble) ( -height + 1. ),
558  "width", (gdouble) ( width ),
559  "height", (gdouble) ( height ),
560  NULL )
561  ) )
562  {
563  plwarn( "GCW driver <plD_eop_gcw>: Canvas item not created." );
564  }
565 
566  // Free the pixbuf
567  g_object_unref( pixbuf );
568  }
569  }
570  else
571  {
572  // Use a rectangle for the background instead (faster)
573  if ( !GNOME_IS_CANVAS_ITEM(
574  item = gnome_canvas_item_new(
575  dev->group_hidden,
576  GNOME_TYPE_CANVAS_RECT,
577  "x1", 0.,
578  "y1", (gdouble) ( -height ),
579  "x2", (gdouble) ( width ),
580  "y2", 0.,
581  "fill-color-rgba", plcolor_to_rgba( pls->cmap0[0], 0xFF ),
582  "width-units", 0.,
583  NULL )
584  ) )
585  {
586  plabort( "GCW driver <pld_eop_gcw>: Canvas item not created" );
587  return;
588  }
589  }
590 
591  // Move the persistent group to the front
592  gnome_canvas_item_raise_to_top( GNOME_CANVAS_ITEM( dev->group_persistent ) );
593 
594  // Move the background to the back
595  if ( GNOME_IS_CANVAS_ITEM( item ) )
596  gnome_canvas_item_lower_to_bottom( item );
597 
598  // Make the hidden group visible
599  gnome_canvas_item_show( GNOME_CANVAS_ITEM( dev->group_hidden ) );
600 
601  // Destroy the old visible group
602  if ( GNOME_IS_CANVAS_GROUP( dev->group_visible ) )
603  {
604  gtk_object_destroy( (GtkObject *) ( dev->group_visible ) );
605  dev->group_visible = NULL;
606  }
607 
608  // Clear the background pixmap
609  if ( !dev->use_persistence && dev->pixmap_has_data )
611 
612  // Name the hidden group as visible
613  dev->group_visible = dev->group_hidden;
614  dev->group_hidden = NULL;
615 
616  // Update the canvas
617  canvas->need_update = 1;
618  gnome_canvas_update_now( canvas );
619 
620  //
621  // Copy the plot buffer for future reference, otherwise it is
622  // thrown out.
623  //
624 
625  save_state = g_object_get_data( G_OBJECT( canvas ), "plotbuf" );
626  save_state = (void *) plbuf_save( pls, save_state );
627 
628  // Attach the saved state to the canvas
629  g_object_set_data( G_OBJECT( canvas ), "plotbuf", (gpointer) save_state );
630 
631  // If the driver is creating its own canvasses, set dev->canvas to be
632  // NULL now in order to force creation of a new canvas when the next
633  // drawing call is made. The new canvas will be placed in a new
634  // notebook page.
635  //
636  if ( dev->window != NULL )
637  {
638  dev->canvas = NULL;
639  dev->group_visible = NULL;
640  dev->group_hidden = NULL;
641  dev->group_persistent = NULL;
642  }
643 
644 #ifdef DEBUG_GCW_1
645  gcw_debug( "</plD_eop_gcw>\n" );
646 #endif
647 }
648 
649 
650 //--------------------------------------------------------------------------
651 // plD_bop_gcw()
652 //
653 // Set up for the next page.
654 //
655 //--------------------------------------------------------------------------
656 
657 void plD_bop_gcw( PLStream *pls )
658 {
659  GcwPLdev * dev = pls->dev;
660  GnomeCanvas* canvas;
661 
662  if ( !GNOME_IS_CANVAS( dev->canvas ) )
663  {
664  if ( pls->hack )
665  return; // Wait for a canvas via DEVINIT
666  else
667  gcw_install_canvas( NULL );
668  }
669  canvas = dev->canvas;
670 
671 #ifdef DEBUG_GCW_1
672  gcw_debug( "<plD_bop_gcw>\n" );
673 #endif
674 
675  // Replay escape calls that come in before PLESC_DEVINIT. Some of them
676  // required a Canvas that didn't exist yet.
677  //
678  if ( dev->plstate_width )
680  if ( dev->plstate_color0 )
682  if ( dev->plstate_color1 )
684  dev->plstate_width = FALSE;
685  dev->plstate_color0 = FALSE;
686  dev->plstate_color1 = FALSE;
687 
688  // Creat a new hidden group; all new drawing will be to this group
689  if ( !GNOME_IS_CANVAS_ITEM(
690  dev->group_hidden = GNOME_CANVAS_GROUP( gnome_canvas_item_new(
691  gnome_canvas_root( canvas ),
692  gnome_canvas_clipgroup_get_type(),
693  "x", 0.,
694  "y", 0.,
695  NULL ) )
696  ) )
697  {
698  plexit( "GCW driver <plD_bop_gcw>: Canvas group cannot be created" );
699  }
700 
701  // Set the clip to NULL
702  g_object_set( G_OBJECT( dev->group_hidden ), "path", NULL, NULL );
703 
704  // Hide this group until drawing is done
705  gnome_canvas_item_hide( GNOME_CANVAS_ITEM( dev->group_hidden ) );
706 
707 #ifdef DEBUG_GCW_1
708  gcw_debug( "</plD_bop_gcw>\n" );
709 #endif
710 }
711 
712 
713 //--------------------------------------------------------------------------
714 // plD_tidy_gcw()
715 //
716 // Close graphics file
717 //--------------------------------------------------------------------------
718 
719 void plD_tidy_gcw( PLStream *pls )
720 {
721  GcwPLdev* dev = pls->dev;
722 
723 #ifdef DEBUG_GCW_1
724  gcw_debug( "<plD_tidy_gcw>\n" );
725 #endif
726 
727 #ifdef PL_HAVE_FREETYPE
728  if ( pls->dev_text )
729  {
730  FT_Data *FT = (FT_Data *) pls->FT;
731  plscmap0n( FT->ncol0_org );
732  plD_FreeType_Destroy( pls );
733  }
734 #endif
735 
736  if ( dev->window != NULL )
737  {
738  gtk_main();
739  }
740 
741 #ifdef DEBUG_GCW_1
742  gcw_debug( "</plD_tidy_gcw>\n" );
743 #endif
744 }
745 
746 
747 //--------------------------------------------------------------------------
748 // plD_state_gcw()
749 //
750 // Handle change in PLStream state (color, pen width, fill attribute, etc).
751 //
752 // Note that PLplot sometimes tries to change states before the device is
753 // fully initialized (i.e., via PLESC_DEVINIT). We must keep track of
754 // such attempts, and invoke the state change during the next call to
755 // plD_bop_gcw.
756 //
757 //--------------------------------------------------------------------------
758 
759 void plD_state_gcw( PLStream *pls, PLINT op )
760 {
761  GcwPLdev* dev = pls->dev;
762  char opname[20], msg[100];
763 
764 #ifdef DEBUG_GCW_1
765  if ( op == PLSTATE_WIDTH )
766  strcpy( opname, "PLSTATE_WIDTH" );
767  else if ( op == PLSTATE_COLOR0 )
768  strcpy( opname, "PLSTATE_COLOR0" );
769  else if ( op == PLSTATE_COLOR1 )
770  strcpy( opname, "PLSTATE_COLOR1" );
771  else if ( op == PLSTATE_FILL )
772  strcpy( opname, "PLSTATE_FILL" );
773  else if ( op == PLSTATE_CMAP0 )
774  strcpy( opname, "PLSTATE_CMAP0" );
775  else if ( op == PLSTATE_CMAP1 )
776  strcpy( opname, "PLSTATE_CMAP1" );
777  else
778  strcpy( opname, "unknown" );
779  snprintf( msg, 100, "<plD_state_gcw />: %s\n", opname );
780  gcw_debug( msg );
781 #endif
782 
783  switch ( op )
784  {
785  case PLSTATE_WIDTH:
786  if ( GNOME_IS_CANVAS( dev->canvas ) )
787  {
788  if ( dev->use_pixmap )
789  {
790  gdk_gc_set_line_attributes( dev->gc, pls->width,
791  GDK_LINE_SOLID,
792  GDK_CAP_BUTT,
793  GDK_JOIN_MITER );
794  }
795  }
796  else
797  dev->plstate_width = TRUE;
798  break;
799 
800  case PLSTATE_COLOR0:
801  if ( GNOME_IS_CANVAS( dev->canvas ) )
802  {
803  dev->color = plcolor_to_rgba( pls->cmap0[pls->icol0], 0xFF );
804  if ( dev->use_pixmap )
806  }
807  else
808  dev->plstate_color0 = TRUE;
809  break;
810 
811  case PLSTATE_COLOR1:
812  if ( GNOME_IS_CANVAS( dev->canvas ) )
813  {
814  dev->color = plcolor_to_rgba( pls->cmap1[pls->icol1], 0xFF );
815  if ( dev->use_pixmap )
817  }
818  else
819  dev->plstate_color1 = TRUE;
820  break;
821 
822  case PLSTATE_FILL:
823  break;
824 
825  case PLSTATE_CMAP0:
826  break;
827 
828  case PLSTATE_CMAP1:
829  break;
830 
831  default:
832  break;
833  }
834 }
835 
836 
837 //--------------------------------------------------------------------------
838 // fill_polygon()
839 //
840 // Fills the polygon defined by the given points. Used for shade
841 // plotting. Only solid fills are allowed.
842 //--------------------------------------------------------------------------
843 
844 static void fill_polygon( PLStream* pls )
845 {
846  GnomeCanvasPoints* points;
847  GnomeCanvasGroup * group;
848  GnomeCanvasItem * item;
849  GcwPLdev * dev = pls->dev;
850  GnomeCanvas * canvas;
851 
852  PLINT i;
853 
854  GdkPoint * gdkpoints;
855 
856  PLINT tmp;
857 
858 #ifdef DEBUG_GCW_2
859  gcw_debug( "<fill_polygon />\n" );
860 #endif
861 
862  if ( !GNOME_IS_CANVAS( dev->canvas ) )
863  plexit( "GCW driver <fill_polygon>: Canvas not found" );
864  canvas = dev->canvas;
865 
866  if ( dev->use_persistence )
867  group = dev->group_persistent;
868  else
869  group = dev->group_hidden;
870 
871  if ( dev->use_pixmap && !dev->use_persistence ) // Write to a pixmap
872 
873  {
874  if ( ( gdkpoints = (GdkPoint *) malloc( pls->dev_npts * sizeof ( GdkPoint ) ) ) == NULL )
875  {
876  plabort( "GCW driver <fill_polygon>: Could not create gdkpoints" );
877  return;
878  }
879 
880  if ( !pls->portrait )
881  {
882  for ( i = 0; i < pls->dev_npts; i++ )
883  {
884  gdkpoints[i].x = (gint) ( pls->dev_x[i] / VSCALE );
885  gdkpoints[i].y = (gint) ( dev->height - pls->dev_y[i] / VSCALE );
886  }
887  }
888  else // Swap x and y for portrait mode
889  {
890  for ( i = 0; i < pls->dev_npts; i++ )
891  {
892  gdkpoints[i].x = (gint) ( dev->height - pls->dev_y[i] / VSCALE );
893  gdkpoints[i].y = (gint) ( dev->width - pls->dev_x[i] / VSCALE );
894  }
895  }
896 
897  gdk_draw_polygon( dev->background, dev->gc, TRUE, gdkpoints, pls->dev_npts );
898 
899  dev->pixmap_has_data = TRUE;
900 
901  free( gdkpoints );
902  }
903  else // Use Gnome Canvas polygons
904 
905  {
906  if ( ( points = gnome_canvas_points_new( pls->dev_npts ) ) == NULL )
907  {
908  plabort( "GCW driver <fill_polygon>: Could not create points" );
909  return;
910  }
911 
912  if ( !pls->portrait )
913  {
914  for ( i = 0; i < pls->dev_npts; i++ )
915  {
916  points->coords[2 * i] = (gdouble) ( pls->dev_x[i] / VSCALE );
917  points->coords[2 * i + 1] = (gdouble) ( -pls->dev_y[i] / VSCALE );
918  }
919  }
920  else // Swap x and y for portrait mode
921  {
922  for ( i = 0; i < pls->dev_npts; i++ )
923  {
924  points->coords[2 * i] = (gdouble) ( dev->height - pls->dev_y[i] / VSCALE );
925  points->coords[2 * i + 1] = (gdouble) ( -pls->dev_x[i] / VSCALE );
926  }
927  }
928 
929  if ( !GNOME_IS_CANVAS_ITEM(
930  item = gnome_canvas_item_new( group,
931  GNOME_TYPE_CANVAS_POLYGON,
932  "points", points,
933  "fill-color-rgba", dev->color,
934  // "outline-color-rgba",dev->color,
935  NULL )
936  ) )
937  {
938  plwarn( "GCW driver <fill_polygon>: Canvas item not created." );
939  }
940 
941  gnome_canvas_points_free( points );
942 
943 
944  // Draw a thin outline for each polygon; note that doing this
945  // using the "outline-color-rgba" property above can result in
946  // Canvas errors.
947  //
948  tmp = pls->width;
949  pls->width = 1;
950  plD_polyline_gcw( pls, pls->dev_x, pls->dev_y, pls->dev_npts );
951  pls->width = tmp;
952  }
953 }
954 
955 #ifdef PL_HAVE_FREETYPE
956 //--------------------------------------------------------------------------
957 // proc_str()
958 //
959 // Handles call to draw text on the canvas when the HAS_TEXT escape funtion
960 // case is invoked.
961 //
962 // This routine is unicode enabled, and requires freetype.
963 //--------------------------------------------------------------------------
964 
965 static void proc_str( PLStream *pls, EscText *args )
966 {
967  PLFLT *t = args->xform; // Transform matrix for string
968 
969  GnomeCanvasGroup* group;
970  GcwPLdev * dev = pls->dev;
971  GnomeCanvas * canvas;
972 
973  PLUNICODE fci; // The unicode font characterization integer
974  guchar *fontname = NULL;
975  gint font_size;
976  GnomeFont *font;
977  GnomeFontFace *face;
978  GnomeGlyphList *glyphlist;
979  guint Nglyphs;
980 
981  gdouble affine_baseline[6] = { 0., 0., 0., 0., 0., 0. }; // Affine transforms
982  gdouble affine_translate[6] = { 0., 0., 0., 0., 0., 0. };
983  gdouble affine_rotate[6] = { 0., 0., 0., 0., 0., 0. };
984  gdouble affine_plplot[6] = { 0., 0., 0., 0., 0., 0. };
985 
986  GnomeCanvasItem * item[200]; // List of string segments
987  gdouble width[200], height[200]; // Height and width of string segment
988  gdouble up_list[200]; // Indicates sub/sup position of string segment
989  gdouble up = 0, scale = 1; // Used to create superscripts and subscripts
990 
991  ArtDRect bbox; // Bounding box for each segment to get width & height
992 
993  const PLUNICODE *text; // The text and pointers to it
994  guint i = 0, Ntext; // The text index and maximum length
995 
996  char esc; // The escape character
997 
998  guint N = 0; // The number of text segments
999  gdouble total_width = 0, sum_width = 0;
1000 
1001  guint symbol;
1002 
1003 
1004 #ifdef DEBUG_GCW_2
1005  gcw_debug( "<proc_str>\n" );
1006 #endif
1007 
1008  if ( !GNOME_IS_CANVAS( dev->canvas ) )
1009  plexit( "GCW driver <proc_str>: Canvas not found" );
1010  canvas = dev->canvas;
1011 
1012  if ( dev->use_persistence )
1013  group = dev->group_persistent;
1014  else
1015  group = dev->group_hidden;
1016 
1017  // Retrieve the escape character
1018  plgesc( &esc );
1019 
1020  // Put the transform matrix values in the order expected by libart.
1021  // Note that the plplot transform matrix only has a rotation and shear;
1022  // plplot's rotation direction and shear are opposite from that expected
1023  // by libart, hence the negative signs below.
1024  //
1025  affine_plplot[0] = t[0]; // cos(theta)
1026  affine_plplot[1] = -t[2]; // sin(theta)
1027  affine_plplot[2] = -t[1]; // a cos(theta) - sin(theta)
1028  affine_plplot[3] = t[3]; // a sin(theta) + cos(theta)
1029 
1030  // Font size: size is in pixels but chrht is in mm. Why the extra factor?
1031  font_size = (gint) ( pls->chrht * DEVICE_PIXELS_PER_MM * 1.5 );
1032 
1033  // Determine the default font
1034  plgfci( &fci );
1035  fontname = plP_FCI2FontName( fci, FontLookup, N_TrueTypeLookup );
1036  if ( fontname == NULL )
1037  {
1038  plabort( "GCW driver <proc_str>: FCI inconsistent with TrueTypeLookup" );
1039  return;
1040  }
1041 
1042  // Retrieve the font face
1043  face = gnome_font_face_find_from_filename( fontname, 0 );
1044 
1045  // Get the unicode string
1046  text = args->unicode_array;
1047  Ntext = (guint) ( args->unicode_array_len );
1048 
1049  // Process the string: Break it into segments of constant font and size,
1050  // making sure we process control characters as we come to them. Save
1051  // the extra information that will allow us to place the text on the
1052  // canvas.
1053  //
1054  while ( i < Ntext )
1055  {
1056  // Process the next character
1057 
1058  if ( text[i] & PL_FCI_MARK ) // Is it a font characterization index?
1059 
1060  { // Determine the font name
1061  fontname = plP_FCI2FontName( text[i], FontLookup, N_TrueTypeLookup );
1062  if ( fontname == NULL )
1063  {
1064  plabort( "GCW driver <proc_str>: FCI inconsistent with "
1065  "TrueTypeLookup" );
1066  return;
1067  }
1068 
1069  // Retrieve the font face
1070  gnome_font_unref( face ); // We already have a face
1071  face = gnome_font_face_find_from_filename( fontname, 0 );
1072 
1073  i++; // Move ahead to the next character
1074  }
1075  else
1076  {
1077  if ( text[i] == esc ) // Check for escape sequences
1078 
1079  { // Process escape sequence
1080  i++; // Move on to next character
1081  if ( i >= Ntext )
1082  {
1083  plwarn( "GCW driver <proc_str>: Invalid escape sequence "
1084  "provided in text." );
1085  return;
1086  }
1087 
1088  switch ( text[i] )
1089  {
1090  case '#': // <esc><esc>; this should translate to a hash
1091  break; // Watch out for it later
1092 
1093  // Move to lower sub/sup position
1094  case 'd':
1095  case 'D':
1096  if ( up > 0. )
1097  scale *= 1.25; // Subscript scaling parameter
1098  else
1099  scale *= 0.8; // Subscript scaling parameter
1100  up -= font_size / 2.;
1101  break;
1102 
1103  // Move to higher sub/sup position
1104  case 'u':
1105  case 'U':
1106  if ( up < 0. )
1107  scale *= 1.25; // Subscript scaling parameter
1108  else
1109  scale *= 0.8; // Subscript scaling parameter
1110  up += font_size / 2.;
1111  break;
1112 
1113  // Ignore the next sequences
1114 
1115  // Overline
1116  case '+':
1117 
1118  // Underline
1119  case '-':
1120 
1121  // Backspace
1122  case 'b':
1123  case 'B':
1124  plwarn( "GCW driver <proc_str>: '+', '-', and 'b' text "
1125  "escape sequences not processed." );
1126  break;
1127  } // switch(text[i])
1128 
1129  if ( text[i] != '#' )
1130  i++; // Move ahead to the next character
1131  } // if(text[i] == esc)
1132  } // if(text[i] & PL_FCI_MARK)
1133 
1134 
1135  if ( i == Ntext )
1136  continue; // End of string
1137 
1138  // Save the sub/sup position
1139  up_list[N] = up;
1140 
1141  // Get the font
1142  font = gnome_font_face_get_font_default( face, font_size * scale );
1143  // printf("\n\nfont name = %s\n\n",gnome_font_get_name(font));
1144 
1145  // Create the glyphlist for this text segment
1146  glyphlist = gnome_glyphlist_new();
1147  gnome_glyphlist_font( glyphlist, font );
1148  gnome_glyphlist_color( glyphlist, dev->color );
1149  gnome_glyphlist_advance( glyphlist, TRUE );
1150  gnome_glyphlist_kerning( glyphlist, 0. );
1151  gnome_glyphlist_letterspace( glyphlist, 0. );
1152 
1153  // Free the font
1154  gnome_font_unref( font );
1155 
1156  // Move along to the next escape or FCI character, stuffing
1157  // everything else into the glyphlist.
1158  //
1159  Nglyphs = 0;
1160  while ( i < Ntext && !( text[i] & PL_FCI_MARK ) )
1161  {
1162  // Differentiate between ## and escape sequences
1163  if ( text[i] == esc )
1164  {
1165  if ( !( i > 0 && text[i - 1] == esc ) )
1166  break;
1167  }
1168 
1169  gnome_glyphlist_glyph( glyphlist,
1170  gnome_font_lookup_default( font, text[i] ) );
1171  i++; Nglyphs++;
1172  }
1173 
1174  if ( Nglyphs )
1175  {
1176  // Determine the bounding box of the text
1177  gnome_glyphlist_bbox( glyphlist, NULL, 0, &bbox );
1178  width[N] = bbox.x1 - bbox.x0;
1179  height[N] = bbox.y1 - bbox.y0;
1180 
1181  // Keep track of the total string width so that we can justify it
1182  total_width += width[N];
1183  if ( N != 0 )
1184  total_width += 2; // Add a little extra space
1185 
1186  // Create the canvas text item
1187  if ( !GNOME_IS_CANVAS_ITEM(
1188  item[N] = gnome_canvas_item_new( group,
1190  "glyphlist", glyphlist,
1191  "fill-color-rgba", dev->color,
1192  "x", 0.,
1193  "y", 0.,
1194  NULL )
1195  ) )
1196  {
1197  plabort( "GCW driver <proc_str>: Canvas item not created" );
1198  return;
1199  }
1200 
1201  // Free the glyphlist
1202  gnome_glyphlist_unref( glyphlist );
1203 
1204  // Advance to next string segment
1205  N++;
1206  } // if(Nglyphs)
1207 
1208 
1209  // Don't overflow buffer
1210  if ( N == 200 && i < Ntext )
1211  {
1212  plabort( "GCW driver <proc_str>: too many text segments" );
1213  return;
1214  }
1215  } // while(i<Ntext)
1216 
1217  // We have all of the string segments. Place each on the canvas
1218  // appropriately.
1219  //
1220  for ( i = 0; i < N; i++ )
1221  {
1222  // Calculate and apply the affine transforms
1223  art_affine_rotate( affine_rotate, 90. * ( pls->diorot - pls->portrait ) );
1224  if ( !pls->portrait )
1225  {
1226  art_affine_translate( affine_baseline,
1227  -total_width * args->just + sum_width,
1228  height[0] / 2.5 - up_list[i] );
1229  art_affine_translate( affine_translate,
1230  args->x / VSCALE, -args->y / VSCALE );
1231  }
1232  else // Swap x and y for portrait mode
1233  {
1234  art_affine_translate( affine_baseline,
1235  -total_width * args->just + sum_width,
1236  height[0] / 2.5 - up_list[i] );
1237  art_affine_translate( affine_translate,
1238  dev->height - args->y / VSCALE, -args->x / VSCALE );
1239  }
1240  gnome_canvas_item_affine_relative( item[i], affine_translate );
1241  gnome_canvas_item_affine_relative( item[i], affine_rotate );
1242  gnome_canvas_item_affine_relative( item[i], affine_plplot );
1243  gnome_canvas_item_affine_relative( item[i], affine_baseline );
1244 
1245  // Keep track of the position in the string
1246  sum_width += width[i];
1247  if ( i != N - 1 )
1248  sum_width += 2; // Add a little extra space
1249  }
1250 
1251 #ifdef DEBUG_GCW_2
1252  gcw_debug( "</proc_str>\n" );
1253 #endif
1254 }
1255 #endif //PL_HAVE_FREETYPE
1256 
1257 
1258 //--------------------------------------------------------------------------
1259 // plD_esc_gcw()
1260 //
1261 // Escape functions.
1262 //
1263 //--------------------------------------------------------------------------
1264 
1265 void plD_esc_gcw( PLStream *pls, PLINT op, void *ptr )
1266 {
1267  GcwPLdev* dev = pls->dev;
1268 
1269 #ifdef DEBUG_GCW_1
1270  char opname[20], msg[100];
1271  if ( op == PLESC_DEVINIT )
1272  strcpy( opname, "PLESC_DEVINIT" );
1273  else if ( op == PLESC_CLEAR )
1274  strcpy( opname, "PLESC_CLEAR" );
1275  else if ( op == PLESC_FILL )
1276  strcpy( opname, "PLESC_FILL" );
1277  else if ( op == PLESC_HAS_TEXT )
1278  strcpy( opname, "PLESC_HAS_TEXT" );
1279  else if ( op == PLESC_GRAPH )
1280  strcpy( opname, "PLESC_GRAPH" );
1281  else
1282  strcpy( opname, "unknown" );
1283  snprintf( msg, 100, "<plD_esc_gcw />: %s\n", opname );
1284  gcw_debug( msg );
1285 #endif
1286 
1287  switch ( op )
1288  {
1289  case PLESC_DEVINIT:
1290  gcw_init_canvas( GNOME_CANVAS( ptr ) );
1291  pls->hack = 0;
1292  break;
1293 
1294  case PLESC_CLEAR:
1295  break;
1296 
1297  case PLESC_FILL:
1298  fill_polygon( pls );
1299  break;
1300 
1301 #ifdef PL_HAVE_FREETYPE
1302  case PLESC_HAS_TEXT:
1303  proc_str( pls, ptr ); // Draw the text
1304  break;
1305 #endif
1306 
1307  case PLESC_GRAPH:
1308  break;
1309 
1310  default:
1311  break;
1312  }
1313 }