/* gcw-lib - Library routines for the Gnome Canvas Widget device driver.

  Copyright (C) 2005  Thomas J. Duck
  All rights reserved.


NOTICE

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
  USA 

DESCRIPTION

  The gcw-lib API allows users to interact with the GCW driver.  The
  library routines are also invoked by the driver itself when needed.

*/

#include "gcw.h"

extern PLStream *pls[];

void gcw_debug(char* msg)
{
  fprintf(stderr,msg);
  fflush(stderr);
}


/*--------------------------------------------------------------------------*\
 * gcw_set_gdk_color()
 *
 * Sets the gdk foreground color for drawing in the current device 
 * according to the current drawing color.
 *
\*--------------------------------------------------------------------------*/

void gcw_set_gdk_color()
{
#ifdef DEBUG_GCW_1
  gcw_debug("<gcw_set_gdk_color>\n");
#endif

  GcwPLdev* dev = plsc->dev;
  GdkColor color;

  color.red=(guint16)(plsc->curcolor.r/255.*65535); 
  color.green=(guint16)(plsc->curcolor.g/255.*65535); 
  color.blue=(guint16)(plsc->curcolor.b/255.*65535);
  
  if(!gdk_colormap_alloc_color(dev->colormap,&color,FALSE,TRUE))
    plwarn("GCW driver <set_gdk_color>: Could not allocate color.");
  
  gdk_gc_set_foreground(dev->gc,&color);

#ifdef DEBUG_GCW_1
  gcw_debug("</gcw_set_gdk_color>\n");
#endif
}


/*--------------------------------------------------------------------------*\
 * gcw_clear_background()
 *
 * Clears the background pixmap for the current gcw device with the 
 * background color.
 *
\*--------------------------------------------------------------------------*/

void gcw_clear_background()
{
  GcwPLdev* dev = plsc->dev;

  PLINT width,height;

#ifdef DEBUG_GCW_1
  gcw_debug("<clear_background>\n");
#endif

  /* Retrieve the device width and height */
  width = *(PLINT*)g_object_get_data(G_OBJECT(dev->canvas),"canvas-width");
  height = *(PLINT*)g_object_get_data(G_OBJECT(dev->canvas),"canvas-height");

  /* Allocate the background color*/
  gdk_colormap_alloc_color(dev->colormap,&(dev->bgcolor),FALSE,TRUE);

  /* Clear the background pixmap with the background color.  Note that
   * we are going to reset the current gdk drawing color below, so we
   * can reuse the gc. */
  gdk_gc_set_foreground(dev->gc,&(dev->bgcolor));
  gdk_draw_rectangle(dev->background,dev->gc,TRUE,0,0,width,height);

  /* Note that our pixmap is currently clear */
  dev->pixmap_has_data = FALSE;

  /* Reset the current gdk drawing color */
  gcw_set_gdk_color();

#ifdef DEBUG_GCW_1
  gcw_debug("</clear_background>\n");
#endif
}


/*--------------------------------------------------------------------------*\
 * gcw_init_canvas()
 *
 * Initializes a canvas for use with this driver, and installs it into
 * the current gcw device.
 *
\*--------------------------------------------------------------------------*/

void gcw_init_canvas(GnomeCanvas* canvas)
{
  GcwPLdev* dev = plsc->dev;

#ifdef DEBUG_GCW_1
  gcw_debug("<gcw_init_canvas>\n");
#endif

  if(!GNOME_IS_CANVAS(canvas)) plexit("GCW driver: Canvas not found");

  /* Add the canvas to the device */
  dev->canvas=canvas;

  /* Get the colormap */
  dev->colormap = gtk_widget_get_colormap(GTK_WIDGET(dev->canvas));

  /* Size the canvas */
  gcw_set_canvas_size(canvas,dev->width,dev->height);

  /* Create the persistent group, which is always visible */
  if(!GNOME_IS_CANVAS_ITEM(
    dev->group_persistent = GNOME_CANVAS_GROUP(gnome_canvas_item_new(
			      gnome_canvas_root(canvas),
			      gnome_canvas_clipgroup_get_type(),
			      "x",0.,
			      "y",0.,
			      NULL))
    )) {
    plexit("GCW driver <gcw_init_canvas>: Canvas group cannot be created");
  }

  /* Set the clip to NULL */
  g_object_set(G_OBJECT(dev->group_persistent),"path",NULL,NULL);

#ifdef DEBUG_GCW1
  gcw_debug("</gcw_init_canvas>\n");
#endif
}


