/* gtkplotcanvas - gtkplot canvas widget for gtk+
 * Copyright 1999-2001  Adrian E. Feiguin <feiguin@ifir.edu.ar>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "gtkplotcanvas.h"
#include "gtkplotgdk.h"
#include "gtkplotps.h"
#include "gtkextra-marshal.h"

#define P_(string) string

#define DEFAULT_WIDTH 100
#define DEFAULT_HEIGHT 150
#define DEFAULT_MARKER_SIZE 6
#define SHADOW_WIDTH 3
#define GRAPH_MASK    (GDK_EXPOSURE_MASK |              \
                       GDK_POINTER_MOTION_MASK |        \
                       GDK_POINTER_MOTION_HINT_MASK |   \
                       GDK_BUTTON_PRESS_MASK |          \
                       GDK_BUTTON_RELEASE_MASK)

enum {
  ARG_CANVAS_0,
  ARG_CANVAS_FLAGS,
  ARG_CANVAS_WIDTH,
  ARG_CANVAS_HEIGHT,
  ARG_CANVAS_MAGNIFICATION,
  ARG_CANVAS_SHOW_GRID,
  ARG_CANVAS_GRID_STEP,
  ARG_CANVAS_LINE_GRID,
  ARG_CANVAS_COLOR_BG,
  ARG_CANVAS_TRANSPARENT,
};

enum {
  ARG_CHILD_0,
  ARG_CHILD_RX1,
  ARG_CHILD_RY1,
  ARG_CHILD_RX2,
  ARG_CHILD_RY2,
  ARG_CHILD_ALLOCATION,
  ARG_CHILD_MIN_WIDTH,
  ARG_CHILD_MIN_HEIGHT,
  ARG_CHILD_STATE,
  ARG_CHILD_FLAGS,
  ARG_CHILD_SELECTION,
  ARG_CHILD_SELECTION_MODE,
};

static void gtk_plot_canvas_class_init 		(GtkPlotCanvasClass *klass);
static void gtk_plot_canvas_init 		(GtkPlotCanvas *plot_canvas);
static void gtk_plot_canvas_set_property        (GObject *object,
                                                 guint            prop_id,
                                                 const GValue          *value,
                                                 GParamSpec      *pspec);
static void gtk_plot_canvas_get_property        (GObject *object,
                                                 guint            prop_id,
                                                 GValue          *value,
                                                 GParamSpec      *pspec);
static void gtk_plot_canvas_child_class_init 	(GtkPlotCanvasChildClass *klass);
static void gtk_plot_canvas_child_init 		(GtkPlotCanvasChild *child);
static void gtk_plot_canvas_child_set_property  (GObject *object,
                                                 guint            prop_id,
                                                 const GValue          *value,
                                                 GParamSpec      *pspec);
static void gtk_plot_canvas_child_get_property  (GObject *object,
                                                 guint            prop_id,
                                                 GValue          *value,
                                                 GParamSpec      *pspec);
static void gtk_plot_canvas_destroy 		(GtkObject *object);
static void gtk_plot_canvas_map                 (GtkWidget *widget);
static void gtk_plot_canvas_size_request        (GtkWidget *widget,
                                                 GtkRequisition *requisition);
static gint gtk_plot_canvas_motion 		(GtkWidget *widget,
                                                 GdkEventMotion *event);
static gint gtk_plot_canvas_button_press	(GtkWidget *widget,
                                                 GdkEventButton *event);
static gint gtk_plot_canvas_key_press		(GtkWidget *widget,
                                                 GdkEventKey *event);
static gint gtk_plot_canvas_button_release	(GtkWidget *widget,
                                                 GdkEventButton *event);
static gint gtk_plot_canvas_focus_in		(GtkWidget *widget,
                                                 GdkEventFocus *event);
static gint gtk_plot_canvas_focus_out		(GtkWidget *widget,
                                                 GdkEventFocus *event);
static void gtk_plot_canvas_child_size_allocate (GtkPlotCanvas *canvas,
						 GtkPlotCanvasChild *child);
static GtkPlotCanvasPos gtk_plot_canvas_child_button_press
						(GtkPlotCanvas *canvas,
						 GtkPlotCanvasChild *child,
						 gint x, gint y);
static void gtk_plot_canvas_child_button_release(GtkPlotCanvas *canvas,
                                                 GtkPlotCanvasChild *child);
/* Drawing functions */
static gint gtk_plot_canvas_expose              (GtkWidget *widget,
                                                 GdkEventExpose *event);
static void gtk_plot_canvas_create_pixmap       (GtkWidget *widget,
                                                 gint width, gint height);
static void gtk_plot_canvas_child_draw		(GtkPlotCanvas *canvas,
						 GtkPlotCanvasChild *child);
static void gtk_plot_canvas_child_draw_selection(GtkPlotCanvas *canvas,
						 GtkPlotCanvasChild *child,
                				 GtkAllocation area);
static void draw_selection 			(GtkPlotCanvas *canvas,
						 GtkPlotCanvasChild *child,
                				 GtkAllocation area);
static void draw_marker				(GtkPlotCanvas *canvas,
						 GdkGC *gc, gint x, gint y);

static void gtk_plot_canvas_draw_grid		(GtkPlotCanvas *canvas);
static void gtk_plot_canvas_child_draw		(GtkPlotCanvas *canvas,
                           			 GtkPlotCanvasChild *child);
/* Auxiliary functions */
GtkPlotCanvasPos 	possible_selection	(GtkAllocation area,
						 gint x, gint y);
extern inline gint roundint                     (gdouble x);


/* Signals */

extern void
_gtkextra_signal_emit(GtkObject *object, guint signal_id, ...);

enum {
        SELECT_ITEM,
        MOVE_ITEM,
        RESIZE_ITEM,
        DELETE_ITEM,
        ADD_ITEM,
        SELECT_REGION,
        CHANGED,
        LAST_SIGNAL
};

typedef gboolean (*GtkPlotCanvasSignal1) (GtkObject *object,
                                          gpointer arg1,
					  gdouble arg2,
					  gdouble arg3,
                                    	  gpointer user_data);

typedef gboolean (*GtkPlotCanvasSignal2) (GtkObject *object,
                                          gpointer arg1,
					  gpointer arg2,
                                    	  gpointer user_data);

typedef gboolean (*GtkPlotCanvasSignal3) (GtkObject *object,
                                          gdouble arg1,
                                          gdouble arg2,
                                          gdouble arg3,
                                          gdouble arg4,
                                    	  gpointer user_data);

static GtkFixedClass *parent_class = NULL;
static guint canvas_signals[LAST_SIGNAL] = {0};

GtkType
gtk_plot_canvas_get_type (void)
{
  static GtkType plot_canvas_type = 0;

  if (!plot_canvas_type)
    {
      GtkTypeInfo plot_canvas_info =
      {
	"GtkPlotCanvas",
	sizeof (GtkPlotCanvas),
	sizeof (GtkPlotCanvasClass),
	(GtkClassInitFunc) gtk_plot_canvas_class_init,
	(GtkObjectInitFunc) gtk_plot_canvas_init,
	/* reserved 1*/ NULL,
        /* reserved 2 */ NULL,
        (GtkClassInitFunc) NULL,
      };

      plot_canvas_type = gtk_type_unique (gtk_fixed_get_type(), &plot_canvas_info);
    }
  return plot_canvas_type;
}

GtkType
gtk_plot_canvas_child_get_type (void)
{
  static GtkType plot_canvas_child_type = 0;

  if (!plot_canvas_child_type)
    {
      GtkTypeInfo plot_canvas_child_info =
      {
       "GtkPlotCanvasChild",
       sizeof(GtkPlotCanvasChild),
       sizeof(GtkPlotCanvasChildClass),
       (GtkClassInitFunc) gtk_plot_canvas_child_class_init,
       (GtkObjectInitFunc) gtk_plot_canvas_child_init,
       /* reserved 1*/ NULL,
        /* reserved 2 */ NULL,
        (GtkClassInitFunc) NULL,
      };

      plot_canvas_child_type = gtk_type_unique (gtk_object_get_type(),
                                              &plot_canvas_child_info);
    }
  return plot_canvas_child_type;
}

