PLplot
5.10.0
|
00001 // gcw-driver - PLplot Gnome Canvas Widget device driver. 00002 // 00003 // Copyright (C) 2004, 2005 Thomas J. Duck 00004 // Copyright (C) 2004 Rafael Laboissiere 00005 // All rights reserved. 00006 // 00007 // 00008 // NOTICE 00009 // 00010 // This library is free software; you can redistribute it and/or 00011 // modify it under the terms of the GNU Lesser General Public 00012 // License as published by the Free Software Foundation; either 00013 // version 2.1 of the License, or (at your option) any later version. 00014 // 00015 // This library is distributed in the hope that it will be useful, 00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 // Lesser General Public License for more details. 00019 // 00020 // You should have received a copy of the GNU Lesser General Public 00021 // License along with this library; if not, write to the Free Software 00022 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 00023 // USA 00024 // 00025 // 00026 // DESCRIPTION 00027 // 00028 // This is the Gnome Canvas Widget driver, written by Thomas J. Duck 00029 // following the heritage of the PLplot Gnome driver by Rafael Laboissiere. 00030 // Like all PLplot drivers, this operates in standalone mode by default. 00031 // However, this driver can also be used to write to a user-supplied 00032 // GnomeCanvas. 00033 // 00034 // Please see the PLplot documentation for more information. 00035 // 00036 // 00037 // DEVELOPMENT NOTES 00038 // 00039 // Truetype text is supplied using the PLPLOT_CANVAS_HACKTEXT item, 00040 // which was cloned from gnome-print. This text item was chosen because 00041 // it rotates and scales under a zoom correctly and easily. 00042 // 00043 // It would be better to use GNOME_CANVAS_TEXT, but currently 00044 // (4 March 2005) it doesn't rotate or scale under a zoom on the 00045 // GnomeCanvas. GNOME_CANVAS_TEXT uses Pango, and rotations were only 00046 // recently implemented in the Pango API (i.e., Fall 2004). If the 00047 // Pango API is used directly, the bounding box doesn't rotate with the 00048 // text on GnomeCanvas, which results in clipping. It is likely that 00049 // GnomeCanvas is not querying the bounding box from Pango correctly, 00050 // and is not directing Pango to scale. So, GnomeCanvas needs to be 00051 // updated to deal with Pango properly. 00052 // 00053 // Another problem is that drawing polylines on the Gnome Canvas sometimes 00054 // results in an 'attempt to put segment in horiz list twice' error. 00055 // The workaround here is to plot single line segments only, but this 00056 // results in a performance hit. This problem will need to be corrected 00057 // in the GnomeCanvas. 00058 // 00059 // 00060 // KNOWN BUGS 00061 // 00062 // PLplot test suite problems: 00063 // 00064 // 1) Example x10c does not clip the text (there is no text clipping). 00065 // 00066 // 2) Example x17c, the strip chart demo, doesn't do a strip chart 00067 // (try the xwin driver to see how it should work). Strip charts 00068 // are fundamentally incompatible with the tabbed window design of 00069 // the GCW driver. Use the PlplotCanvas to create animations 00070 // instead. 00071 // 00072 00073 #include <sys/stat.h> 00074 00075 #include "gcw.h" 00076 #include "plplotcanvas-hacktext.h" 00077 00078 #ifdef PL_HAVE_FREETYPE 00079 00080 #include "plfreetype.h" 00081 #include "plfci-truetype.h" 00082 00083 // Font lookup table that is constructed in plD_FreeType_init 00084 extern FCI_to_FontName_Table FontLookup[N_TrueTypeLookup]; 00085 00086 #endif // PL_HAVE_FREETYPE 00087 00088 00089 // Device info 00090 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_gcw = "gcw:Gnome Canvas Widget:1:gcw:10:gcw\n"; 00091 00092 // Global driver options 00093 00094 #ifdef PL_HAVE_FREETYPE 00095 static PLINT text = 1; 00096 #else 00097 static PLINT text = 0; 00098 #endif 00099 00100 static PLINT hrshsym = 0; 00101 static PLINT replot = 1; 00102 00103 static DrvOpt gcw_options[] = 00104 { 00105 { "text", DRV_INT, &text, "Use truetype fonts (text=0|1)" }, 00106 { "hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)" }, 00107 { "replot", DRV_INT, &replot, "Allow replotting to other devices (replot=0|1)" }, 00108 { NULL, DRV_INT, NULL, NULL } 00109 }; 00110 00111 00112 //******************** 00113 // Utility functions * 00114 //******************* 00115 00116 guint32 plcolor_to_rgba( PLColor color, guchar alpha ) 00117 { 00118 return 00119 ( (int) ( color.r ) << 24 ) 00120 + ( (int) ( color.g ) << 16 ) 00121 + ( (int) ( color.b ) << 8 ) 00122 + alpha; 00123 } 00124 00125 00126 //-------------------------------------------------------------------------- 00127 // plD_dispatch_init_gcw() 00128 // 00129 // Initializes the dispatch table. 00130 //-------------------------------------------------------------------------- 00131 00132 void plD_open_gcw( PLStream *pls ); 00133 void plD_init_gcw( PLStream * ); 00134 void plD_line_gcw( PLStream *, short, short, short, short ); 00135 void plD_polyline_gcw( PLStream *, short *, short *, PLINT ); 00136 void plD_eop_gcw( PLStream * ); 00137 void plD_bop_gcw( PLStream * ); 00138 void plD_tidy_gcw( PLStream * ); 00139 void plD_state_gcw( PLStream *, PLINT ); 00140 void plD_esc_gcw( PLStream *, PLINT, void * ); 00141 00142 void plD_dispatch_init_gcw( PLDispatchTable *pdt ) 00143 { 00144 #ifdef DEBUG_GCW_1 00145 gcw_debug( "<plD_dispatch_init_gcw>\n" ); 00146 #endif 00147 00148 #ifndef ENABLE_DYNDRIVERS 00149 pdt->pl_MenuStr = "Gnome Canvas Widget"; 00150 pdt->pl_DevName = "gcw"; 00151 #endif 00152 pdt->pl_type = plDevType_Interactive; 00153 pdt->pl_seq = 1; 00154 pdt->pl_init = (plD_init_fp) plD_init_gcw; 00155 pdt->pl_line = (plD_line_fp) plD_line_gcw; 00156 pdt->pl_polyline = (plD_polyline_fp) plD_polyline_gcw; 00157 pdt->pl_eop = (plD_eop_fp) plD_eop_gcw; 00158 pdt->pl_bop = (plD_bop_fp) plD_bop_gcw; 00159 pdt->pl_tidy = (plD_tidy_fp) plD_tidy_gcw; 00160 pdt->pl_state = (plD_state_fp) plD_state_gcw; 00161 pdt->pl_esc = (plD_esc_fp) plD_esc_gcw; 00162 00163 #ifdef DEBUG_GCW_1 00164 gcw_debug( "</plD_dispatch_init_gcw>\n" ); 00165 #endif 00166 } 00167 00168 00169 //-------------------------------------------------------------------------- 00170 // plD_init_gcw() 00171 // 00172 // Initializes the device. 00173 // 00174 // This routine is invoked by a call to plinit. 00175 // 00176 //-------------------------------------------------------------------------- 00177 00178 void plD_init_gcw( PLStream *pls ) 00179 { 00180 GcwPLdev* dev; 00181 00182 PLINT width, height, tmp; 00183 00184 PLColor bgcolor = pls->cmap0[0]; 00185 00186 #ifdef DEBUG_GCW_1 00187 gcw_debug( "<plD_init_gcw>\n" ); 00188 #endif 00189 00190 // Parse the driver options 00191 plParseDrvOpts( gcw_options ); 00192 00193 // Set up the stream 00194 pls->termin = 1; // Is an interactive terminal 00195 pls->dev_flush = 1; // Handle our own flushes 00196 pls->plbuf_write = replot; // Use plot buffer to replot to another device 00197 pls->width = 1; 00198 pls->dev_clear = 0; // Handle plclear() 00199 pls->dev_fill0 = 1; // Handle solid fills 00200 00201 // Create the device 00202 if ( ( dev = g_malloc( sizeof ( GcwPLdev ) ) ) == NULL ) 00203 plexit( "GCW driver <plD_init_gcw>: Cannot create device" ); 00204 pls->dev = dev; 00205 00206 // Set text handling 00207 #ifdef PL_HAVE_FREETYPE 00208 if ( text ) 00209 { 00210 pls->dev_text = TRUE; 00211 pls->dev_unicode = TRUE; 00212 if ( hrshsym ) 00213 pls->dev_hrshsym = 1; 00214 00215 // Initialize freetype 00216 plD_FreeType_init( pls ); 00217 } 00218 else 00219 { 00220 pls->dev_text = FALSE; 00221 pls->dev_unicode = FALSE; 00222 } 00223 #else 00224 pls->dev_text = FALSE; 00225 pls->dev_unicode = FALSE; 00226 #endif 00227 00228 // Set up pixmap support 00229 dev->use_pixmap = (gboolean) ( !pls->nopixmap ); 00230 dev->pixmap_has_data = FALSE; 00231 00232 // Initialize the device colors 00233 dev->color = plcolor_to_rgba( pls->cmap0[pls->icol0], 0xFF ); 00234 dev->bgcolor.red = (guint16) ( bgcolor.r / 255. * 65535 ); 00235 dev->bgcolor.green = (guint16) ( bgcolor.b / 255. * 65535 ); 00236 dev->bgcolor.blue = (guint16) ( bgcolor.g / 255. * 65535 ); 00237 00238 // Set the device canvas and window pointers 00239 dev->canvas = NULL; 00240 dev->background = NULL; 00241 dev->gc = NULL; 00242 dev->colormap = NULL; 00243 dev->window = NULL; 00244 dev->notebook = NULL; 00245 dev->statusbar = NULL; 00246 dev->filew = NULL; 00247 00248 // Initialize the Canvas groups. All of the plplot plotting 00249 // commands are drawn to the hidden group. When the page is finalized, 00250 // the group is made visible, and the old group destroyed. The persistent 00251 // group is never erased, and always plotted at the very front. 00252 // 00253 dev->group_visible = NULL; 00254 dev->group_hidden = NULL; 00255 dev->group_persistent = NULL; 00256 00257 // Assume that pladv should completeley refresh the page 00258 dev->use_persistence = FALSE; 00259 00260 // Set the initialization state monitors to FALSE 00261 dev->plstate_width = FALSE; 00262 dev->plstate_color0 = FALSE; 00263 dev->plstate_color1 = FALSE; 00264 00265 // Initialize gtk 00266 gtk_init( 0, NULL ); 00267 00268 // Set up the physical device in the next series of commands. It is very 00269 // important to do this properly, because many PLplot routines depend on 00270 // physical coordinates (e.g., dashed lines, hatched areas, the 00271 // replot mechanism, hidden line removal, etc. 00272 // 00273 // Note that coordinates in the driver are measured in device units, 00274 // which correspond to the pixel size on a typical screen. The coordinates 00275 // reported and received from the PLplot core, however, are in virtual 00276 // coordinates, which is just a scaled version of the device coordinates. 00277 // This strategy is used so that the calculations in the PLplot 00278 // core are performed at reasonably high resolution. 00279 // 00280 // 00281 if ( pls->xlength > 0 && pls->ylength > 0 ) 00282 { 00283 // xlength and length are the dimensions specified using -geometry 00284 // on the command line, in device coordinates. 00285 // 00286 width = pls->xlength; 00287 height = pls->ylength; 00288 } 00289 else 00290 { 00291 width = (PLINT) ( CANVAS_WIDTH * DEVICE_PIXELS_PER_IN ); 00292 height = (PLINT) ( CANVAS_HEIGHT * DEVICE_PIXELS_PER_IN ); 00293 } 00294 00295 // If portrait mode, apply a rotation and set freeaspect 00296 if ( pls->portrait ) 00297 { 00298 plsdiori( (PLFLT) ( 4 - ORIENTATION ) ); 00299 pls->freeaspect = 1; 00300 } 00301 00302 // Setup the page size for this device. Very important for any driver! 00303 gcw_set_device_size( width, height ); 00304 00305 // Install a canvas... unless plsc->hack is set, which is a driver-specific 00306 // hack that indicates a PLESC_DEVINIT escape call will provide us with a 00307 // canvas to use. This hack is used by the PlplotCanvas. 00308 // 00309 if ( !pls->hack ) 00310 { 00311 dev->allow_resize = FALSE; // The size is set an should not be changed 00312 gcw_install_canvas( NULL ); 00313 } 00314 else 00315 dev->allow_resize = TRUE; // Resizing allowed for canvasses 00316 // provided via PLESC_DEVINIT 00317 00318 00319 #ifdef DEBUG_GCW_1 00320 gcw_debug( "</plD_init_gcw>\n" ); 00321 #endif 00322 } 00323 00324 00325 //-------------------------------------------------------------------------- 00326 // plD_polyline_gcw() 00327 // 00328 // Draw a polyline in the current color. 00329 //-------------------------------------------------------------------------- 00330 00331 void plD_polyline_gcw( PLStream *pls, short *x, short *y, PLINT npts ) 00332 { 00333 GcwPLdev * dev = pls->dev; 00334 GnomeCanvasPoints * points; 00335 GnomeCanvasPoints pts; 00336 GnomeCanvasGroup * group; 00337 GnomeCanvasItem * item; 00338 GnomeCanvas * canvas; 00339 00340 GdkPoint * gdkpoints; 00341 00342 PLINT i; 00343 00344 gdouble width; 00345 guint32 color; 00346 00347 #ifdef DEBUG_GCW_2 00348 gcw_debug( "<plD_polyline_gcw />\n" ); 00349 #endif 00350 00351 if ( !GNOME_IS_CANVAS( dev->canvas ) ) 00352 plexit( "GCW driver <plD_polyline_gcw>: Canvas not found" ); 00353 canvas = dev->canvas; 00354 00355 if ( dev->use_persistence ) 00356 group = dev->group_persistent; 00357 else 00358 group = dev->group_hidden; 00359 00360 if ( dev->use_pixmap && !dev->use_persistence ) // Write to bg pixmap 00361 00362 { 00363 if ( ( gdkpoints = (GdkPoint *) malloc( npts * sizeof ( GdkPoint ) ) ) == NULL ) 00364 { 00365 plabort( "GCW driver <plD_polyline_gcw>: Could not create gdkpoints" ); 00366 return; 00367 } 00368 00369 if ( !pls->portrait ) 00370 { 00371 for ( i = 0; i < npts; i++ ) 00372 { 00373 gdkpoints[i].x = (gint) ( x[i] / VSCALE ); 00374 gdkpoints[i].y = (gint) ( dev->height - y[i] / VSCALE ); 00375 } 00376 } 00377 else // Swap x and y for portrait mode 00378 { 00379 for ( i = 0; i < npts; i++ ) 00380 { 00381 gdkpoints[i].x = (gint) ( dev->height - y[i] / VSCALE ); 00382 gdkpoints[i].y = (gint) ( dev->width - x[i] / VSCALE ); 00383 } 00384 } 00385 00386 gdk_draw_lines( dev->background, dev->gc, gdkpoints, npts ); 00387 00388 dev->pixmap_has_data = TRUE; 00389 00390 free( gdkpoints ); 00391 } 00392 else // Draw Canvas lines 00393 00394 { 00395 // Put the data in a points structure 00396 if ( ( points = gnome_canvas_points_new( npts ) ) == NULL ) 00397 { 00398 plabort( "GCW driver <plD_polyline_gcw>: Cannot create points" ); 00399 return; 00400 } 00401 if ( !pls->portrait ) 00402 { 00403 for ( i = 0; i < npts; i++ ) 00404 { 00405 points->coords[2 * i] = (gdouble) ( x[i] / VSCALE ); 00406 points->coords[2 * i + 1] = (gdouble) ( -y[i] / VSCALE ); 00407 } 00408 } 00409 else // Swap x and y for portrait mode 00410 { 00411 for ( i = 0; i < npts; i++ ) 00412 { 00413 points->coords[2 * i] = (gdouble) ( dev->height - y[i] / VSCALE ); 00414 points->coords[2 * i + 1] = (gdouble) ( -x[i] / VSCALE ); 00415 } 00416 } 00417 00418 // Get the pen width and color 00419 width = pls->width; 00420 color = dev->color; 00421 00422 00423 // Workaround for the 'attempt to put segment in horiz list twice' 00424 // from libgnomecanvas: 00425 // 00426 // Plot a series of line segments rather than a single polyline. 00427 // 00428 // This slows rendering down a considerable amount. However, it is 00429 // unclear what else can be done. Libgnomecanvas should be able to 00430 // deal with all valid data; bizarre plotting errors happen along with 00431 // this error. 00432 // 00433 // Note that instead of allocating a series of points structures, 00434 // we just refer to the original one from a separate struct 00435 // (GnomeCanvas does not hold a reference to the points structure). 00436 // 00437 00438 pts.num_points = 2; 00439 pts.ref_count = 1; 00440 pts.coords = points->coords; 00441 00442 for ( i = 0; i < npts - 1; i++ ) 00443 { 00444 pts.coords = &( points->coords[2 * i] ); 00445 00446 if ( !GNOME_IS_CANVAS_ITEM( 00447 item = gnome_canvas_item_new( group, 00448 GNOME_TYPE_CANVAS_LINE, 00449 "cap_style", GDK_CAP_ROUND, 00450 "join-style", GDK_JOIN_ROUND, 00451 "points", &pts, 00452 "fill-color-rgba", color, 00453 "width-units", width, 00454 NULL ) 00455 ) ) 00456 { 00457 plwarn( "GCW driver <plD_polyline_gcw>: Canvas item not created." ); 00458 } 00459 } 00460 00461 // Free the points structure 00462 gnome_canvas_points_free( points ); 00463 } 00464 } 00465 00466 00467 //-------------------------------------------------------------------------- 00468 // plD_line_gcw() 00469 // 00470 // Draw a line in the current color from (x1,y1) to (x2,y2). 00471 //-------------------------------------------------------------------------- 00472 00473 void plD_line_gcw( PLStream *pls, short x1, short y1, short x2, short y2 ) 00474 { 00475 short x[2]; 00476 short y[2]; 00477 00478 #ifdef DEBUG_GCW_2 00479 gcw_debug( "<plD_line_gcw />\n" ); 00480 #endif 00481 00482 x[0] = x1; 00483 x[1] = x2; 00484 y[0] = y1; 00485 y[1] = y2; 00486 00487 plD_polyline_gcw( pls, x, y, (PLINT) 2 ); 00488 } 00489 00490 00491 //-------------------------------------------------------------------------- 00492 // plD_eop_gcw() 00493 // 00494 // End of page. 00495 //-------------------------------------------------------------------------- 00496 00497 void plD_eop_gcw( PLStream *pls ) 00498 { 00499 GcwPLdev * dev = pls->dev; 00500 GnomeCanvas * canvas; 00501 00502 GdkPixbuf * pixbuf; 00503 GnomeCanvasItem * item; 00504 GnomeCanvasGroup* group; 00505 00506 gdouble dx, dy; 00507 00508 gint count = 1, n; 00509 00510 void *save_state; 00511 00512 PLINT width, height; 00513 00514 if ( !GNOME_IS_CANVAS( dev->canvas ) ) 00515 plexit( "GCW driver <plD_eop_gcw>: Canvas not found" ); 00516 canvas = dev->canvas; 00517 00518 // Ignore if there is no hidden group. This means BOP has not been 00519 // called yet. 00520 // 00521 if ( !GNOME_IS_CANVAS_GROUP( dev->group_hidden ) ) 00522 return; 00523 00524 #ifdef DEBUG_GCW_1 00525 gcw_debug( "<plD_eop_gcw>\n" ); 00526 #endif 00527 00528 if ( dev->use_persistence ) 00529 group = dev->group_persistent; 00530 else 00531 group = dev->group_hidden; 00532 00533 // Retrieve the device width and height of the canvas 00534 width = *(PLINT *) g_object_get_data( G_OBJECT( canvas ), "canvas-width" ); 00535 height = *(PLINT *) g_object_get_data( G_OBJECT( canvas ), "canvas-height" ); 00536 00537 if ( dev->pixmap_has_data ) 00538 { 00539 // Render the pixmap to a pixbuf on the canvas. 00540 if ( !GDK_IS_PIXBUF( pixbuf = gdk_pixbuf_get_from_drawable( NULL, 00541 dev->background, 00542 dev->colormap, 00543 0, 0, 00544 0, 0, 00545 width, height ) ) ) 00546 { 00547 plwarn( "GCW driver <plD_eop_gcw>: Can't draw pixmap into pixbuf." ); 00548 } 00549 else // Pixbuf creation succeeded 00550 00551 { 00552 if ( !GNOME_IS_CANVAS_ITEM( 00553 item = gnome_canvas_item_new( dev->group_hidden, 00554 GNOME_TYPE_CANVAS_PIXBUF, 00555 "pixbuf", pixbuf, 00556 "x", 1., 00557 "y", (gdouble) ( -height + 1. ), 00558 "width", (gdouble) ( width ), 00559 "height", (gdouble) ( height ), 00560 NULL ) 00561 ) ) 00562 { 00563 plwarn( "GCW driver <plD_eop_gcw>: Canvas item not created." ); 00564 } 00565 00566 // Free the pixbuf 00567 g_object_unref( pixbuf ); 00568 } 00569 } 00570 else 00571 { 00572 // Use a rectangle for the background instead (faster) 00573 if ( !GNOME_IS_CANVAS_ITEM( 00574 item = gnome_canvas_item_new( 00575 dev->group_hidden, 00576 GNOME_TYPE_CANVAS_RECT, 00577 "x1", 0., 00578 "y1", (gdouble) ( -height ), 00579 "x2", (gdouble) ( width ), 00580 "y2", 0., 00581 "fill-color-rgba", plcolor_to_rgba( pls->cmap0[0], 0xFF ), 00582 "width-units", 0., 00583 NULL ) 00584 ) ) 00585 { 00586 plabort( "GCW driver <pld_eop_gcw>: Canvas item not created" ); 00587 return; 00588 } 00589 } 00590 00591 // Move the persistent group to the front 00592 gnome_canvas_item_raise_to_top( GNOME_CANVAS_ITEM( dev->group_persistent ) ); 00593 00594 // Move the background to the back 00595 if ( GNOME_IS_CANVAS_ITEM( item ) ) 00596 gnome_canvas_item_lower_to_bottom( item ); 00597 00598 // Make the hidden group visible 00599 gnome_canvas_item_show( GNOME_CANVAS_ITEM( dev->group_hidden ) ); 00600 00601 // Destroy the old visible group 00602 if ( GNOME_IS_CANVAS_GROUP( dev->group_visible ) ) 00603 { 00604 gtk_object_destroy( (GtkObject *) ( dev->group_visible ) ); 00605 dev->group_visible = NULL; 00606 } 00607 00608 // Clear the background pixmap 00609 if ( !dev->use_persistence && dev->pixmap_has_data ) 00610 gcw_clear_background(); 00611 00612 // Name the hidden group as visible 00613 dev->group_visible = dev->group_hidden; 00614 dev->group_hidden = NULL; 00615 00616 // Update the canvas 00617 canvas->need_update = 1; 00618 gnome_canvas_update_now( canvas ); 00619 00620 // 00621 // Copy the plot buffer for future reference, otherwise it is 00622 // thrown out. 00623 // 00624 00625 save_state = g_object_get_data( G_OBJECT( canvas ), "plotbuf" ); 00626 save_state = (void *) plbuf_save( pls, save_state ); 00627 00628 // Attach the saved state to the canvas 00629 g_object_set_data( G_OBJECT( canvas ), "plotbuf", (gpointer) save_state ); 00630 00631 // If the driver is creating its own canvasses, set dev->canvas to be 00632 // NULL now in order to force creation of a new canvas when the next 00633 // drawing call is made. The new canvas will be placed in a new 00634 // notebook page. 00635 // 00636 if ( dev->window != NULL ) 00637 { 00638 dev->canvas = NULL; 00639 dev->group_visible = NULL; 00640 dev->group_hidden = NULL; 00641 dev->group_persistent = NULL; 00642 } 00643 00644 #ifdef DEBUG_GCW_1 00645 gcw_debug( "</plD_eop_gcw>\n" ); 00646 #endif 00647 } 00648 00649 00650 //-------------------------------------------------------------------------- 00651 // plD_bop_gcw() 00652 // 00653 // Set up for the next page. 00654 // 00655 //-------------------------------------------------------------------------- 00656 00657 void plD_bop_gcw( PLStream *pls ) 00658 { 00659 GcwPLdev * dev = pls->dev; 00660 GnomeCanvas* canvas; 00661 00662 if ( !GNOME_IS_CANVAS( dev->canvas ) ) 00663 { 00664 if ( pls->hack ) 00665 return; // Wait for a canvas via DEVINIT 00666 else 00667 gcw_install_canvas( NULL ); 00668 } 00669 canvas = dev->canvas; 00670 00671 #ifdef DEBUG_GCW_1 00672 gcw_debug( "<plD_bop_gcw>\n" ); 00673 #endif 00674 00675 // Replay escape calls that come in before PLESC_DEVINIT. Some of them 00676 // required a Canvas that didn't exist yet. 00677 // 00678 if ( dev->plstate_width ) 00679 plD_state_gcw( pls, PLSTATE_WIDTH ); 00680 if ( dev->plstate_color0 ) 00681 plD_state_gcw( pls, PLSTATE_COLOR0 ); 00682 if ( dev->plstate_color1 ) 00683 plD_state_gcw( pls, PLSTATE_COLOR1 ); 00684 dev->plstate_width = FALSE; 00685 dev->plstate_color0 = FALSE; 00686 dev->plstate_color1 = FALSE; 00687 00688 // Creat a new hidden group; all new drawing will be to this group 00689 if ( !GNOME_IS_CANVAS_ITEM( 00690 dev->group_hidden = GNOME_CANVAS_GROUP( gnome_canvas_item_new( 00691 gnome_canvas_root( canvas ), 00692 gnome_canvas_clipgroup_get_type(), 00693 "x", 0., 00694 "y", 0., 00695 NULL ) ) 00696 ) ) 00697 { 00698 plexit( "GCW driver <plD_bop_gcw>: Canvas group cannot be created" ); 00699 } 00700 00701 // Set the clip to NULL 00702 g_object_set( G_OBJECT( dev->group_hidden ), "path", NULL, NULL ); 00703 00704 // Hide this group until drawing is done 00705 gnome_canvas_item_hide( GNOME_CANVAS_ITEM( dev->group_hidden ) ); 00706 00707 #ifdef DEBUG_GCW_1 00708 gcw_debug( "</plD_bop_gcw>\n" ); 00709 #endif 00710 } 00711 00712 00713 //-------------------------------------------------------------------------- 00714 // plD_tidy_gcw() 00715 // 00716 // Close graphics file 00717 //-------------------------------------------------------------------------- 00718 00719 void plD_tidy_gcw( PLStream *pls ) 00720 { 00721 GcwPLdev* dev = pls->dev; 00722 00723 #ifdef DEBUG_GCW_1 00724 gcw_debug( "<plD_tidy_gcw>\n" ); 00725 #endif 00726 00727 #ifdef PL_HAVE_FREETYPE 00728 if ( pls->dev_text ) 00729 { 00730 FT_Data *FT = (FT_Data *) pls->FT; 00731 plscmap0n( FT->ncol0_org ); 00732 plD_FreeType_Destroy( pls ); 00733 } 00734 #endif 00735 00736 if ( dev->window != NULL ) 00737 { 00738 gtk_main(); 00739 } 00740 00741 #ifdef DEBUG_GCW_1 00742 gcw_debug( "</plD_tidy_gcw>\n" ); 00743 #endif 00744 } 00745 00746 00747 //-------------------------------------------------------------------------- 00748 // plD_state_gcw() 00749 // 00750 // Handle change in PLStream state (color, pen width, fill attribute, etc). 00751 // 00752 // Note that PLplot sometimes tries to change states before the device is 00753 // fully initialized (i.e., via PLESC_DEVINIT). We must keep track of 00754 // such attempts, and invoke the state change during the next call to 00755 // plD_bop_gcw. 00756 // 00757 //-------------------------------------------------------------------------- 00758 00759 void plD_state_gcw( PLStream *pls, PLINT op ) 00760 { 00761 GcwPLdev* dev = pls->dev; 00762 char opname[20], msg[100]; 00763 00764 #ifdef DEBUG_GCW_1 00765 if ( op == PLSTATE_WIDTH ) 00766 strcpy( opname, "PLSTATE_WIDTH" ); 00767 else if ( op == PLSTATE_COLOR0 ) 00768 strcpy( opname, "PLSTATE_COLOR0" ); 00769 else if ( op == PLSTATE_COLOR1 ) 00770 strcpy( opname, "PLSTATE_COLOR1" ); 00771 else if ( op == PLSTATE_FILL ) 00772 strcpy( opname, "PLSTATE_FILL" ); 00773 else if ( op == PLSTATE_CMAP0 ) 00774 strcpy( opname, "PLSTATE_CMAP0" ); 00775 else if ( op == PLSTATE_CMAP1 ) 00776 strcpy( opname, "PLSTATE_CMAP1" ); 00777 else 00778 strcpy( opname, "unknown" ); 00779 snprintf( msg, 100, "<plD_state_gcw />: %s\n", opname ); 00780 gcw_debug( msg ); 00781 #endif 00782 00783 switch ( op ) 00784 { 00785 case PLSTATE_WIDTH: 00786 if ( GNOME_IS_CANVAS( dev->canvas ) ) 00787 { 00788 if ( dev->use_pixmap ) 00789 { 00790 gdk_gc_set_line_attributes( dev->gc, pls->width, 00791 GDK_LINE_SOLID, 00792 GDK_CAP_BUTT, 00793 GDK_JOIN_MITER ); 00794 } 00795 } 00796 else 00797 dev->plstate_width = TRUE; 00798 break; 00799 00800 case PLSTATE_COLOR0: 00801 if ( GNOME_IS_CANVAS( dev->canvas ) ) 00802 { 00803 dev->color = plcolor_to_rgba( pls->cmap0[pls->icol0], 0xFF ); 00804 if ( dev->use_pixmap ) 00805 gcw_set_gdk_color(); 00806 } 00807 else 00808 dev->plstate_color0 = TRUE; 00809 break; 00810 00811 case PLSTATE_COLOR1: 00812 if ( GNOME_IS_CANVAS( dev->canvas ) ) 00813 { 00814 dev->color = plcolor_to_rgba( pls->cmap1[pls->icol1], 0xFF ); 00815 if ( dev->use_pixmap ) 00816 gcw_set_gdk_color(); 00817 } 00818 else 00819 dev->plstate_color1 = TRUE; 00820 break; 00821 00822 case PLSTATE_FILL: 00823 break; 00824 00825 case PLSTATE_CMAP0: 00826 break; 00827 00828 case PLSTATE_CMAP1: 00829 break; 00830 00831 default: 00832 break; 00833 } 00834 } 00835 00836 00837 //-------------------------------------------------------------------------- 00838 // fill_polygon() 00839 // 00840 // Fills the polygon defined by the given points. Used for shade 00841 // plotting. Only solid fills are allowed. 00842 //-------------------------------------------------------------------------- 00843 00844 static void fill_polygon( PLStream* pls ) 00845 { 00846 GnomeCanvasPoints* points; 00847 GnomeCanvasGroup * group; 00848 GnomeCanvasItem * item; 00849 GcwPLdev * dev = pls->dev; 00850 GnomeCanvas * canvas; 00851 00852 PLINT i; 00853 00854 GdkPoint * gdkpoints; 00855 00856 PLINT tmp; 00857 00858 #ifdef DEBUG_GCW_2 00859 gcw_debug( "<fill_polygon />\n" ); 00860 #endif 00861 00862 if ( !GNOME_IS_CANVAS( dev->canvas ) ) 00863 plexit( "GCW driver <fill_polygon>: Canvas not found" ); 00864 canvas = dev->canvas; 00865 00866 if ( dev->use_persistence ) 00867 group = dev->group_persistent; 00868 else 00869 group = dev->group_hidden; 00870 00871 if ( dev->use_pixmap && !dev->use_persistence ) // Write to a pixmap 00872 00873 { 00874 if ( ( gdkpoints = (GdkPoint *) malloc( pls->dev_npts * sizeof ( GdkPoint ) ) ) == NULL ) 00875 { 00876 plabort( "GCW driver <fill_polygon>: Could not create gdkpoints" ); 00877 return; 00878 } 00879 00880 if ( !pls->portrait ) 00881 { 00882 for ( i = 0; i < pls->dev_npts; i++ ) 00883 { 00884 gdkpoints[i].x = (gint) ( pls->dev_x[i] / VSCALE ); 00885 gdkpoints[i].y = (gint) ( dev->height - pls->dev_y[i] / VSCALE ); 00886 } 00887 } 00888 else // Swap x and y for portrait mode 00889 { 00890 for ( i = 0; i < pls->dev_npts; i++ ) 00891 { 00892 gdkpoints[i].x = (gint) ( dev->height - pls->dev_y[i] / VSCALE ); 00893 gdkpoints[i].y = (gint) ( dev->width - pls->dev_x[i] / VSCALE ); 00894 } 00895 } 00896 00897 gdk_draw_polygon( dev->background, dev->gc, TRUE, gdkpoints, pls->dev_npts ); 00898 00899 dev->pixmap_has_data = TRUE; 00900 00901 free( gdkpoints ); 00902 } 00903 else // Use Gnome Canvas polygons 00904 00905 { 00906 if ( ( points = gnome_canvas_points_new( pls->dev_npts ) ) == NULL ) 00907 { 00908 plabort( "GCW driver <fill_polygon>: Could not create points" ); 00909 return; 00910 } 00911 00912 if ( !pls->portrait ) 00913 { 00914 for ( i = 0; i < pls->dev_npts; i++ ) 00915 { 00916 points->coords[2 * i] = (gdouble) ( pls->dev_x[i] / VSCALE ); 00917 points->coords[2 * i + 1] = (gdouble) ( -pls->dev_y[i] / VSCALE ); 00918 } 00919 } 00920 else // Swap x and y for portrait mode 00921 { 00922 for ( i = 0; i < pls->dev_npts; i++ ) 00923 { 00924 points->coords[2 * i] = (gdouble) ( dev->height - pls->dev_y[i] / VSCALE ); 00925 points->coords[2 * i + 1] = (gdouble) ( -pls->dev_x[i] / VSCALE ); 00926 } 00927 } 00928 00929 if ( !GNOME_IS_CANVAS_ITEM( 00930 item = gnome_canvas_item_new( group, 00931 GNOME_TYPE_CANVAS_POLYGON, 00932 "points", points, 00933 "fill-color-rgba", dev->color, 00934 // "outline-color-rgba",dev->color, 00935 NULL ) 00936 ) ) 00937 { 00938 plwarn( "GCW driver <fill_polygon>: Canvas item not created." ); 00939 } 00940 00941 gnome_canvas_points_free( points ); 00942 00943 00944 // Draw a thin outline for each polygon; note that doing this 00945 // using the "outline-color-rgba" property above can result in 00946 // Canvas errors. 00947 // 00948 tmp = pls->width; 00949 pls->width = 1; 00950 plD_polyline_gcw( pls, pls->dev_x, pls->dev_y, pls->dev_npts ); 00951 pls->width = tmp; 00952 } 00953 } 00954 00955 #ifdef PL_HAVE_FREETYPE 00956 //-------------------------------------------------------------------------- 00957 // proc_str() 00958 // 00959 // Handles call to draw text on the canvas when the HAS_TEXT escape funtion 00960 // case is invoked. 00961 // 00962 // This routine is unicode enabled, and requires freetype. 00963 //-------------------------------------------------------------------------- 00964 00965 static void proc_str( PLStream *pls, EscText *args ) 00966 { 00967 PLFLT *t = args->xform; // Transform matrix for string 00968 00969 GnomeCanvasGroup* group; 00970 GcwPLdev * dev = pls->dev; 00971 GnomeCanvas * canvas; 00972 00973 PLUNICODE fci; // The unicode font characterization integer 00974 guchar *fontname = NULL; 00975 gint font_size; 00976 GnomeFont *font; 00977 GnomeFontFace *face; 00978 GnomeGlyphList *glyphlist; 00979 guint Nglyphs; 00980 00981 gdouble affine_baseline[6] = { 0., 0., 0., 0., 0., 0. }; // Affine transforms 00982 gdouble affine_translate[6] = { 0., 0., 0., 0., 0., 0. }; 00983 gdouble affine_rotate[6] = { 0., 0., 0., 0., 0., 0. }; 00984 gdouble affine_plplot[6] = { 0., 0., 0., 0., 0., 0. }; 00985 00986 GnomeCanvasItem * item[200]; // List of string segments 00987 gdouble width[200], height[200]; // Height and width of string segment 00988 gdouble up_list[200]; // Indicates sub/sup position of string segment 00989 gdouble up = 0, scale = 1; // Used to create superscripts and subscripts 00990 00991 ArtDRect bbox; // Bounding box for each segment to get width & height 00992 00993 const PLUNICODE *text; // The text and pointers to it 00994 guint i = 0, Ntext; // The text index and maximum length 00995 00996 char esc; // The escape character 00997 00998 guint N = 0; // The number of text segments 00999 gdouble total_width = 0, sum_width = 0; 01000 01001 guint symbol; 01002 01003 01004 #ifdef DEBUG_GCW_2 01005 gcw_debug( "<proc_str>\n" ); 01006 #endif 01007 01008 if ( !GNOME_IS_CANVAS( dev->canvas ) ) 01009 plexit( "GCW driver <proc_str>: Canvas not found" ); 01010 canvas = dev->canvas; 01011 01012 if ( dev->use_persistence ) 01013 group = dev->group_persistent; 01014 else 01015 group = dev->group_hidden; 01016 01017 // Retrieve the escape character 01018 plgesc( &esc ); 01019 01020 // Put the transform matrix values in the order expected by libart. 01021 // Note that the plplot transform matrix only has a rotation and shear; 01022 // plplot's rotation direction and shear are opposite from that expected 01023 // by libart, hence the negative signs below. 01024 // 01025 affine_plplot[0] = t[0]; // cos(theta) 01026 affine_plplot[1] = -t[2]; // sin(theta) 01027 affine_plplot[2] = -t[1]; // a cos(theta) - sin(theta) 01028 affine_plplot[3] = t[3]; // a sin(theta) + cos(theta) 01029 01030 // Font size: size is in pixels but chrht is in mm. Why the extra factor? 01031 font_size = (gint) ( pls->chrht * DEVICE_PIXELS_PER_MM * 1.5 ); 01032 01033 // Determine the default font 01034 plgfci( &fci ); 01035 fontname = plP_FCI2FontName( fci, FontLookup, N_TrueTypeLookup ); 01036 if ( fontname == NULL ) 01037 { 01038 plabort( "GCW driver <proc_str>: FCI inconsistent with TrueTypeLookup" ); 01039 return; 01040 } 01041 01042 // Retrieve the font face 01043 face = gnome_font_face_find_from_filename( fontname, 0 ); 01044 01045 // Get the unicode string 01046 text = args->unicode_array; 01047 Ntext = (guint) ( args->unicode_array_len ); 01048 01049 // Process the string: Break it into segments of constant font and size, 01050 // making sure we process control characters as we come to them. Save 01051 // the extra information that will allow us to place the text on the 01052 // canvas. 01053 // 01054 while ( i < Ntext ) 01055 { 01056 // Process the next character 01057 01058 if ( text[i] & PL_FCI_MARK ) // Is it a font characterization index? 01059 01060 { // Determine the font name 01061 fontname = plP_FCI2FontName( text[i], FontLookup, N_TrueTypeLookup ); 01062 if ( fontname == NULL ) 01063 { 01064 plabort( "GCW driver <proc_str>: FCI inconsistent with " 01065 "TrueTypeLookup" ); 01066 return; 01067 } 01068 01069 // Retrieve the font face 01070 gnome_font_unref( face ); // We already have a face 01071 face = gnome_font_face_find_from_filename( fontname, 0 ); 01072 01073 i++; // Move ahead to the next character 01074 } 01075 else 01076 { 01077 if ( text[i] == esc ) // Check for escape sequences 01078 01079 { // Process escape sequence 01080 i++; // Move on to next character 01081 if ( i >= Ntext ) 01082 { 01083 plwarn( "GCW driver <proc_str>: Invalid escape sequence " 01084 "provided in text." ); 01085 return; 01086 } 01087 01088 switch ( text[i] ) 01089 { 01090 case '#': // <esc><esc>; this should translate to a hash 01091 break; // Watch out for it later 01092 01093 // Move to lower sub/sup position 01094 case 'd': 01095 case 'D': 01096 if ( up > 0. ) 01097 scale *= 1.25; // Subscript scaling parameter 01098 else 01099 scale *= 0.8; // Subscript scaling parameter 01100 up -= font_size / 2.; 01101 break; 01102 01103 // Move to higher sub/sup position 01104 case 'u': 01105 case 'U': 01106 if ( up < 0. ) 01107 scale *= 1.25; // Subscript scaling parameter 01108 else 01109 scale *= 0.8; // Subscript scaling parameter 01110 up += font_size / 2.; 01111 break; 01112 01113 // Ignore the next sequences 01114 01115 // Overline 01116 case '+': 01117 01118 // Underline 01119 case '-': 01120 01121 // Backspace 01122 case 'b': 01123 case 'B': 01124 plwarn( "GCW driver <proc_str>: '+', '-', and 'b' text " 01125 "escape sequences not processed." ); 01126 break; 01127 } // switch(text[i]) 01128 01129 if ( text[i] != '#' ) 01130 i++; // Move ahead to the next character 01131 } // if(text[i] == esc) 01132 } // if(text[i] & PL_FCI_MARK) 01133 01134 01135 if ( i == Ntext ) 01136 continue; // End of string 01137 01138 // Save the sub/sup position 01139 up_list[N] = up; 01140 01141 // Get the font 01142 font = gnome_font_face_get_font_default( face, font_size * scale ); 01143 // printf("\n\nfont name = %s\n\n",gnome_font_get_name(font)); 01144 01145 // Create the glyphlist for this text segment 01146 glyphlist = gnome_glyphlist_new(); 01147 gnome_glyphlist_font( glyphlist, font ); 01148 gnome_glyphlist_color( glyphlist, dev->color ); 01149 gnome_glyphlist_advance( glyphlist, TRUE ); 01150 gnome_glyphlist_kerning( glyphlist, 0. ); 01151 gnome_glyphlist_letterspace( glyphlist, 0. ); 01152 01153 // Free the font 01154 gnome_font_unref( font ); 01155 01156 // Move along to the next escape or FCI character, stuffing 01157 // everything else into the glyphlist. 01158 // 01159 Nglyphs = 0; 01160 while ( i < Ntext && !( text[i] & PL_FCI_MARK ) ) 01161 { 01162 // Differentiate between ## and escape sequences 01163 if ( text[i] == esc ) 01164 { 01165 if ( !( i > 0 && text[i - 1] == esc ) ) 01166 break; 01167 } 01168 01169 gnome_glyphlist_glyph( glyphlist, 01170 gnome_font_lookup_default( font, text[i] ) ); 01171 i++; Nglyphs++; 01172 } 01173 01174 if ( Nglyphs ) 01175 { 01176 // Determine the bounding box of the text 01177 gnome_glyphlist_bbox( glyphlist, NULL, 0, &bbox ); 01178 width[N] = bbox.x1 - bbox.x0; 01179 height[N] = bbox.y1 - bbox.y0; 01180 01181 // Keep track of the total string width so that we can justify it 01182 total_width += width[N]; 01183 if ( N != 0 ) 01184 total_width += 2; // Add a little extra space 01185 01186 // Create the canvas text item 01187 if ( !GNOME_IS_CANVAS_ITEM( 01188 item[N] = gnome_canvas_item_new( group, 01189 PLPLOT_TYPE_CANVAS_HACKTEXT, 01190 "glyphlist", glyphlist, 01191 "fill-color-rgba", dev->color, 01192 "x", 0., 01193 "y", 0., 01194 NULL ) 01195 ) ) 01196 { 01197 plabort( "GCW driver <proc_str>: Canvas item not created" ); 01198 return; 01199 } 01200 01201 // Free the glyphlist 01202 gnome_glyphlist_unref( glyphlist ); 01203 01204 // Advance to next string segment 01205 N++; 01206 } // if(Nglyphs) 01207 01208 01209 // Don't overflow buffer 01210 if ( N == 200 && i < Ntext ) 01211 { 01212 plabort( "GCW driver <proc_str>: too many text segments" ); 01213 return; 01214 } 01215 } // while(i<Ntext) 01216 01217 // We have all of the string segments. Place each on the canvas 01218 // appropriately. 01219 // 01220 for ( i = 0; i < N; i++ ) 01221 { 01222 // Calculate and apply the affine transforms 01223 art_affine_rotate( affine_rotate, 90. * ( pls->diorot - pls->portrait ) ); 01224 if ( !pls->portrait ) 01225 { 01226 art_affine_translate( affine_baseline, 01227 -total_width * args->just + sum_width, 01228 height[0] / 2.5 - up_list[i] ); 01229 art_affine_translate( affine_translate, 01230 args->x / VSCALE, -args->y / VSCALE ); 01231 } 01232 else // Swap x and y for portrait mode 01233 { 01234 art_affine_translate( affine_baseline, 01235 -total_width * args->just + sum_width, 01236 height[0] / 2.5 - up_list[i] ); 01237 art_affine_translate( affine_translate, 01238 dev->height - args->y / VSCALE, -args->x / VSCALE ); 01239 } 01240 gnome_canvas_item_affine_relative( item[i], affine_translate ); 01241 gnome_canvas_item_affine_relative( item[i], affine_rotate ); 01242 gnome_canvas_item_affine_relative( item[i], affine_plplot ); 01243 gnome_canvas_item_affine_relative( item[i], affine_baseline ); 01244 01245 // Keep track of the position in the string 01246 sum_width += width[i]; 01247 if ( i != N - 1 ) 01248 sum_width += 2; // Add a little extra space 01249 } 01250 01251 #ifdef DEBUG_GCW_2 01252 gcw_debug( "</proc_str>\n" ); 01253 #endif 01254 } 01255 #endif //PL_HAVE_FREETYPE 01256 01257 01258 //-------------------------------------------------------------------------- 01259 // plD_esc_gcw() 01260 // 01261 // Escape functions. 01262 // 01263 //-------------------------------------------------------------------------- 01264 01265 void plD_esc_gcw( PLStream *pls, PLINT op, void *ptr ) 01266 { 01267 GcwPLdev* dev = pls->dev; 01268 01269 #ifdef DEBUG_GCW_1 01270 char opname[20], msg[100]; 01271 if ( op == PLESC_DEVINIT ) 01272 strcpy( opname, "PLESC_DEVINIT" ); 01273 else if ( op == PLESC_CLEAR ) 01274 strcpy( opname, "PLESC_CLEAR" ); 01275 else if ( op == PLESC_FILL ) 01276 strcpy( opname, "PLESC_FILL" ); 01277 else if ( op == PLESC_HAS_TEXT ) 01278 strcpy( opname, "PLESC_HAS_TEXT" ); 01279 else if ( op == PLESC_GRAPH ) 01280 strcpy( opname, "PLESC_GRAPH" ); 01281 else 01282 strcpy( opname, "unknown" ); 01283 snprintf( msg, 100, "<plD_esc_gcw />: %s\n", opname ); 01284 gcw_debug( msg ); 01285 #endif 01286 01287 switch ( op ) 01288 { 01289 case PLESC_DEVINIT: 01290 gcw_init_canvas( GNOME_CANVAS( ptr ) ); 01291 pls->hack = 0; 01292 break; 01293 01294 case PLESC_CLEAR: 01295 break; 01296 01297 case PLESC_FILL: 01298 fill_polygon( pls ); 01299 break; 01300 01301 #ifdef PL_HAVE_FREETYPE 01302 case PLESC_HAS_TEXT: 01303 proc_str( pls, ptr ); // Draw the text 01304 break; 01305 #endif 01306 01307 case PLESC_GRAPH: 01308 break; 01309 01310 default: 01311 break; 01312 } 01313 }