/*--------------------------------------------------------------------------*\
 * gcw_install_canvas()
 *
 * Installs a canvas into the current gcw device's window.  The canvas is
 * created if necessary.  A variety of callbacks are defined for actions
 * in the window.
 *
\*--------------------------------------------------------------------------*/

/*******************************
 * Main window event callbacks *
 *******************************/

/* Delete event callback */
gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data ) {
  return FALSE; /* FALSE follows with the destroy signal */
}

/* Destroy event calback */
void destroy(GtkWidget *widget, gpointer data) {
  gtk_main_quit ();
}

/******************
 * Zoom callbacks *
 ******************/

/* All-purpose zoom callback.  Referred to by specific zoom callbacks given
 *  below.
 */
void zoom(gpointer data, gint flag) {

  gint n;

  GnomeCanvas *canvas;
  GtkWidget *scrolled_window;
  GList *list;

  GcwPLdev* dev = data;

  gdouble curmag,dum;

  /* Get the current canvas */
  n = gtk_notebook_get_current_page(GTK_NOTEBOOK(dev->notebook));
  scrolled_window = gtk_notebook_get_nth_page(GTK_NOTEBOOK(dev->notebook),n);
  canvas = GNOME_CANVAS(gtk_container_get_children(
	   GTK_CONTAINER(gtk_container_get_children(
           GTK_CONTAINER(scrolled_window))->data))->data);

  /* Determine the new magnification */
  if(flag==2) /* Zoom in */
    gcw_set_canvas_zoom(canvas,ZOOMSTEP);
  else if(flag==0) { /* Zoom out */

    /* Don't zoom smaller than the original size: this causes GDK pixmap
     * errors.
     */
    gnome_canvas_c2w(canvas,1,0,&curmag,&dum); 
    curmag = 1./curmag;
    if(curmag/ZOOMSTEP>1.) {
      gcw_set_canvas_zoom(canvas,1./ZOOMSTEP);
    }
    else {
      gcw_set_canvas_zoom(canvas,(PLFLT)(ZOOM100/curmag));
    }
  }
  else { /* Zoom 100 */
    /* Get current magnification */
    gnome_canvas_c2w(canvas,1,0,&curmag,&dum); 
    curmag = 1./curmag;
    gcw_set_canvas_zoom(canvas,(PLFLT)(ZOOM100/curmag));
  }

  /* Set the focus on the notebook */
  gtk_window_set_focus(GTK_WINDOW(dev->window),dev->notebook);
}


/* Callback when zoom in button is pressed */
void zoom_in(GtkWidget *widget, gpointer data ) {
  zoom(data,2);
}

/* Callback when zoom 100 button is pressed */
void zoom_100(GtkWidget *widget, gpointer data ) {
  zoom(data,1);
}

/* Callback when zoom out button is pressed */
void zoom_out(GtkWidget *widget, gpointer data ) {
  zoom(data,0);
}

/*****************************
 * File selection callbacks. *
 *****************************/

/* Callback to OK file selection.  Retrieves the selected filename and 
 * replays the plot buffer to it through the associated driver. 
 */