static void
gtk_plot_canvas_child_class_init (GtkPlotCanvasChildClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);

  klass->size_allocate = gtk_plot_canvas_child_size_allocate;
  klass->draw = NULL;
  klass->unselect = NULL;
  klass->move = NULL;
  klass->move_resize = NULL;
  klass->draw_selection = draw_selection;
  klass->button_press = gtk_plot_canvas_child_button_press;
  klass->button_release = gtk_plot_canvas_child_button_release;
  klass->set_magnification = NULL;

  gobject_class->get_property = gtk_plot_canvas_child_get_property;
  gobject_class->set_property = gtk_plot_canvas_child_set_property;

  g_object_class_install_property (gobject_class,
                           ARG_CHILD_RX1,
  g_param_spec_double ("rx1",
                           P_(""),
                           P_(""),
                           -G_MAXDOUBLE,G_MAXDOUBLE,0.0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_RY1,
  g_param_spec_double ("ry1",
                           P_(""),
                           P_(""),
                           -G_MAXDOUBLE,G_MAXDOUBLE,0.0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_RX2,
  g_param_spec_double ("rx2",
                           P_(""),
                           P_(""),
                           -G_MAXDOUBLE,G_MAXDOUBLE,0.0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_RY2,
  g_param_spec_double ("ry2",
                           P_(""),
                           P_(""),
                           -G_MAXDOUBLE,G_MAXDOUBLE,0.0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_ALLOCATION,
  g_param_spec_pointer ("allocation",
                           P_(""),
                           P_(""),
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_MIN_WIDTH,
  g_param_spec_int ("min_width",
                           P_(""),
                           P_(""),
                           -1,G_MAXINT,0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_MIN_HEIGHT,
  g_param_spec_int ("min_height",
                           P_(""),
                           P_(""),
                           -1,G_MAXINT,0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_STATE,
  g_param_spec_int ("state",
                           P_(""),
                           P_(""),
                           0,G_MAXINT,0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_FLAGS,
  g_param_spec_int ("flags",
                           P_(""),
                           P_(""),
                           0,G_MAXINT,0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_SELECTION,
  g_param_spec_int ("selection",
                           P_(""),
                           P_(""),
                           0,G_MAXINT,0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CHILD_SELECTION_MODE,
  g_param_spec_int ("selection_mode",
                           P_(""),
                           P_(""),
                           0,G_MAXINT,0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));

}

static void
gtk_plot_canvas_child_get_property (GObject      *object,
                                    guint            prop_id,
                                    GValue          *value,
                                    GParamSpec      *pspec)
{
  GtkPlotCanvasChild *child = GTK_PLOT_CANVAS_CHILD(object);

  switch(prop_id){
    case ARG_CHILD_RX1:
      g_value_set_double(value, child->rx1);
      break;
    case ARG_CHILD_RY1:
      g_value_set_double(value, child->ry1);
      break;
    case ARG_CHILD_RX2:
      g_value_set_double(value, child->rx2);
      break;
    case ARG_CHILD_RY2:
      g_value_set_double(value, child->ry2);
      break;
    case ARG_CHILD_ALLOCATION:
      g_value_set_pointer(value, &child->allocation);
      break;
    case ARG_CHILD_MIN_WIDTH:
      g_value_set_int(value, child->min_width);
      break;
    case ARG_CHILD_MIN_HEIGHT:
      g_value_set_int(value, child->min_height);
      break;
    case ARG_CHILD_STATE:
      g_value_set_int(value, child->state);
      break;
    case ARG_CHILD_FLAGS:
      g_value_set_int(value, child->flags);
      break;
    case ARG_CHILD_SELECTION:
      g_value_set_int(value, child->selection);
      break;
    case ARG_CHILD_SELECTION_MODE:
      g_value_set_int(value, child->mode);
      break;
  }
}

static void
gtk_plot_canvas_child_set_property (GObject      *object,
                                    guint            prop_id,
                                    const GValue          *value,
                                    GParamSpec      *pspec)
{
  GtkPlotCanvasChild *child = GTK_PLOT_CANVAS_CHILD(object);

  switch(prop_id){
    case ARG_CHILD_RX1:
      child->rx1 = g_value_get_double(value);
      break;
    case ARG_CHILD_RY1:
      child->ry1 = g_value_get_double(value);
      break;
    case ARG_CHILD_RX2:
      child->rx2 = g_value_get_double(value);
      break;
    case ARG_CHILD_RY2:
      child->ry2 = g_value_get_double(value);
      break;
    case ARG_CHILD_ALLOCATION:
      child->allocation = *((GtkAllocation *)g_value_get_pointer(value));
      break;
    case ARG_CHILD_MIN_WIDTH:
      child->min_width = g_value_get_int(value);
      break;
    case ARG_CHILD_MIN_HEIGHT:
      child->min_height = g_value_get_int(value);
      break;
    case ARG_CHILD_STATE:
      child->state = g_value_get_int(value);
      break;
    case ARG_CHILD_FLAGS:
      child->flags = g_value_get_int(value);
      break;
    case ARG_CHILD_SELECTION:
      child->selection = g_value_get_int(value);
      break;
    case ARG_CHILD_SELECTION_MODE:
      child->mode = g_value_get_int(value);
      break;
  }
}

static void
gtk_plot_canvas_child_init(GtkPlotCanvasChild *child)
{
  child->flags = GTK_PLOT_CANVAS_CAN_MOVE |
                 GTK_PLOT_CANVAS_CAN_RESIZE;

  child->min_width = -1;
  child->min_height = -1;

  child->selection = GTK_PLOT_CANVAS_SELECT_MARKERS;
  child->mode = GTK_PLOT_CANVAS_SELECT_CLICK_2;
  child->parent = NULL;
}

static void
gtk_plot_canvas_class_init (GtkPlotCanvasClass *klass)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;
  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);

  parent_class = gtk_type_class (gtk_fixed_get_type ());

  object_class = (GtkObjectClass *) klass;
  widget_class = (GtkWidgetClass *) klass;
  container_class = (GtkContainerClass *) klass;

  canvas_signals[SELECT_ITEM] =
    gtk_signal_new ("select_item",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE(object_class),
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, select_item),
                    gtkextra_BOOLEAN__BOXED_BOXED,
                    GTK_TYPE_BOOL, 2, GDK_TYPE_EVENT,
                    GTK_TYPE_PLOT_CANVAS_CHILD);

  canvas_signals[MOVE_ITEM] =
    gtk_signal_new ("move_item",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE(object_class),
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, move_item),
                    gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE,
                    GTK_TYPE_BOOL, 3, GTK_TYPE_PLOT_CANVAS_CHILD,
                    GTK_TYPE_DOUBLE,
                    GTK_TYPE_DOUBLE);

  canvas_signals[RESIZE_ITEM] =
    gtk_signal_new ("resize_item",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE(object_class),
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, resize_item),
                    gtkextra_BOOLEAN__BOXED_DOUBLE_DOUBLE,
                    GTK_TYPE_BOOL, 3, GTK_TYPE_PLOT_CANVAS_CHILD,
                    GTK_TYPE_DOUBLE,
                    GTK_TYPE_DOUBLE);

  canvas_signals[ADD_ITEM] =
    gtk_signal_new ("add_item",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE(object_class),
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, add_item),
                    gtkextra_VOID__POINTER,
                    GTK_TYPE_NONE, 1,
                    GTK_TYPE_PLOT_CANVAS_CHILD);

  canvas_signals[DELETE_ITEM] =
    gtk_signal_new ("delete_item",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE(object_class),
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, delete_item),
                    gtkextra_BOOL__POINTER,
                    GTK_TYPE_BOOL, 1,
                    GTK_TYPE_PLOT_CANVAS_CHILD);

  canvas_signals[SELECT_REGION] =
    gtk_signal_new ("select_region",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE(object_class),
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, select_region),
                    gtkextra_VOID__DOUBLE_DOUBLE_DOUBLE_DOUBLE,
                    GTK_TYPE_NONE, 4,
                    GTK_TYPE_DOUBLE, GTK_TYPE_DOUBLE,
                    GTK_TYPE_DOUBLE, GTK_TYPE_DOUBLE);

  canvas_signals[CHANGED] =
    gtk_signal_new("changed",
                   GTK_RUN_LAST,
                   GTK_CLASS_TYPE(object_class),
                   GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, changed),
                   gtkextra_VOID__VOID,
                   GTK_TYPE_NONE, 0);

  object_class->destroy = gtk_plot_canvas_destroy;

  gobject_class->get_property = gtk_plot_canvas_get_property;
  gobject_class->set_property = gtk_plot_canvas_set_property;

  widget_class->map = gtk_plot_canvas_map;
  widget_class->expose_event = gtk_plot_canvas_expose;
  widget_class->size_request = gtk_plot_canvas_size_request;
  widget_class->focus_in_event = gtk_plot_canvas_focus_in;
  widget_class->focus_out_event = gtk_plot_canvas_focus_out;
  widget_class->motion_notify_event = gtk_plot_canvas_motion;
  widget_class->button_press_event = gtk_plot_canvas_button_press;
  widget_class->button_release_event = gtk_plot_canvas_button_release;
  widget_class->key_press_event = gtk_plot_canvas_key_press;

  klass->move_item = NULL;
  klass->resize_item = NULL;
  klass->select_item = NULL;
  klass->delete_item = NULL;
  klass->select_region = NULL;

  g_object_class_install_property (gobject_class,
                           ARG_CANVAS_FLAGS,
  g_param_spec_int ("flags",
                           P_(""),
                           P_(""),
                           0,G_MAXINT,0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CANVAS_MAGNIFICATION,
  g_param_spec_double ("magnification",
                           P_(""),
                           P_(""),
                           0,G_MAXDOUBLE,1.0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CANVAS_WIDTH,
  g_param_spec_int ("width",
                           P_(""),
                           P_(""),
                           0,G_MAXINT,1,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CANVAS_HEIGHT,
  g_param_spec_int ("height",
                           P_(""),
                           P_(""),
                           0,G_MAXINT,1,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CANVAS_SHOW_GRID,
  g_param_spec_boolean ("show_grid",
                           P_(""),
                           P_(""),
                           FALSE,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CANVAS_GRID_STEP,
  g_param_spec_double ("grid_step",
                           P_(""),
                           P_(""),
                           0,G_MAXDOUBLE,0.0,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CANVAS_LINE_GRID,
  g_param_spec_pointer ("line_grid",
                           P_(""),
                           P_(""),
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CANVAS_COLOR_BG,
  g_param_spec_pointer ("color_bg",
                           P_(""),
                           P_(""),
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
  g_object_class_install_property (gobject_class,
                           ARG_CANVAS_TRANSPARENT,
  g_param_spec_boolean ("transparent",
                           P_(""),
                           P_(""),
                           TRUE,
                           G_PARAM_READABLE|G_PARAM_WRITABLE));
}

static void
gtk_plot_canvas_get_property (GObject      *object,
                              guint            prop_id,
                              GValue          *value,
                              GParamSpec      *pspec)
{
  GtkPlotCanvas *canvas = GTK_PLOT_CANVAS(object);

  switch(prop_id){
    case ARG_CANVAS_FLAGS:
      g_value_set_int(value, canvas->flags);
      break;
    case ARG_CANVAS_WIDTH:
      g_value_set_int(value, canvas->width);
      break;
    case ARG_CANVAS_HEIGHT:
      g_value_set_int(value, canvas->height);
      break;
    case ARG_CANVAS_MAGNIFICATION:
      g_value_set_double(value, canvas->magnification);
      break;
    case ARG_CANVAS_SHOW_GRID:
      g_value_set_boolean(value, canvas->show_grid);
      break;
    case ARG_CANVAS_GRID_STEP:
      g_value_set_double(value, canvas->grid_step);
      break;
    case ARG_CANVAS_LINE_GRID:
      g_value_set_pointer(value, &canvas->grid);
      break;
    case ARG_CANVAS_COLOR_BG:
      g_value_set_pointer(value, &canvas->background);
      break;
    case ARG_CANVAS_TRANSPARENT:
      g_value_set_boolean(value, canvas->transparent);
      break;
  }
}

static void
gtk_plot_canvas_set_property (GObject      *object,
                              guint            prop_id,
                              const GValue          *value,
                              GParamSpec      *pspec)
{
  GtkPlotCanvas *canvas;

  canvas = GTK_PLOT_CANVAS (object);

  switch(prop_id){
    case ARG_CANVAS_FLAGS:
      canvas->flags = g_value_get_int(value);
      break;
    case ARG_CANVAS_WIDTH:
      canvas->width = g_value_get_int(value);
      break;
    case ARG_CANVAS_HEIGHT:
      canvas->height = g_value_get_int(value);
      break;
    case ARG_CANVAS_MAGNIFICATION:
      canvas->magnification = g_value_get_double(value);
      break;
    case ARG_CANVAS_SHOW_GRID:
      canvas->show_grid = g_value_get_boolean(value);
      break;
    case ARG_CANVAS_GRID_STEP:
       canvas->grid_step = g_value_get_double(value);
      break;
    case ARG_CANVAS_LINE_GRID:
      canvas->grid = *((GtkPlotLine *)g_value_get_pointer(value));
      break;
    case ARG_CANVAS_COLOR_BG:
      canvas->background = *((GdkColor *)g_value_get_pointer(value));
      break;
    case ARG_CANVAS_TRANSPARENT:
      canvas->transparent = g_value_get_boolean(value);
      break;
  }
}

static void
gtk_plot_canvas_init (GtkPlotCanvas *plot_canvas)
{
  GtkWidget *widget;
  GdkColor color;

  widget = GTK_WIDGET(plot_canvas);
  GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS);

  gdk_color_black(gtk_widget_get_colormap(widget), &widget->style->black);
  gdk_color_white(gtk_widget_get_colormap(widget), &widget->style->white);

  gtk_widget_set_events (widget, gtk_widget_get_events(widget)|
                         GRAPH_MASK);

  plot_canvas->freeze_count = 0;
  plot_canvas->cursor = gdk_cursor_new(GDK_TOP_LEFT_ARROW);

  plot_canvas->background = widget->style->white;
  plot_canvas->transparent = TRUE;

  plot_canvas->flags = 0;
  plot_canvas->state = GTK_STATE_NORMAL;
  plot_canvas->action = GTK_PLOT_CANVAS_ACTION_INACTIVE;
  plot_canvas->magnification = 1.;

  plot_canvas->show_grid = FALSE;
  plot_canvas->grid_step = 20.;
  plot_canvas->grid.line_style = GTK_PLOT_LINE_SOLID;
  plot_canvas->grid.line_width = 0;

  gdk_color_parse("grey90", &color);
  gdk_color_alloc(gdk_colormap_get_system(), &color);
  plot_canvas->grid.color = color;

  plot_canvas->drag_x = plot_canvas->drag_y = 0;
  plot_canvas->pointer_x = plot_canvas->pointer_y = 0;

  plot_canvas->childs = NULL;

  plot_canvas->width = DEFAULT_WIDTH;
  plot_canvas->height = DEFAULT_HEIGHT;
  plot_canvas->pixmap_width = DEFAULT_WIDTH;
  plot_canvas->pixmap_height = DEFAULT_HEIGHT;

  gtk_psfont_init();

  plot_canvas->pc = NULL;
  gtk_plot_canvas_set_pc(plot_canvas, NULL);

  plot_canvas->pixmap = NULL;
}

void
gtk_plot_canvas_set_pc(GtkPlotCanvas *canvas, GtkPlotPC *pc)
{
  if(canvas->pc)
    gtk_object_unref(GTK_OBJECT(canvas->pc));

  if(!pc){
    canvas->pc = GTK_PLOT_PC(gtk_plot_gdk_new(GTK_WIDGET(canvas)));
    gtk_object_ref(GTK_OBJECT(canvas->pc));
    gtk_object_sink(GTK_OBJECT(canvas->pc));
  } else {
    canvas->pc = pc;
    gtk_object_ref(GTK_OBJECT(pc));
    gtk_object_sink(GTK_OBJECT(pc));
  }

  if(canvas->pc && GTK_IS_PLOT_GDK(canvas->pc)){
       GTK_PLOT_GDK(canvas->pc)->drawable = canvas->pixmap;
  }
  gtk_plot_pc_set_viewport(canvas->pc, canvas->pixmap_width, canvas->pixmap_height);
}

GtkWidget*
gtk_plot_canvas_new (gint width, gint height, gdouble magnification)
{
  GtkPlotCanvas *plot_canvas;

  plot_canvas = gtk_type_new (gtk_plot_canvas_get_type ());

  gtk_plot_canvas_construct(GTK_PLOT_CANVAS(plot_canvas),
			    width, height, magnification);

  return GTK_WIDGET (plot_canvas);
}

void
gtk_plot_canvas_construct(GtkPlotCanvas *plot_canvas,
			  gint width, gint height, gdouble magnification)
{
  gdouble m = magnification;

  plot_canvas->width = width;
  plot_canvas->height = height;
  plot_canvas->pixmap_width = roundint(width * m);
  plot_canvas->pixmap_height = roundint(height * m);
  gtk_plot_canvas_set_magnification(plot_canvas, m);

  gtk_fixed_set_has_window (GTK_FIXED(plot_canvas), TRUE);
}

void
gtk_plot_canvas_freeze(GtkPlotCanvas *canvas)
{
  canvas->freeze_count++;
}

void
gtk_plot_canvas_thaw(GtkPlotCanvas *canvas)
{
  if(canvas->freeze_count == 0) return;
  canvas->freeze_count--;
}

static void
gtk_plot_canvas_destroy (GtkObject *object)
{
  GtkPlotCanvas *plot_canvas;
  GList *list;
  gboolean veto = TRUE;

  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_PLOT_CANVAS (object));

  plot_canvas = GTK_PLOT_CANVAS (object);

  list = plot_canvas->childs;
  while(list){
    gtk_signal_emit(GTK_OBJECT(plot_canvas), canvas_signals[DELETE_ITEM],
                    GTK_PLOT_CANVAS_CHILD(list->data), &veto);

    gtk_object_unref(GTK_OBJECT(list->data));

    plot_canvas->childs = g_list_remove_link(plot_canvas->childs, list);
    g_list_free_1(list);

    list = plot_canvas->childs;
  }

  plot_canvas->childs = NULL;

  if( plot_canvas->cursor ){
     gdk_cursor_destroy(plot_canvas->cursor);
     plot_canvas->cursor = NULL;
  }
  if( plot_canvas->pc ){
     gtk_object_unref(GTK_OBJECT(plot_canvas->pc));
     plot_canvas->pc = NULL;
  }

  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);

  gtk_psfont_unref();
}

void
gtk_plot_canvas_remove_child(GtkPlotCanvas *canvas, GtkPlotCanvasChild *child)
{
  GList *list;

  gtk_plot_canvas_cancel_action(canvas);

  list = canvas->childs;
  while(list){
   if(list->data == child){
      gboolean veto = TRUE;

      _gtkextra_signal_emit(GTK_OBJECT(canvas), canvas_signals[DELETE_ITEM],
                      child, &veto);

      if(veto){
        child->parent = NULL;
        gtk_object_unref(GTK_OBJECT(child));
        canvas->childs = g_list_remove_link(canvas->childs, list);
        g_list_free_1(list);
      }
      break;
   }
   list = list->next;
  }
}

/*
static void
gtk_plot_canvas_draw (GtkWidget *widget, GdkRectangle *area)
{
  GtkPlotCanvas *canvas;

  canvas = GTK_PLOT_CANVAS(widget);

  if(!GTK_WIDGET_REALIZED(widget)) return;
  if(!canvas->pixmap) return;

  GTK_WIDGET_CLASS(parent_class)->draw(widget, area);

  gtk_plot_canvas_paint(canvas);
  gtk_plot_canvas_refresh(canvas);
}
*/

void
gtk_plot_canvas_paint (GtkPlotCanvas *canvas)
{
  GtkWidget *widget;
  GList *childs;

  widget = GTK_WIDGET(canvas);

  if(GTK_WIDGET_REALIZED(widget) && !canvas->pixmap) return;
  if(canvas->freeze_count > 0) return;

  if(!gtk_plot_pc_init(canvas->pc)) return;

  gtk_plot_pc_gsave(canvas->pc);

  if(!GTK_IS_PLOT_PS(canvas->pc) || !canvas->transparent){
    if(canvas->transparent){
      GdkColor white;
      gdk_color_white(gtk_widget_get_colormap(GTK_WIDGET(canvas)), &white);
      gtk_plot_pc_set_color(canvas->pc, &white);
    } else
      gtk_plot_pc_set_color(canvas->pc, &canvas->background);
    gtk_plot_pc_draw_rectangle(canvas->pc,
                              TRUE,
                              0,0,canvas->pixmap_width, canvas->pixmap_height);
  }

  gtk_plot_canvas_draw_grid(canvas);

  childs = canvas->childs;
  while(childs)
   {
     GtkPlotCanvasChild *child;

     child = GTK_PLOT_CANVAS_CHILD(childs->data);
     gtk_plot_canvas_child_draw(canvas, child);
     childs = childs->next;
   }

  gtk_plot_pc_grestore(canvas->pc);
  gtk_plot_pc_leave(canvas->pc);
}

void
gtk_plot_canvas_refresh(GtkPlotCanvas *canvas)
{
  GList *children = NULL;
  GtkFixed *fixed = GTK_FIXED(canvas);

  if(!GTK_WIDGET_REALIZED(GTK_WIDGET(canvas))) return;
  if(!canvas->pixmap) return;

  gdk_draw_drawable(GTK_WIDGET(canvas)->window,
                  GTK_WIDGET(canvas)->style->fg_gc[GTK_STATE_NORMAL],
                  canvas->pixmap,
                  0, 0,
                  0, 0,
                  -1, -1);

  children = fixed->children;
  while (children)
    {
      GtkFixedChild *child;
      child = children->data;
      gtk_widget_queue_draw(child->widget);
      children = children->next;
    }

}

static void
gtk_plot_canvas_draw_grid(GtkPlotCanvas *canvas)
{
  gdouble x, y;

  if(!canvas->pixmap) return;
  if(!canvas->show_grid) return;

  if(!GTK_IS_PLOT_GDK(canvas->pc)) return;

  gtk_plot_canvas_set_line_attributes(canvas, canvas->grid);

  for(x = 0; x < canvas->pixmap_width; x += canvas->grid_step)
      gtk_plot_pc_draw_line(canvas->pc,
                            roundint(x), 0, roundint(x), canvas->pixmap_height);

  for(y = 0; y < canvas->pixmap_height; y += canvas->grid_step)
      gtk_plot_pc_draw_line(canvas->pc,
                            0, roundint(y), canvas->pixmap_width, roundint(y));
}

static void
gtk_plot_canvas_map(GtkWidget *widget)
{
  GtkPlotCanvas *plot_canvas;

  plot_canvas=GTK_PLOT_CANVAS(widget);

  GTK_WIDGET_CLASS(parent_class)->map(widget);

  if(!plot_canvas->pixmap){
      gtk_plot_canvas_create_pixmap(widget,
                                    plot_canvas->pixmap_width,
                                    plot_canvas->pixmap_height);
  }
  gtk_plot_canvas_paint(plot_canvas);
  gdk_window_set_cursor(widget->window, plot_canvas->cursor);
}

static gint
gtk_plot_canvas_key_press(GtkWidget *widget, GdkEventKey *key)
{
  GtkPlotCanvas *canvas = GTK_PLOT_CANVAS(widget);

  switch(key->keyval){
    case GDK_Escape:
      gtk_plot_canvas_cancel_action(canvas);
      break;
    default:
      break;
  }

  return TRUE;
}

static gint
gtk_plot_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
{
  GtkPlotCanvas *canvas;
  GtkAllocation area;
  gint x, y;
  gint new_x = 0, new_y = 0;
  gint new_width = 0, new_height = 0;
  gint cursor = GDK_TOP_LEFT_ARROW;
  gint pivot_x, pivot_y;

  canvas = GTK_PLOT_CANVAS(widget);
  gtk_widget_get_pointer(widget, &x, &y);

  if(canvas->active_item && canvas->active_item->flags == GTK_PLOT_CANVAS_FROZEN) return TRUE;

  if(canvas->active_item){
    area = canvas->active_item->drag_area;
    new_x = area.x;
    new_y = area.y;
    new_width = area.width;
    new_height = area.height;
  }
  pivot_x = x;
  pivot_y = y;

  if(canvas->action == GTK_PLOT_CANVAS_ACTION_INACTIVE)
       cursor = GDK_TOP_LEFT_ARROW;
  else if(canvas->action == GTK_PLOT_CANVAS_ACTION_DRAG)
       cursor = GDK_FLEUR;
  else
       switch(canvas->drag_point){
            case GTK_PLOT_CANVAS_TOP_LEFT:
                 cursor = GDK_UL_ANGLE;
                 pivot_x = area.x + area.width;
                 pivot_y = area.y + area.height;
                 break;
            case GTK_PLOT_CANVAS_TOP_RIGHT:
                 cursor = GDK_UR_ANGLE;
                 pivot_x = area.x;
                 pivot_y = area.y + area.height;
                 break;
            case GTK_PLOT_CANVAS_TOP:
                 cursor = GDK_TOP_SIDE;
                 pivot_y = area.y + area.height;
                 break;
            case GTK_PLOT_CANVAS_BOTTOM_LEFT:
                 cursor = GDK_LL_ANGLE;
                 pivot_x = area.x + area.width;
                 pivot_y = area.y;
                 break;
            case GTK_PLOT_CANVAS_BOTTOM_RIGHT:
                 cursor = GDK_LR_ANGLE;
                 pivot_x = area.x;
                 pivot_y = area.y;
                 break;
            case GTK_PLOT_CANVAS_BOTTOM:
                 cursor = GDK_BOTTOM_SIDE;
                 pivot_y = area.y;
                 break;
            case GTK_PLOT_CANVAS_LEFT:
                 cursor = GDK_LEFT_SIDE;
                 pivot_x = area.x + area.width;
                 break;
            case GTK_PLOT_CANVAS_RIGHT:
                 cursor = GDK_RIGHT_SIDE;
                 pivot_x = area.x;
                 break;
            default:
                 cursor = GDK_TOP_LEFT_ARROW;
       }

  if(cursor != canvas->cursor->type){
         gdk_cursor_destroy(canvas->cursor);
         canvas->cursor = gdk_cursor_new(cursor);
         gdk_window_set_cursor(widget->window, canvas->cursor);
  }


  if(canvas->action == GTK_PLOT_CANVAS_ACTION_INACTIVE) return TRUE;

  switch(canvas->action){
     case GTK_PLOT_CANVAS_ACTION_DRAG:
       if(canvas->active_item && canvas->active_item->flags & GTK_PLOT_CANVAS_CAN_MOVE){
         gint dx, dy;

         gtk_plot_canvas_child_draw_selection(canvas, canvas->active_item, canvas->drag_area);
         canvas->pointer_x = x;
         canvas->pointer_y = y;
         dx = x - canvas->drag_x;
         dy = y - canvas->drag_y;
         area.x = canvas->active_item->drag_area.x + dx;
         area.y = canvas->active_item->drag_area.y + dy;
         gtk_plot_canvas_child_draw_selection(canvas, canvas->active_item, area);
         canvas->drag_area = area;
       }
       break;
     case GTK_PLOT_CANVAS_ACTION_RESIZE:
       switch(canvas->drag_point){
            case GTK_PLOT_CANVAS_TOP_LEFT:
            case GTK_PLOT_CANVAS_TOP_RIGHT:
               if(canvas->active_item && canvas->active_item->flags & GTK_PLOT_CANVAS_CAN_RESIZE){
                    new_x = MIN(x, pivot_x);
                    new_width = abs(x - pivot_x);
               }
            case GTK_PLOT_CANVAS_TOP:
               if(canvas->active_item && canvas->active_item->flags & GTK_PLOT_CANVAS_CAN_RESIZE){
                    new_y = MIN(y, pivot_y);
                    new_height = abs(y - pivot_y);
               }
               gdk_cursor_destroy(canvas->cursor);
               canvas->cursor = gdk_cursor_new(cursor);
               gdk_window_set_cursor(widget->window, canvas->cursor);
               break;
            case GTK_PLOT_CANVAS_BOTTOM_LEFT:
            case GTK_PLOT_CANVAS_BOTTOM_RIGHT:
               if(canvas->active_item && canvas->active_item->flags & GTK_PLOT_CANVAS_CAN_RESIZE){
                    new_x = MIN(x, pivot_x);
                    new_width = abs(x - pivot_x);
               }
            case GTK_PLOT_CANVAS_BOTTOM:
               if(canvas->active_item && canvas->active_item->flags & GTK_PLOT_CANVAS_CAN_RESIZE){
                    new_y = MIN(y, pivot_y);
                    new_height = abs(y - pivot_y);
               }
               break;
            case GTK_PLOT_CANVAS_LEFT:
            case GTK_PLOT_CANVAS_RIGHT:
               if(canvas->active_item && canvas->active_item->flags & GTK_PLOT_CANVAS_CAN_RESIZE){
                    new_x = MIN(x, pivot_x);
                    new_width = abs(x - pivot_x);
               }
               break;
            case GTK_PLOT_CANVAS_IN:
            case GTK_PLOT_CANVAS_OUT:
            default:
               break;
       }


       if(canvas->active_item && new_width >= canvas->active_item->min_width &&
          new_height >= canvas->active_item->min_height){

                gtk_plot_canvas_child_draw_selection(canvas, canvas->active_item, canvas->drag_area);
                canvas->pointer_x = x;
                canvas->pointer_y = y;
                if(canvas->active_item->flags & GTK_PLOT_CANVAS_CAN_MOVE || canvas->active_item->flags & GTK_PLOT_CANVAS_CAN_RESIZE){
                  area.x = new_x;
                  area.y = new_y;
                }
                if(canvas->active_item->flags & GTK_PLOT_CANVAS_CAN_RESIZE){
                  area.width = new_width;
                  area.height = new_height;
                }
                gtk_plot_canvas_child_draw_selection(canvas, canvas->active_item, area);
                canvas->drag_area = area;

       }

       break;
     case GTK_PLOT_CANVAS_ACTION_SELECTION:
         draw_selection(canvas, NULL, canvas->drag_area);
         canvas->pointer_x = x;
         canvas->pointer_y = y;
         area.x = MIN(canvas->pointer_x, canvas->drag_x);
         area.y = MIN(canvas->pointer_y, canvas->drag_y);
         area.width = abs(x - canvas->drag_x);
         area.height = abs(y - canvas->drag_y);
         canvas->drag_area = area;
         draw_selection(canvas, NULL, canvas->drag_area);
 	 break;
     case GTK_PLOT_CANVAS_ACTION_INACTIVE:
     default:
         break;
  }

  return TRUE;

}

static GtkPlotCanvasPos
gtk_plot_canvas_child_button_press(GtkPlotCanvas *canvas,
				   GtkPlotCanvasChild *child,
				   gint x, gint y)
{
  GtkPlotCanvasPos pos;
  GtkAllocation area;

  area = child->allocation;

  if((pos = possible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){
      child->state = GTK_STATE_SELECTED;
      child->drag_area = area;
  }
  return pos;
}

static gint
gtk_plot_canvas_button_press(GtkWidget *widget, GdkEventButton *event)
{
  GtkPlotCanvas *canvas = NULL;
  GtkPlotCanvasChild *active_item = NULL;
  GList *childs = NULL;
  GdkModifierType mods;
  gint x = 0, y = 0;
  gboolean veto;
  gboolean double_click = FALSE;
  gdouble m;
  gboolean new_item = FALSE;
  GtkPlotCanvasPos pos = GTK_PLOT_CANVAS_OUT;

  gdk_window_get_pointer(widget->window, NULL, NULL, &mods);
  if(!(mods & GDK_BUTTON1_MASK)) return FALSE;
  double_click = (event->button == GDK_2BUTTON_PRESS);

  canvas = GTK_PLOT_CANVAS(widget);
  m = canvas->magnification;

/*
  if(double_click && canvas->state == GTK_STATE_SELECTED) return TRUE;
*/
/*
  gdk_pointer_ungrab(event->time);
*/

  if(!GTK_WIDGET_HAS_FOCUS(widget)) gtk_widget_grab_focus(widget);

  gtk_widget_get_pointer(widget, &x, &y);

/**********************************************************************/

  if(GTK_PLOT_CANVAS_CAN_SELECT_ITEM(canvas)){
    childs = g_list_last(canvas->childs);
    while(childs)
      {
        GtkPlotCanvasChild *child = GTK_PLOT_CANVAS_CHILD(childs->data);

        pos = GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->button_press(canvas, child, x, y);
        if(pos != GTK_PLOT_CANVAS_OUT && child->state == GTK_STATE_SELECTED){
          active_item = child;
          break;
        }

        childs = childs->prev;
      }

/**********************************************************************/

    new_item = active_item && ((canvas->state != GTK_STATE_SELECTED ||
                active_item != canvas->active_item));

    veto = TRUE;
    if(active_item && active_item->state == GTK_STATE_SELECTED)
      _gtkextra_signal_emit(GTK_OBJECT(canvas), canvas_signals[SELECT_ITEM],
                      event, active_item, &veto);

    if(new_item){

      if(veto){
        gtk_plot_canvas_unselect(canvas);
        canvas->active_item = active_item;
        canvas->drag_area = active_item->drag_area;
        canvas->state = GTK_STATE_SELECTED;
        canvas->action = GTK_PLOT_CANVAS_ACTION_INACTIVE;

        canvas->drag_point = pos;

        canvas->drag_x = x;
        canvas->drag_y = y;
        canvas->pointer_x = x;
        canvas->pointer_y = y;

        gtk_plot_canvas_child_draw_selection(canvas, active_item, active_item->drag_area);

        if(active_item->mode == GTK_PLOT_CANVAS_SELECT_CLICK_2)
          return TRUE;
      }
    }
    if(active_item && veto){
      if((!new_item && active_item->mode == GTK_PLOT_CANVAS_SELECT_CLICK_2) ||            active_item->mode == GTK_PLOT_CANVAS_SELECT_CLICK_1) {

        if(GTK_PLOT_CANVAS_CAN_DND(canvas)) {
          switch(pos){
           case GTK_PLOT_CANVAS_IN:
               canvas->action = GTK_PLOT_CANVAS_ACTION_DRAG;
               break;
           default:
               if(active_item->flags & GTK_PLOT_CANVAS_CAN_RESIZE)
                  canvas->action = GTK_PLOT_CANVAS_ACTION_RESIZE;
               else
                  canvas->action = GTK_PLOT_CANVAS_ACTION_DRAG;
          }
/*
          gdk_pointer_grab (widget->window, FALSE,
                            GDK_POINTER_MOTION_HINT_MASK |
                            GDK_BUTTON1_MOTION_MASK |
                            GDK_BUTTON_RELEASE_MASK,
                            NULL, NULL, event->time);
*/
          canvas->drag_point = pos;

          canvas->drag_x = x;
          canvas->drag_y = y;
          canvas->pointer_x = x;
          canvas->pointer_y = y;

          return TRUE;
        }
      }
    }
  }
  gtk_plot_canvas_unselect(canvas);

  if(GTK_PLOT_CANVAS_CAN_SELECT(canvas)){
    veto = TRUE;

    _gtkextra_signal_emit(GTK_OBJECT(canvas), canvas_signals[SELECT_ITEM],
                  event, NULL, &veto);
    if(veto){
      canvas->active_item = NULL;
      canvas->state = GTK_STATE_SELECTED;
      canvas->action = GTK_PLOT_CANVAS_ACTION_SELECTION;
      canvas->drag_point = pos;
      canvas->drag_x = x;
      canvas->drag_y = y;
      canvas->pointer_x = x;
      canvas->pointer_y = y;
      canvas->drag_area.x = x;
      canvas->drag_area.y = y;
      canvas->drag_area.width = 0;
      canvas->drag_area.height = 0;
/*
      gdk_pointer_grab (widget->window, FALSE,
                        GDK_POINTER_MOTION_HINT_MASK |
                        GDK_BUTTON1_MOTION_MASK |
                        GDK_BUTTON_RELEASE_MASK,
                        NULL, NULL, event->time);
*/
      draw_selection(canvas, NULL, canvas->drag_area);
    }
  }

  return TRUE;
}

static void
gtk_plot_canvas_child_button_release(GtkPlotCanvas *canvas, GtkPlotCanvasChild *child)
{
  gdouble new_x, new_y, new_width, new_height;
  double dx, dy;
  gdouble x1, x2, y1, y2;

  gtk_plot_canvas_get_position(canvas,
                               canvas->drag_area.width,
                               canvas->drag_area.height,
                               &new_width, &new_height);

  gtk_plot_canvas_get_position(canvas,
                               canvas->drag_area.x,
                               canvas->drag_area.y,
                               &new_x, &new_y);

  gtk_plot_canvas_get_position(canvas,
                               canvas->drag_area.x - child->drag_area.x,
                               canvas->drag_area.y - child->drag_area.y,
                               &dx, &dy);

  x1 = MIN(child->rx1, child->rx2);
  y1 = MIN(child->ry1, child->ry2);
  x2 = MAX(child->rx1, child->rx2);
  y2 = MAX(child->ry1, child->ry2);
  x1 += dx;
  y1 += dy;
  x2 = x1 + new_width;
  y2 = y1 + new_height;

  gtk_plot_canvas_child_move_resize(canvas, child, x1, y1, x2, y2);
  child->drag_area = canvas->drag_area;
}

static gint
gtk_plot_canvas_button_release(GtkWidget *widget, GdkEventButton *event)
{
  GtkPlotCanvas *canvas;
  gdouble new_x, new_y;
  gdouble new_width, new_height;
  gdouble x1 = 0., y1 = 0., x2 = 0., y2 = 0.;
  gboolean veto = TRUE;
  gdouble dx = 0., dy = 0.;

  canvas = GTK_PLOT_CANVAS(widget);

/*
  gdk_pointer_ungrab(event->time);
*/

  if(GTK_WIDGET_MAPPED(widget)){
      gdk_cursor_destroy(canvas->cursor);
      canvas->cursor = gdk_cursor_new(GDK_TOP_LEFT_ARROW);
      gdk_window_set_cursor(widget->window,
                            canvas->cursor);
  }

  if(canvas->action == GTK_PLOT_CANVAS_ACTION_INACTIVE) return TRUE;

  gtk_plot_canvas_get_position(canvas,
                               canvas->drag_area.width,
                               canvas->drag_area.height,
                               &new_width, &new_height);

  gtk_plot_canvas_get_position(canvas,
                               canvas->drag_area.x,
                               canvas->drag_area.y,
                               &new_x, &new_y);

  if(canvas->action != GTK_PLOT_CANVAS_ACTION_SELECTION && canvas->active_item){
    GtkPlotCanvasChild *child = canvas->active_item;

    gtk_plot_canvas_get_position(canvas,
                                 canvas->drag_area.x -
                                 canvas->active_item->drag_area.x,
                                 canvas->drag_area.y -
                                 canvas->active_item->drag_area.y,
                                 &dx, &dy);

    x1 = MIN(child->rx1, child->rx2);
    y1 = MIN(child->ry1, child->ry2);
    x2 = MAX(child->rx1, child->rx2);
    y2 = MAX(child->ry1, child->ry2);
    x1 += dx;
    y1 += dy;
    x2 = x1 + new_width;
    y2 = y1 + new_height;

    if(canvas->action == GTK_PLOT_CANVAS_ACTION_DRAG){
      _gtkextra_signal_emit(GTK_OBJECT(canvas), canvas_signals[MOVE_ITEM],
                      child,
                      x1, y1, &veto);
    }
    if(canvas->action == GTK_PLOT_CANVAS_ACTION_RESIZE){
       _gtkextra_signal_emit(GTK_OBJECT(canvas),
                       canvas_signals[RESIZE_ITEM],
                       child, new_width, new_height, &veto);
    }
    if(canvas->action != GTK_PLOT_CANVAS_ACTION_INACTIVE && veto) {
      if(GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(canvas->active_item)))->button_release)
        GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(canvas->active_item)))->button_release(canvas, canvas->active_item);

      gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
      canvas->pointer_x = canvas->pointer_y = canvas->drag_x = canvas->drag_y = 0;
      gtk_plot_canvas_child_draw_selection(canvas, canvas->active_item, canvas->active_item->drag_area);
      canvas->action = GTK_PLOT_CANVAS_ACTION_INACTIVE;
      return TRUE;
    } else {
      canvas->state = GTK_STATE_NORMAL;
    }

  } else {
    gtk_plot_canvas_get_position(canvas,
                                 canvas->drag_x, canvas->drag_y,
				 &x1, &y1);
    gtk_plot_canvas_get_position(canvas,
                                 canvas->pointer_x, canvas->pointer_y,
				 &x2, &y2);
    new_width = abs(canvas->pointer_x - canvas->drag_x);
    new_height = abs(canvas->pointer_y - canvas->drag_y);
    draw_selection(canvas, NULL, canvas->drag_area);
    gtk_signal_emit(GTK_OBJECT(canvas), canvas_signals[SELECT_REGION],
                    x1, y1, x2, y2);
    canvas->state = GTK_STATE_NORMAL;
    canvas->action = GTK_PLOT_CANVAS_ACTION_INACTIVE;
    return TRUE;
  }

  canvas->drag_x = canvas->pointer_x;
  canvas->drag_y = canvas->pointer_y;

  if(canvas->action != GTK_PLOT_CANVAS_ACTION_SELECTION)
             canvas->state = GTK_STATE_NORMAL;
  canvas->action = GTK_PLOT_CANVAS_ACTION_INACTIVE;

  return TRUE;

}

static gint
gtk_plot_canvas_focus_in(GtkWidget *widget, GdkEventFocus *event)
{
  GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
  return FALSE;
}


static gint
gtk_plot_canvas_focus_out(GtkWidget *widget, GdkEventFocus *event)
{
  GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
  gtk_plot_canvas_unselect(GTK_PLOT_CANVAS(widget));
  return FALSE;
}

void
gtk_plot_canvas_set_transparent (GtkPlotCanvas *canvas, gboolean transparent)
{
  g_return_if_fail (canvas != NULL);
  g_return_if_fail (GTK_IS_PLOT_CANVAS (canvas));

  canvas->transparent = transparent;
}

gboolean
gtk_plot_canvas_transparent (GtkPlotCanvas *canvas)
{
  g_return_val_if_fail (canvas != NULL, TRUE);
  g_return_val_if_fail (GTK_IS_PLOT_CANVAS (canvas), TRUE);

  return(canvas->transparent);
}

void
gtk_plot_canvas_set_background (GtkPlotCanvas *canvas, const GdkColor *color)
{

  g_return_if_fail (canvas != NULL);
  g_return_if_fail (GTK_IS_PLOT_CANVAS (canvas));

  if(!color) {
    canvas->transparent = TRUE;
    return;
  } else {
    canvas->background = *color;
    canvas->transparent = FALSE;
  }

  if(GTK_WIDGET_REALIZED(GTK_WIDGET(canvas)))
       gtk_plot_canvas_paint(canvas);

  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

void
gtk_plot_canvas_get_pixel(GtkPlotCanvas *canvas, gdouble px, gdouble py,
                          gint *x, gint *y)
{
  *x = roundint(canvas->pixmap_width * px);
  *y = roundint(canvas->pixmap_height * py);
}

void
gtk_plot_canvas_get_position(GtkPlotCanvas *plot_canvas, gint x, gint y,
                             gdouble *px, gdouble *py)
{
  *px = (gdouble) x / (gdouble) plot_canvas->pixmap_width;
  *py = (gdouble) y / (gdouble) plot_canvas->pixmap_height;
}

void
gtk_plot_canvas_unselect (GtkPlotCanvas *plot_canvas)
{

  if(plot_canvas->state == GTK_STATE_SELECTED){
    if(plot_canvas->active_item)
      gtk_plot_canvas_child_draw_selection(plot_canvas, plot_canvas->active_item, plot_canvas->drag_area);
    else
      draw_selection(plot_canvas, NULL, plot_canvas->drag_area);
  }

  plot_canvas->action = GTK_PLOT_CANVAS_ACTION_INACTIVE;
  plot_canvas->state = GTK_STATE_NORMAL;
  if(plot_canvas->active_item){
    plot_canvas->active_item->state = GTK_STATE_NORMAL;
    if(GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(plot_canvas->active_item)))->unselect)
      GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(plot_canvas->active_item)))->unselect(plot_canvas, plot_canvas->active_item);
  }
  plot_canvas->active_item = NULL;

  if(GTK_WIDGET_MAPPED(GTK_WIDGET(plot_canvas))){
      gdk_cursor_destroy(plot_canvas->cursor);
      plot_canvas->cursor = gdk_cursor_new(GDK_TOP_LEFT_ARROW);
      gdk_window_set_cursor(GTK_WIDGET(plot_canvas)->window,
                            plot_canvas->cursor);
  }
}

