• Main Page
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

gcw.c

Go to the documentation of this file.
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 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 // 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 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 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 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 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 //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 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 }

Generated on Wed Oct 12 2011 20:42:21 for PLplot by  doxygen 1.7.1