void file_ok_sel(GtkWidget *w, gpointer data)
{
  GcwPLdev* dev = data;

  GtkWidget *scrolled_window;
  GnomeCanvas *canvas;

  gchar *fname;
  FILE *f;
  guint n;
  char devname[10],str[100];

  PLINT cur_strm, new_strm;
  PLStream *plsr;

  GtkWidget *fs;

  gdouble curmag,dum;

  PLINT icol0,icol1,ncol0,ncol1;
  PLColor *cmap0,*cmap1;

  gboolean errflag = FALSE;

  struct stat buf;

  GtkDialog *dialog;
  GtkWidget *hbox,*message,*icon;
  gint result;

  guint context;
  
  /* Get the file name */
  if( (fname = 
       strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(dev->filew))))
      ==NULL )
    plabort("GCW driver <file_ok_sel>: Cannot obtain filename");

  /* Check to see if the file already exists, and respond appropriately */
  if(stat(fname,&buf)==0) {

    /* Confirm the user wants to overwrite the existing file */

    dialog = GTK_DIALOG(gtk_dialog_new_with_buttons("",
		          GTK_WINDOW(dev->filew),
			  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                          GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
                          GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL));

    message = gtk_label_new("");
    gtk_label_set_markup(GTK_LABEL(message),
			 "<span size=\"large\"><b>File exists.  "
			 "Overwrite?</b></span>");

    icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
				    GTK_ICON_SIZE_DIALOG);

    hbox = gtk_hbox_new(FALSE,0);
    gtk_box_pack_start(GTK_BOX(hbox), icon, TRUE, TRUE, 10);
    gtk_box_pack_start(GTK_BOX(hbox), message, TRUE, TRUE, 10);
    gtk_container_add(GTK_CONTAINER(dialog->vbox),hbox);

    gtk_widget_show_all(GTK_WIDGET(dialog));

    result = gtk_dialog_run(dialog);
    gtk_widget_destroy(GTK_WIDGET(dialog));
    if(result==GTK_RESPONSE_REJECT) return;
  }

  /* Hide the file selection widget */
  gtk_widget_hide(dev->filew);

  /* Put the focus back on the notebook */
  gtk_window_set_focus(GTK_WINDOW(dev->window),dev->notebook);

  /* Test that we can open and write to the file */
  if((f=fopen(fname,"w"))==NULL)
    plabort("GCW driver <file_ok_sel>: Cannot open output file");
  fclose(f);
  remove(fname); /* Otherwise, we may leave behind a zero-length file */

  /* Get the file extension and make sure we recognize it.  Allow names
   * of display devices as well.
   *
   * Note that we can't replot to xwin or tk devices, which can't open a
   * display for some reason.
   *
   */
  n = strlen(fname);
  if     (strcasecmp(&fname[n-3],"png")==0)  sprintf(devname,"png");
  else if(strcasecmp(&fname[n-3],"gif")==0)  sprintf(devname,"gif");
  else if(strcasecmp(&fname[n-3],"jpg")==0)  sprintf(devname,"jpg");
  else if(strcasecmp(&fname[n-4],"jpeg")==0) sprintf(devname,"jpeg");
  else if(strcasecmp(&fname[n-2],"ps")==0)   sprintf(devname,"ps");
  else if(strcasecmp(&fname[n-3],"psc")==0)  sprintf(devname,"psc");
  else if(strcasecmp(&fname[n-4],"xwin")==0) sprintf(devname,"xwin");
  else if(strcasecmp(&fname[n-3],"gcw")==0) sprintf(devname,"gcw");
  else if(strcasecmp(&fname[n-2],"tk")==0) sprintf(devname,"tk");
  else {
    if(dev->statusbar!=NULL) {
      context=gtk_statusbar_get_context_id(GTK_STATUSBAR(dev->statusbar),
					   "PLplot");
      gtk_statusbar_push(GTK_STATUSBAR(dev->statusbar), context,
			 " WARNING: File type not recognized (unknown "
			 "extension).  Use one of ps, psc, png, jpg, or "
			 "gif."
			 );
      return;
    }
    else plabort("GCW driver <file_ok_sel>: File type not recognized");
  }

  /* Check that we are set up appropriately for device */
  if( strcmp(devname,"ps")==0 || strcmp(devname,"psc")==0 ) {
    if(plsc->dev_hrshsym != 1)
      if(dev->statusbar!=NULL) {
	context=gtk_statusbar_get_context_id(GTK_STATUSBAR(dev->statusbar),
					     "PLplot");

	gtk_statusbar_push(GTK_STATUSBAR(dev->statusbar),context,
			   " NOTE: Use '-drvopt hrshsym' on command-line "
			   "for complete symbol set in PostScript files."
			   );
      }
      else
	plwarn("GCW driver: Use '-drvopt hrshsym' if symbols are missing in "
	       "saved PostScript files.");
  }
  if( strcmp(devname,"xwin")==0 || strcmp(devname,"tk")==0 ) {
    if(plsc->dev_text != 0)
      plwarn("GCW driver: Use '-drvopt text=0'");
  }

  /* Get the current canvas */
  n = gtk_notebook_get_current_page(GTK_NOTEBOOK(dev->notebook));
  scrolled_window = gtk_notebook_get_nth_page(GTK_NOTEBOOK(dev->notebook),n);
  canvas = GNOME_CANVAS(gtk_container_get_children(
	   GTK_CONTAINER(gtk_container_get_children(
           GTK_CONTAINER(scrolled_window))->data))->data);

  /* Hack: Swap in the saved tempfile and colormaps */
  f = plsc->plbufFile;
  plsc->plbufFile = g_object_get_data(G_OBJECT(canvas),"plotbuf");
  if(plsc->plbufFile == NULL) errflag=TRUE;

  icol0=plsc->icol0;
  if( g_object_get_data(G_OBJECT(canvas),"icol0") != NULL )
    plsc->icol0 = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"icol0");
  else errflag=TRUE;

  ncol0=plsc->ncol0;
  if( g_object_get_data(G_OBJECT(canvas),"ncol0") != NULL )
    plsc->ncol0 = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"ncol0");
  else errflag=TRUE;
    
  cmap0=plsc->cmap0;
  if( g_object_get_data(G_OBJECT(canvas),"cmap0") )
    plsc->cmap0 = (PLColor*)g_object_get_data(G_OBJECT(canvas),"cmap0");
  else errflag=TRUE;

  icol1=plsc->icol1;
  if( g_object_get_data(G_OBJECT(canvas),"icol1") != NULL )
    plsc->icol1 = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"icol1");
  else errflag=TRUE;

  ncol1=plsc->ncol1;
  if( g_object_get_data(G_OBJECT(canvas),"ncol1") != NULL )
    plsc->ncol1 = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"ncol1");
  else errflag=TRUE;

  cmap1=plsc->cmap1;
  if( g_object_get_data(G_OBJECT(canvas),"cmap1") != NULL )
    plsc->cmap1 = (PLColor*)g_object_get_data(G_OBJECT(canvas),"cmap1");
  else errflag=TRUE;

  if(!errflag) {

    plsetopt ("drvopt","text"); /* Blank out the drvopts (not needed here) */

    /* Get the current stream and make a new one */
    plgstrm(&cur_strm);
    plsr = plsc; /* Save a pointer to the current stream */

    plmkstrm(&new_strm);
    plsfnam(fname); /* Set the file name */
    plsdev(devname); /* Set the device */

    /* Copy the current stream to the new stream. */
    plcpstrm(cur_strm, 0); 

    plreplot();            /* do the save by replaying the plot buffer */

    plend1();              /* finish the device */

    plsstrm(cur_strm);     /* return to previous stream */
  }
  else plwarn("GCW driver <file_ok_sel>: Cannot set up output stream.");


  /* Restore */

  plsc->plbufFile = f;
  plsc->icol0 = icol0;
  plsc->ncol0 = ncol0;
  plsc->cmap0 = cmap0;
  plsc->icol1 = icol1;
  plsc->ncol1 = ncol1;
  plsc->cmap1 = cmap1;
}