void
gtk_plot_canvas_cancel_action (GtkPlotCanvas *plot_canvas)
{
  gtk_plot_canvas_unselect(plot_canvas);
/*
  gdk_pointer_ungrab(GDK_CURRENT_TIME);
*/
}


GtkPlotCanvasChild *
gtk_plot_canvas_get_active_item(GtkPlotCanvas *canvas)
{
  return (canvas->active_item);
}

gboolean
gtk_plot_canvas_child_get_position(GtkPlotCanvas *canvas,
                                   GtkPlotCanvasChild *child,
                                   double *x1, double *y1,
                                   double *x2, double *y2)
{
  GList *list;
  gpointer data;

  *x1 = child->rx1;
  *x2 = child->rx2;
  *y1 = child->ry1;
  *y2 = child->ry2;

  list = canvas->childs;

  /* Return TRUE if child is actually contained w/in canvas, and
     coordinates of the childs bounding box, else FALSE */
  while(list)
   {
     data = list->data;
     if(GTK_PLOT_CANVAS_CHILD(data) == child) return TRUE;
     list = list->next;
   }

  return FALSE;

}

void
gtk_plot_canvas_set_size(GtkPlotCanvas *canvas, gint width, gint height)
{
  GList *list = NULL;
  gdouble m = canvas->magnification;
  GtkAllocation allocation = GTK_WIDGET(canvas)->allocation;

  gtk_plot_canvas_cancel_action(canvas);

  canvas->width = width;
  canvas->height = height;
  canvas->pixmap_width = roundint(m * width);
  canvas->pixmap_height = roundint(m * height);

  if(GTK_WIDGET_MAPPED(canvas)){
    gtk_plot_canvas_create_pixmap(GTK_WIDGET(canvas),
                                  canvas->pixmap_width,
                                  canvas->pixmap_height);
  }

/*
  gtk_widget_set_usize(GTK_WIDGET(canvas),
                       canvas->pixmap_width, canvas->pixmap_height);
*/

  allocation.width = canvas->pixmap_width;
  allocation.height = canvas->pixmap_height;
  gtk_widget_size_allocate(GTK_WIDGET(canvas), &allocation);

  list = canvas->childs;
  while(list){
    GtkPlotCanvasChild *child = GTK_PLOT_CANVAS_CHILD(list->data);
    if(GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->size_allocate)
      GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->size_allocate(canvas, child);
    list = list->next;
  }


  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

static void
gtk_plot_canvas_create_pixmap(GtkWidget *widget, gint width, gint height)
{
  GtkPlotCanvas *canvas;
  gint pixmap_width, pixmap_height;

  canvas = GTK_PLOT_CANVAS(widget);
  if (!canvas->pixmap)
    canvas->pixmap = gdk_pixmap_new (widget->window,
                                     width,
                                     height, -1);
  else{
    gdk_window_get_size(canvas->pixmap, &pixmap_width, &pixmap_height);
    if(width != pixmap_width || height != pixmap_height) {
        gdk_pixmap_unref(canvas->pixmap);
        canvas->pixmap = gdk_pixmap_new (widget->window,
                                         width,
                                         height, -1);
	}
  }

  if(canvas->pc && GTK_IS_PLOT_GDK(canvas->pc)){
       GTK_PLOT_GDK(canvas->pc)->drawable = canvas->pixmap;
  }
  gtk_plot_pc_set_viewport(canvas->pc, width, height);
}

static gint
gtk_plot_canvas_expose(GtkWidget *widget, GdkEventExpose *event)
{
  GtkPlotCanvas *canvas;
  GdkPixmap *pixmap;

  if(!GTK_WIDGET_DRAWABLE(widget)) return FALSE;

  canvas = GTK_PLOT_CANVAS(widget);

  if(!canvas->pixmap){
      gtk_plot_canvas_create_pixmap(widget,
                                    canvas->pixmap_width,
                                    canvas->pixmap_height);
      gtk_plot_canvas_paint(canvas);
      return FALSE;
  }

  pixmap = canvas->pixmap;
  gdk_draw_pixmap(GTK_WIDGET(canvas)->window,
                  widget->style->fg_gc[GTK_STATE_NORMAL],
                  pixmap,
                  event->area.x,
                  event->area.y,
                  event->area.x,
                  event->area.y,
                  event->area.width, event->area.height);

  GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);

  return FALSE;

}


