PLplot: gcw.c Source File

PLplot 5.10.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
142 void plD_dispatch_init_gcw( PLDispatchTable *pdt )
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
152 pdt->pl_type = plDevType_Interactive;
153 pdt->pl_seq = 1;
154 pdt->pl_init = (plD_init_fp) plD_init_gcw;
155 pdt->pl_line = (plD_line_fp) plD_line_gcw;
156 pdt->pl_polyline = (plD_polyline_fp) plD_polyline_gcw;
157 pdt->pl_eop = (plD_eop_fp) plD_eop_gcw;
158 pdt->pl_bop = (plD_bop_fp) plD_bop_gcw;
159 pdt->pl_tidy = (plD_tidy_fp) plD_tidy_gcw;
160 pdt->pl_state = (plD_state_fp) plD_state_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 )
610 gcw_clear_background();
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 )
679 plD_state_gcw( pls, PLSTATE_WIDTH );
680 if ( dev->plstate_color0 )
681 plD_state_gcw( pls, PLSTATE_COLOR0 );
682 if ( dev->plstate_color1 )
683 plD_state_gcw( pls, 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 )
805 gcw_set_gdk_color();
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 )
816 gcw_set_gdk_color();
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,
1189 PLPLOT_TYPE_CANVAS_HACKTEXT,
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 }