/* Callback to cancel file selection */
void file_cancel_sel(GtkWidget *w, gpointer data)
{
  GcwPLdev* dev = data;
  
  /* Hide the file selection widget */
  gtk_widget_hide(dev->filew);

  /* Put the focus back on the notebook */
  gtk_window_set_focus(GTK_WINDOW(dev->window),dev->notebook);
}

/* Callback to create file selection dialog */
void filesel(GtkWidget *widget, gpointer data ) {

  GtkWidget *scrolled_window;
  GnomeCanvas *canvas;

  GcwPLdev* dev = data;

  guint n;

  /* Get the current canvas */
  n = gtk_notebook_get_current_page(GTK_NOTEBOOK(dev->notebook));
  scrolled_window = gtk_notebook_get_nth_page(GTK_NOTEBOOK(dev->notebook),n);
  canvas = GNOME_CANVAS(gtk_container_get_children(
	   GTK_CONTAINER(gtk_container_get_children(
           GTK_CONTAINER(scrolled_window))->data))->data);

  /* Create a new file dialog if it doesn't already exist */
  if(dev->filew == NULL) {

    /* Create a new file selection widget */
    dev->filew = gtk_file_selection_new ("File selection");
    
    /* Connect the ok_button to file_ok_sel function */
    g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(dev->filew)->ok_button),
		     "clicked", G_CALLBACK(file_ok_sel), (gpointer)dev);
    
    /* Connect the cancel_button to destroy the widget */
    g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(dev->filew)->cancel_button),
		     "clicked", G_CALLBACK(file_cancel_sel), (gpointer)dev);
    
    /* Lets set the filename, as if this were a save dialog, and we are giving
       a default filename */
    gtk_file_selection_set_filename(GTK_FILE_SELECTION(dev->filew), 
				    "plot.psc");
  }
    
  gtk_widget_show(dev->filew);
}