static void
gtk_plot_canvas_size_request (GtkWidget *widget, GtkRequisition *requisition)
{
  GtkPlotCanvas *canvas;

  canvas = GTK_PLOT_CANVAS(widget);

  GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition);

  widget->requisition.width = MAX(canvas->pixmap_width, requisition->width);
  widget->requisition.height = MAX(canvas->pixmap_height, requisition->height);
}


void
gtk_plot_canvas_set_magnification(GtkPlotCanvas *canvas,
                                  gdouble magnification)
{
  GList *list;

  canvas->magnification = magnification;

  list = canvas->childs;
  while(list){
    GtkPlotCanvasChild *child = GTK_PLOT_CANVAS_CHILD(list->data);

    if(GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->set_magnification)
      GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->set_magnification(canvas, child, magnification);

    list = list->next;
  }

  gtk_plot_canvas_set_size(canvas,
                           canvas->width,
                           canvas->height);
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

static void
gtk_plot_canvas_child_draw_selection(GtkPlotCanvas *canvas, GtkPlotCanvasChild *child, GtkAllocation area)
{
   if(GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->draw_selection)
     GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->draw_selection(canvas, child, area);
}

static void
draw_selection(GtkPlotCanvas *canvas, GtkPlotCanvasChild *child, GtkAllocation area)
{
  GdkGC *xor_gc = NULL;
  GdkGCValues values;

  gdk_gc_get_values(GTK_WIDGET(canvas)->style->fg_gc[0], &values);
  values.function = GDK_INVERT;
  values.foreground = GTK_WIDGET(canvas)->style->white;
  values.subwindow_mode = GDK_INCLUDE_INFERIORS;
  xor_gc = gdk_gc_new_with_values(GTK_WIDGET(canvas)->window,
                                  &values,
                                  GDK_GC_FOREGROUND |
                                  GDK_GC_FUNCTION |
                                  GDK_GC_SUBWINDOW);

  if(canvas->active_item){
    if(canvas->active_item->selection == GTK_PLOT_CANVAS_SELECT_TARGET){
      area.x += area.width / 2 - 10;
      area.y += area.height / 2 - 10;
      area.width = 20;
      area.height = 20;
      gdk_draw_rectangle (GTK_WIDGET(canvas)->window,
                          xor_gc,
                          FALSE,
                          area.x, area.y,
                          area.width, area.height);

      gdk_draw_line(GTK_WIDGET(canvas)->window, xor_gc,
                    area.x + 1, area.y + area.height/2,
                    area.x + 6, area.y + area.height/2);
      gdk_draw_line(GTK_WIDGET(canvas)->window, xor_gc,
                    area.x + area.width - 1, area.y + area.height / 2,
                    area.x + area.width - 6, area.y + area.height / 2);
      gdk_draw_line(GTK_WIDGET(canvas)->window, xor_gc,
                    area.x + area.width/2, area.y + 1,
                    area.x + area.width/2, area.y + 6);
      gdk_draw_line(GTK_WIDGET(canvas)->window, xor_gc,
                    area.x + area.width/2, area.y + area.height - 1,
                    area.x + area.width/2, area.y + area.height - 6);

      if(xor_gc) gdk_gc_destroy(xor_gc);
      return;
    }


    if(canvas->active_item->selection == GTK_PLOT_CANVAS_SELECT_MARKERS){
       gdk_draw_rectangle (GTK_WIDGET(canvas)->window,
                           xor_gc,
                           FALSE,
                           area.x, area.y,
                           area.width, area.height);
       draw_marker(canvas, xor_gc, area.x, area.y);
       draw_marker(canvas, xor_gc, area.x, area.y + area.height);
       draw_marker(canvas, xor_gc, area.x + area.width, area.y);
       draw_marker(canvas, xor_gc, area.x + area.width, area.y + area.height);
       if(area.height > DEFAULT_MARKER_SIZE * 2){
         draw_marker(canvas, xor_gc, area.x, area.y + area.height / 2);
         draw_marker(canvas, xor_gc, area.x + area.width,
                                     area.y + area.height / 2);
       }
       if(area.width > DEFAULT_MARKER_SIZE * 2){
         draw_marker(canvas, xor_gc, area.x + area.width / 2, area.y);
         draw_marker(canvas, xor_gc, area.x + area.width / 2,
                                     area.y + area.height);
       }
    }
  } else {
    gdk_gc_set_line_attributes(xor_gc, 1, 1, 0 ,0 );

    gdk_draw_rectangle (GTK_WIDGET(canvas)->window,
                        xor_gc,
                        FALSE,
                        area.x, area.y,
                        area.width, area.height);
  }
  if(xor_gc) gdk_gc_unref(xor_gc);
}

static void
draw_marker(GtkPlotCanvas *canvas, GdkGC *gc, gint x, gint y)
{
  GdkDrawable *darea;

  darea = GTK_WIDGET(canvas)->window;

  gdk_draw_rectangle(darea, gc, TRUE,
                     x - DEFAULT_MARKER_SIZE / 2, y - DEFAULT_MARKER_SIZE / 2,
                     DEFAULT_MARKER_SIZE + 1, DEFAULT_MARKER_SIZE + 1);
}

GtkPlotCanvasPos
possible_selection(GtkAllocation area, gint x, gint y)
{
  GtkPlotCanvasPos return_value = GTK_PLOT_CANVAS_OUT;

  if(x >= area.x - DEFAULT_MARKER_SIZE / 2 &&
     x <= area.x + DEFAULT_MARKER_SIZE / 2){
       if(y >= area.y - DEFAULT_MARKER_SIZE / 2. &&
          y <= area.y + DEFAULT_MARKER_SIZE / 2.)
                      return_value = GTK_PLOT_CANVAS_TOP_LEFT;
       if(y >= area.y + area.height - DEFAULT_MARKER_SIZE / 2. &&
          y <= area.y + area.height + DEFAULT_MARKER_SIZE / 2.)
                      return_value = GTK_PLOT_CANVAS_BOTTOM_LEFT;
       if(y >= area.y + area.height / 2 - DEFAULT_MARKER_SIZE / 2. &&
          y <= area.y + area.height / 2 + DEFAULT_MARKER_SIZE / 2. &&
          area.height > DEFAULT_MARKER_SIZE * 2)
                      return_value = GTK_PLOT_CANVAS_LEFT;
  }

  if(x >= area.x + area.width - DEFAULT_MARKER_SIZE / 2 &&
     x <= area.x + area.width + DEFAULT_MARKER_SIZE / 2){
       if(y >= area.y - DEFAULT_MARKER_SIZE / 2. &&
          y <= area.y + DEFAULT_MARKER_SIZE / 2.)
                      return_value = GTK_PLOT_CANVAS_TOP_RIGHT;
       if(y >= area.y + area.height - DEFAULT_MARKER_SIZE / 2. &&
          y <= area.y + area.height + DEFAULT_MARKER_SIZE / 2.)
                      return_value = GTK_PLOT_CANVAS_BOTTOM_RIGHT;
       if(y >= area.y + area.height / 2 - DEFAULT_MARKER_SIZE / 2. &&
          y <= area.y + area.height / 2 + DEFAULT_MARKER_SIZE / 2. &&
          area.height > DEFAULT_MARKER_SIZE * 2)
                      return_value = GTK_PLOT_CANVAS_RIGHT;
  }

  if(x >= area.x + area.width / 2 - DEFAULT_MARKER_SIZE / 2 &&
     x <= area.x + area.width / 2 + DEFAULT_MARKER_SIZE / 2 &&
     area.width > DEFAULT_MARKER_SIZE * 2){
       if(y >= area.y - DEFAULT_MARKER_SIZE / 2. &&
          y <= area.y + DEFAULT_MARKER_SIZE / 2.)
                      return_value = GTK_PLOT_CANVAS_TOP;
       if(y >= area.y + area.height - DEFAULT_MARKER_SIZE / 2. &&
          y <= area.y + area.height + DEFAULT_MARKER_SIZE / 2.)
                      return_value = GTK_PLOT_CANVAS_BOTTOM;
  }

  if(return_value == GTK_PLOT_CANVAS_OUT){
     if (x >= area.x && x <= area.x + area.width &&
         y >= area.y && y <= area.y + area.height)
                      return_value = GTK_PLOT_CANVAS_IN;
  }

  return (return_value);
}

/**********************************************************************/

void
gtk_plot_canvas_grid_set_visible(GtkPlotCanvas *canvas, gboolean visible)
{
  canvas->show_grid= visible;
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

void
gtk_plot_canvas_grid_set_step(GtkPlotCanvas *canvas, gdouble step)
{
  canvas->grid_step = step;
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

void
gtk_plot_canvas_grid_set_attributes(GtkPlotCanvas *canvas,
		 	            GtkPlotLineStyle style,
			            gint width,
			            const GdkColor *color)
{
  if(color)
      canvas->grid.color = *color;
  canvas->grid.line_width = width;
  canvas->grid.line_style = style;
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

/**********************************************************************/
void
gtk_plot_canvas_put_child(GtkPlotCanvas *canvas,
                          GtkPlotCanvasChild *child,
			  gdouble x1, gdouble y1,
                          gdouble x2, gdouble y2)
{
  child->rx1 = x1;
  child->ry1 = y1;
  child->rx2 = x2;
  child->ry2 = y2;

  child->parent = canvas;
  canvas->childs = g_list_append(canvas->childs, child);
  gtk_object_ref(GTK_OBJECT(child));
  gtk_object_sink(GTK_OBJECT(child));

  if(GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->size_allocate)
    GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->size_allocate(canvas, child);
  if(GTK_WIDGET_REALIZED(canvas) && GTK_WIDGET_VISIBLE(canvas))
    gtk_plot_canvas_child_draw(canvas, child);
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
  gtk_signal_emit(GTK_OBJECT(canvas), canvas_signals[ADD_ITEM], child);
}

void
gtk_plot_canvas_child_move(GtkPlotCanvas *canvas,
                           GtkPlotCanvasChild *child,
			   gdouble x1, gdouble y1)
{
  child->rx2 += (x1 - child->rx1);
  child->ry2 += (y1 - child->ry1);
  child->rx1 = x1;
  child->ry1 = y1;

  if(GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->move)
    GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->move(canvas, child, x1, y1);
  GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->size_allocate(canvas, child);
  gtk_plot_canvas_paint(canvas);
  gtk_plot_canvas_refresh(canvas);
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

void
gtk_plot_canvas_child_move_resize(GtkPlotCanvas *canvas,
                                  GtkPlotCanvasChild *child,
			          gdouble x1, gdouble y1,
				  gdouble x2, gdouble y2)
{
  child->rx1 = x1;
  child->ry1 = y1;
  child->rx2 = x2;
  child->ry2 = y2;

  if(GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->move_resize)
    GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->move_resize(canvas, child, x1, y1, x2, y2);
  GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->size_allocate(canvas, child);
  gtk_plot_canvas_paint(canvas);
  gtk_plot_canvas_refresh(canvas);
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

static void
gtk_plot_canvas_child_size_allocate(GtkPlotCanvas *canvas, GtkPlotCanvasChild *child)
{
  gint x1, x2, y1, y2;

  gtk_plot_canvas_get_pixel(canvas, child->rx1, child->ry1, &x1, &y1);
  gtk_plot_canvas_get_pixel(canvas, child->rx2, child->ry2, &x2, &y2);
  child->allocation.x = MIN(x1, x2);
  child->allocation.y = MIN(y1, y2);
  child->allocation.width = abs(x1 - x2);
  child->allocation.height = abs(y1 - y2);
}

void
gtk_plot_canvas_child_set_selection (GtkPlotCanvasChild *child,
				     GtkPlotCanvasSelection selection)
{
  if(!child) return;
  child->selection = selection;
}

void
gtk_plot_canvas_child_set_selection_mode (GtkPlotCanvasChild *child,
				          GtkPlotCanvasSelectionMode mode)
{
  if(!child) return;
  child->mode = mode;
}

void
gtk_plot_canvas_child_draw(GtkPlotCanvas *canvas,
                           GtkPlotCanvasChild *child)
{
  gtk_plot_pc_gsave(canvas->pc);

  GTK_PLOT_CANVAS_CHILD_CLASS(GTK_OBJECT_GET_CLASS(GTK_OBJECT(child)))->draw(canvas, child);

  gtk_plot_pc_grestore(canvas->pc);
}

void
gtk_plot_canvas_set_line_attributes(GtkPlotCanvas *canvas, GtkPlotLine line)
{
  gdouble dot[] = {2., 3.};
  gdouble dash[] = {6., 4.};
  gdouble dot_dash[] = {6., 4., 2., 4.};
  gdouble dot_dot_dash[] = {6., 4., 2., 4., 2., 4.};
  gdouble dot_dash_dash[] = {6., 4., 6., 4., 2., 4.};

  gtk_plot_pc_set_color(canvas->pc, &line.color);

  switch(line.line_style){
   case GTK_PLOT_LINE_SOLID:
        gtk_plot_pc_set_lineattr(canvas->pc, line.line_width, 0, 0, 0);
        break;
   case GTK_PLOT_LINE_DOTTED:
        gtk_plot_pc_set_lineattr(canvas->pc, line.line_width,
                                GDK_LINE_ON_OFF_DASH, 0, 0);
        gtk_plot_pc_set_dash(canvas->pc, 0, dot, 2);
        break;
   case GTK_PLOT_LINE_DASHED:
        gtk_plot_pc_set_lineattr(canvas->pc, line.line_width,
                                GDK_LINE_ON_OFF_DASH, 0, 0);
        gtk_plot_pc_set_dash(canvas->pc, 0, dash, 2);
   case GTK_PLOT_LINE_DOT_DASH:
        gtk_plot_pc_set_lineattr(canvas->pc, line.line_width,
                                GDK_LINE_ON_OFF_DASH, 0, 0);
        gtk_plot_pc_set_dash(canvas->pc, 0, dot_dash, 4);
        break;
   case GTK_PLOT_LINE_DOT_DOT_DASH:
        gtk_plot_pc_set_lineattr(canvas->pc, line.line_width,
                                GDK_LINE_ON_OFF_DASH, 0, 0);
        gtk_plot_pc_set_dash(canvas->pc, 0, dot_dot_dash, 6);
        break;
   case GTK_PLOT_LINE_DOT_DASH_DASH:
        gtk_plot_pc_set_lineattr(canvas->pc, line.line_width,
                                GDK_LINE_ON_OFF_DASH, 0, 0);
        gtk_plot_pc_set_dash(canvas->pc, 0, dot_dash_dash, 6);
        break;
   case GTK_PLOT_LINE_NONE:
   default:
        break;
  }
}