/**************************
 * Key release callbacks. *
 **************************/

void key_release(GtkWidget *widget, GdkEventKey  *event, gpointer data ) {
  if(event->keyval == '+')  zoom(data,2);
  if(event->keyval == '=')  zoom(data,1);
  if(event->keyval == '-')  zoom(data,0);
  if(event->keyval == 'q')  destroy(widget,data);
}

/**********************
 * gcw_install_canvas *
 **********************/

void gcw_install_canvas(GnomeCanvas *canvas)
{
  GcwPLdev* dev = plsc->dev;
  GtkWidget *vbox,*hbox,*button,*image,*scrolled_window;

  gboolean flag = FALSE;

  PLINT width,height;

#ifdef DEBUG_GCW_1
  gcw_debug("<gcw_install_canvas>\n");
#endif

  /* Create a new canvas if needed */
  if(!GNOME_IS_CANVAS(canvas)) {   
      if( !GNOME_IS_CANVAS(canvas = GNOME_CANVAS(gnome_canvas_new_aa())) )
	plexit("GCW driver <gcw_install_canvas>: Could not create Canvas");
  }

  /* Initialize canvas */
  gcw_init_canvas(canvas);

  if(dev->window==NULL) { /* Create a new window and install a notebook */

    flag = TRUE;
    
    /* Create a new window */
    dev->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(dev->window),"PLplot");

    /* Connect the signal handlers to the window decorations */
    g_signal_connect(G_OBJECT(dev->window),"delete_event",
		     G_CALLBACK(delete_event),NULL);
    g_signal_connect(G_OBJECT(dev->window),"destroy",G_CALLBACK(destroy),NULL);


    /* Create a vbox and put it into the window */
    vbox = gtk_vbox_new(FALSE,2);
    gtk_container_add(GTK_CONTAINER(dev->window),vbox);

    /* Create a hbox and put it into the vbox */
    hbox = gtk_hbox_new(FALSE,0);
    gtk_box_pack_start(GTK_BOX(vbox),hbox,TRUE,TRUE,0);

    /* Create a statusbar and put it into the vbox */
    dev->statusbar = gtk_statusbar_new();
    gtk_box_pack_start(GTK_BOX(vbox),dev->statusbar,FALSE,FALSE,0);

    /* Add an vbox to the hbox */
    vbox = gtk_vbox_new(FALSE,5);
    gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
    gtk_box_pack_start(GTK_BOX(hbox),vbox,FALSE,FALSE,0);

    /* Create the new notebook and add it to the hbox*/
    dev->notebook = gtk_notebook_new();
    gtk_notebook_set_scrollable(GTK_NOTEBOOK(dev->notebook),TRUE);
    gtk_box_pack_start(GTK_BOX(hbox),dev->notebook,TRUE,TRUE,0);
    g_signal_connect(G_OBJECT(dev->notebook), "key_release_event",
                         G_CALLBACK(key_release), G_OBJECT(dev->notebook));

    /* Use a few labels as spacers */
    gtk_box_pack_start(GTK_BOX(vbox),gtk_label_new(" "),FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(vbox),gtk_label_new(" "),FALSE,FALSE,0);

    /* Add buttons to the vbox */

    /* Add zoom in button and create callbacks */
    image = gtk_image_new_from_stock(GTK_STOCK_ZOOM_IN,
				     GTK_ICON_SIZE_SMALL_TOOLBAR);
    button = gtk_button_new();
    gtk_container_add(GTK_CONTAINER(button),image);
    gtk_box_pack_start(GTK_BOX(vbox),button,FALSE,FALSE,0);
    g_signal_connect (G_OBJECT(button), "clicked",
		      G_CALLBACK(zoom_in), (gpointer)dev);
    g_signal_connect(G_OBJECT(button), "key_release_event",
                         G_CALLBACK(key_release), (gpointer)dev);

    /* Add zoom100 button and create callbacks */
    image = gtk_image_new_from_stock(GTK_STOCK_ZOOM_100,
				     GTK_ICON_SIZE_SMALL_TOOLBAR);
    button = gtk_button_new();
    gtk_container_add(GTK_CONTAINER(button),image);
    gtk_box_pack_start(GTK_BOX(vbox),button,FALSE,FALSE,0);
    g_signal_connect (G_OBJECT(button), "clicked",
		      G_CALLBACK(zoom_100), (gpointer)dev);

    /* Add zoom out button and create callbacks */
    image = gtk_image_new_from_stock(GTK_STOCK_ZOOM_OUT,
				     GTK_ICON_SIZE_SMALL_TOOLBAR);
    button = gtk_button_new();
    gtk_container_add(GTK_CONTAINER(button),image);
    gtk_box_pack_start(GTK_BOX(vbox),button,FALSE,FALSE,0);
    g_signal_connect (G_OBJECT(button), "clicked",
		      G_CALLBACK(zoom_out), (gpointer)dev);
    g_signal_connect(G_OBJECT(button), "key_release_event",
                         G_CALLBACK(key_release), (gpointer)dev);

    /* Add save button and create callbacks */
    if(plsc->plbuf_write) {
      image = gtk_image_new_from_stock(GTK_STOCK_SAVE,
				       GTK_ICON_SIZE_SMALL_TOOLBAR);
      button = gtk_button_new();
      gtk_container_add(GTK_CONTAINER(button),image);
      gtk_box_pack_start(GTK_BOX(vbox),button,FALSE,FALSE,0);
      g_signal_connect (G_OBJECT(button), "clicked",
			G_CALLBACK(filesel), (gpointer)dev);
    }
  } /* if(dev->window==NULL) */

  /* Put the canvas in a scrolled window */
  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), 
					GTK_WIDGET(canvas));

  /* Install the scrolled window in the notebook */
  gtk_notebook_append_page(GTK_NOTEBOOK(dev->notebook),scrolled_window, NULL);

  if(flag) {

    /* Set the focus on the notebook */
    gtk_window_set_focus(GTK_WINDOW(dev->window),dev->notebook);

    /* Retrieve the canvas width and height */
    width = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"canvas-width");
    height = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"canvas-height");

    /* Size the window */
    gtk_window_resize(GTK_WINDOW(dev->window),width*ZOOM100+65,
 		      height*ZOOM100+75);
  }

  /* Display everything */
  gtk_widget_show_all(dev->window);

#ifdef DEBUG_GCW_1
  gcw_debug("</gcw_install_canvas>\n");
#endif
}


/*--------------------------------------------------------------------------*\
 * gcw_set_device_size()
 *
 * Sets the page size for this device.
 *
 * Width and height are both measured in device coordinate units.
 *
 * Note that coordinates in driver-space are measured in device units,
 * which correspond to the pixel size on a typical screen.  The coordinates
 * reported and received from the PLplot core, however, are in virtual
 * coordinates, which is just a scaled version of the device coordinates.
 * This strategy is used so that the calculations in the PLplot
 * core are performed at reasonably high resolution.
 *
\*--------------------------------------------------------------------------*/

void gcw_set_device_size(PLINT width,PLINT height) {
  GcwPLdev* dev = plsc->dev;
  PLINT w, h;

  /* Set the number of virtual coordinate units per mm */
  plP_setpxl((PLFLT)VIRTUAL_PIXELS_PER_MM,(PLFLT)VIRTUAL_PIXELS_PER_MM);
  
  /* Set up physical limits of plotting device, in virtual coord units */
  w = (PLINT)(width*VSCALE);
  h = (PLINT)(height*VSCALE);
  plP_setphy((PLINT)0,w,(PLINT)0,h);
  
  /* Save the width and height for the device, in device units */
  dev->width = width;
  dev->height = height;
}

/*--------------------------------------------------------------------------*\
 * gcw_set_canvas_size()
 *
 * Sets the canvas size.  If resizing is not allowed, this function
 * ensures that the canvas size matches the physical page size.
 *
\*--------------------------------------------------------------------------*/

gint count = 0;

void gcw_set_canvas_size(GnomeCanvas* canvas,PLINT width,PLINT height)
{
  GcwPLdev* dev = plsc->dev;
  PLINT tmp;

  PLINT *w,*h;

  GdkGC *gc_new;
  GdkGCValues values;

  PLINT strm;

  GdkPixmap *background;

#ifdef DEBUG_GCW_1
  gcw_debug("<gcw_set_canvas_size>\n");
#endif

  /* Set the device size, if resizing is allowed. */
  if(dev->allow_resize) gcw_set_device_size(width,height);

  width = dev->width;
  height = dev->height;
  if(plsc->portrait) { /* Swap width and height of display */
    tmp=width;
    width=height;
    height=tmp;
  }

  /* The width and height need to be enlarged by 1 pixel for the canvas */
  width += 1;
  height += 1;

  /* Attach the width and height to the canvas */
  if((w=(PLINT*)malloc(sizeof(gint)))==NULL)
    plwarn("GCW driver <gcw_set_canvas_size>: Insufficient memory.");
  if((h=(PLINT*)malloc(sizeof(gint)))==NULL)
    plwarn("GCW driver <gcw_set_canvas_size>: Insufficient memory.");
  *w = width;
  *h = height;
  g_object_set_data(G_OBJECT(canvas),"canvas-width",(gpointer)w);
  g_object_set_data(G_OBJECT(canvas),"canvas-height",(gpointer)h);

  /* Size the canvas appropriately */
  gtk_widget_set_size_request(GTK_WIDGET(canvas),(gint)(width),
			      (gint)(height));

  /* Position the canvas appropriately */
  gnome_canvas_set_scroll_region(canvas,0.,(gdouble)(-height),
				 (gdouble)(width),1.);

  /* Set up the background pixmap */
  if(dev->background==NULL || dev->allow_resize) { 

    if(GDK_IS_PIXMAP(dev->background)) g_object_unref(dev->background);

    /* Why does this next *useless* command speed up the animation demo?
     * If we unref the allocated pixmaps, the benefit goes away!! */
/*     if(count<2) { */
/*       gdk_pixmap_new(NULL,width,height,gdk_visual_get_best_depth()); */
/*       printf("Count %d\n",count); */
/*       count++; */
/*     } */
/*     else { printf("Count %d\n",count); count ++; } */

    dev->background = gdk_pixmap_new(NULL,width,height,
		            gtk_widget_get_visual(GTK_WIDGET(canvas))->depth);
  }

  /* Set up the drawing context for the background pixmap */
  if(dev->gc==NULL || dev->allow_resize) {

    /* Maintain the old values for pen width, color, etc */
    if(GDK_IS_GC(dev->gc)) {
      gdk_gc_get_values(dev->gc,&values);
      gdk_gc_unref(dev->gc);
      dev->gc = gdk_gc_new_with_values(dev->background,&values,
				       GDK_GC_FOREGROUND | GDK_GC_LINE_WIDTH |
				       GDK_GC_LINE_STYLE | GDK_GC_CAP_STYLE |
				       GDK_GC_JOIN_STYLE);
    }
    else dev->gc = gdk_gc_new(dev->background);
  }

  /* Clear the background pixmap */
  gcw_clear_background(dev);

  /* Advance the page if we are allowing resizing.  This ensures that
   * the physical coordinate system is set up correctly.
   */
  if(dev->allow_resize) pladv(0);

#ifdef DEBUG_GCW_1
  gcw_debug("</gcw_set_canvas_size>\n");
#endif
}


/*--------------------------------------------------------------------------*\
 * gcw_set_canvas_zoom()
 *
 * Sets the zoom magnification on the canvas and resizes the widget
 * appropriately.
\*--------------------------------------------------------------------------*/

void gcw_set_canvas_zoom(GnomeCanvas* canvas,PLFLT magnification)
{
  gdouble curmag=1.,dum;

  PLINT width,height;

#ifdef DEBUG_GCW_1
  gcw_debug("<gcw_set_canvas_zoom>\n");
#endif

  /* Retrieve the device width and height */
  width = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"canvas-width");
  height = *(PLINT*)g_object_get_data(G_OBJECT(canvas),"canvas-height");

  /* Get the current magnification */
  gnome_canvas_c2w(canvas,1,0,&curmag,&dum);
  curmag = 1./curmag;

  /* Apply the new magnification */
  gnome_canvas_set_pixels_per_unit(canvas,magnification*curmag);

  /* Size the canvas appropriately */
  gtk_widget_set_size_request(GTK_WIDGET(canvas),
			      (gint)((width)*magnification*curmag),
			      (gint)((height)*magnification*curmag));

  /* Position the canvas appropriately */
  gnome_canvas_set_scroll_region(canvas,0.,(gdouble)(-height),
				 (gdouble)(width),1.);

#ifdef DEBUG_GCW_1
  gcw_debug("</gcw_set_canvas_zoom>\n");
#endif
}


/*--------------------------------------------------------------------------*\
 * gcw_use_text()
 *
 * Used to turn text usage on and off for the current device.
\*--------------------------------------------------------------------------*/

void gcw_use_text(PLINT use_text)
{
  GcwPLdev* dev = plsc->dev;

#ifdef DEBUG_GCW_1
  gcw_debug("<gcw_use_text>\n");
#endif

#ifdef HAVE_FREETYPE

  if(use_text) plsc->dev_text = 1; /* Allow text handling */
  else plsc->dev_text = 0; /* Disallow text handling */

#endif

#ifdef DEBUG_GCW_1
  gcw_debug("</gcw_use_text>\n");
#endif
}


/*--------------------------------------------------------------------------*\
 * gcw_use_pixmap()
 *
 * Used to turn pixmap usage on and off for the current device.
 * This is relevant for polygon fills (used during shading calls).
\*--------------------------------------------------------------------------*/

void gcw_use_pixmap(PLINT use_pixmap)
{
  GcwPLdev* dev = plsc->dev;

#ifdef DEBUG_GCW_1
  gcw_debug("<gcw_use_pixmap>\n");
#endif

  dev->use_pixmap = (gboolean)use_pixmap;

#ifdef DEBUG_GCW_1
  gcw_debug("</gcw_use_pixmap>\n");
#endif
}


/*--------------------------------------------------------------------------*\
 * gcw_use_hrshsym()
 *
 * Used to turn hershey symbol usage on and off for the current device.
\*--------------------------------------------------------------------------*/

void gcw_use_hrshsym(PLINT use_hrshsym)
{
  GcwPLdev* dev = plsc->dev;

#ifdef DEBUG_GCW_1
  gcw_debug("<gcw_use_hrshsym>\n");
#endif

  plsc->dev_hrshsym = use_hrshsym;

#ifdef DEBUG_GCW_1
  gcw_debug("</gcw_use_hrshsym>\n");
#endif
}


/*--------------------------------------------------------------------------*\
 * gcw_use_persistence
 *
 * Directs the GCW driver whether or not the subsequent drawing operations
 * should be persistent.
\*--------------------------------------------------------------------------*/

void gcw_use_persistence(PLINT use_persistence)
{
  GcwPLdev* dev = plsc->dev;

#ifdef DEBUG_GCW_1
  gcw_debug("<gcw_use_persistence>\n");
#endif

  dev->use_persistence = (gboolean)use_persistence;

#ifdef DEBUG_GCW_1
  gcw_debug("</gcw_use_persistence>\n");
#endif
}


syntax highlighted by Code2HTML, v. 0.9.1