/* 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 "gtkplot.h"
#include "gtkplot3d.h"
#include "gtkplotdata.h"
#include "gtkplotcanvas.h"
#include "gtkplotgdk.h"

#define DEFAULT_WIDTH 100
#define DEFAULT_HEIGHT 150
#define DEFAULT_MARKER_SIZE 6
#define DEFAULT_FONT_HEIGHT 12
#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)


static gchar DEFAULT_FONT[] = "Helvetica";


static void gtk_plot_canvas_class_init 		(GtkPlotCanvasClass *klass);
static void gtk_plot_canvas_init 		(GtkPlotCanvas *plot_canvas);
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_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_remove		(GtkContainer *container,
						 GtkWidget *child);

/* Drawing functions */
static void gtk_plot_canvas_draw                (GtkWidget *widget, 
                                                 GdkRectangle *area);
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_set_plots_pixmap    (GtkPlotCanvas *plot_canvas);
static void gtk_plot_canvas_get_real_pixel      (GtkWidget *widget,
                                                 gdouble px, gdouble py,
                                                 gint *x, gint *y);
static void draw_selection 			(GtkPlotCanvas *canvas, 
                				 GdkRectangle area,
                				 gboolean markers);
static void draw_marker				(GtkPlotCanvas *canvas, 
						 GdkGC *gc, gint x, gint y);
static void gtk_plot_canvas_draw_text           (GtkPlotCanvas *canvas,
                                                 GtkPlotCanvasChild *child);

static void gtk_plot_canvas_draw_grid		(GtkPlotCanvas *canvas);
static void gtk_plot_canvas_draw_child		(GtkPlotCanvas *canvas,
                           			 GtkPlotCanvasChild *child);
static void gtk_plot_canvas_set_line_attributes	(GtkPlotCanvas *canvas, 
                                                 GtkPlotLine line);
/* Auxiliary functions */
static GtkPlotCanvasPos posible_selection	(GdkRectangle area, 
						 gint x, gint y);
static gint roundint                            (gdouble x);


/* Signals */

enum {
        SELECT_ITEM,
        MOVE_ITEM,
        RESIZE_ITEM,
        DELETE_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 void
gtk_plot_canvas_marshal_move_resize             (GtkObject *object,
                                                 GtkSignalFunc func,
                                                 gpointer func_data,
                                                 GtkArg * args);

static void
gtk_plot_canvas_marshal_select_item             (GtkObject *object,
                                                 GtkSignalFunc func,
                                                 gpointer func_data,
                                                 GtkArg * args);

static void
gtk_plot_canvas_marshal_select		        (GtkObject *object,
                                                 GtkSignalFunc func,
                                                 gpointer func_data,
                                                 GtkArg * args);

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",
       0,
       0,
       (GtkClassInitFunc) NULL,
       (GtkObjectInitFunc) NULL,
       /* reserved 1*/ NULL,
        /* reserved 2 */ NULL,
        (GtkClassInitFunc) NULL,
      };

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

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

  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,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, select_item),
                    gtk_plot_canvas_marshal_select_item,
                    GTK_TYPE_BOOL, 2, GTK_TYPE_GDK_EVENT, 
                    GTK_TYPE_PLOT_CANVAS_CHILD);

  canvas_signals[MOVE_ITEM] =
    gtk_signal_new ("move_item",
                    GTK_RUN_LAST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, move_item),
                    gtk_plot_canvas_marshal_move_resize,
                    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,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, resize_item),
                    gtk_plot_canvas_marshal_move_resize,
                    GTK_TYPE_BOOL, 3, GTK_TYPE_PLOT_CANVAS_CHILD,
                    GTK_TYPE_DOUBLE,
                    GTK_TYPE_DOUBLE);

  canvas_signals[DELETE_ITEM] =
    gtk_signal_new ("delete_item",
                    GTK_RUN_LAST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, delete_item),
                    gtk_marshal_BOOL__POINTER,
                    GTK_TYPE_BOOL, 1,  
                    GTK_TYPE_PLOT_CANVAS_CHILD);

  canvas_signals[SELECT_REGION] =
    gtk_signal_new ("select_region",
                    GTK_RUN_LAST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, select_region),
                    gtk_plot_canvas_marshal_select,
                    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,
                   object_class->type,
                   GTK_SIGNAL_OFFSET (GtkPlotCanvasClass, changed),
                   gtk_marshal_NONE__NONE,
                   GTK_TYPE_NONE, 0);

  gtk_object_class_add_signals (object_class, canvas_signals, LAST_SIGNAL);

  object_class->destroy = gtk_plot_canvas_destroy;

  widget_class->map = gtk_plot_canvas_map;
  widget_class->draw = gtk_plot_canvas_draw;
  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;

  container_class->remove = gtk_plot_canvas_remove;

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

static void
gtk_plot_canvas_marshal_move_resize           (GtkObject *object,
                                               GtkSignalFunc func,
                                               gpointer func_data,
                                               GtkArg * args)
{
  GtkPlotCanvasSignal1 rfunc;
  gboolean *veto;
  veto = GTK_RETLOC_BOOL (args[3]);

  rfunc = (GtkPlotCanvasSignal1) func;

  *veto = (*rfunc) (object, 
                    GTK_VALUE_POINTER (args[0]),
                    GTK_VALUE_DOUBLE (args[1]),
                    GTK_VALUE_DOUBLE (args[2]),
                    func_data);
}

static void
gtk_plot_canvas_marshal_select_item                   (GtkObject *object,
                                                       GtkSignalFunc func,
                                                       gpointer func_data,
                                                       GtkArg * args)
{
  GtkPlotCanvasSignal2 rfunc;
  gboolean *veto;
  veto = GTK_RETLOC_BOOL (args[2]);

  rfunc = (GtkPlotCanvasSignal2) func;

  *veto = (*rfunc) (object, 
                    GTK_VALUE_POINTER (args[0]),
                    GTK_VALUE_POINTER (args[1]),
                    func_data);
}


static void
gtk_plot_canvas_marshal_select			      (GtkObject *object,
                                                       GtkSignalFunc func,
                                                       gpointer func_data,
                                                       GtkArg * args)
{
  GtkPlotCanvasSignal3 rfunc;

  rfunc = (GtkPlotCanvasSignal3) func;

  (*rfunc) (object, 
            GTK_VALUE_DOUBLE (args[0]),
            GTK_VALUE_DOUBLE (args[1]),
            GTK_VALUE_DOUBLE (args[2]),
            GTK_VALUE_DOUBLE (args[3]),
            func_data);
}

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->cursor = gdk_cursor_new(GDK_TOP_LEFT_ARROW);

  plot_canvas->num_plots = 0;
  plot_canvas->background = widget->style->white;

  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->active_plot = NULL;
  plot_canvas->active_data = NULL;
  plot_canvas->active_point = -1;

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

  plot_canvas->plots = NULL;
  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 = GTK_PLOT_PC(gtk_plot_gdk_new(NULL));

  plot_canvas->pixmap = NULL;
}

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);
}


static void
gtk_plot_canvas_destroy (GtkObject *object)
{
  GtkPlotCanvas *plot_canvas;
  GList *list;

  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){
    GtkPlotCanvasChild *child;

    child = (GtkPlotCanvasChild *) list->data;

    if(child->type == GTK_PLOT_CANVAS_TEXT){
       GtkPlotText *text;
     
       text = (GtkPlotText *)child->data;
   
       if(text->font) g_free(text->font); 
       if(text->text) g_free(text->text); 
    }

    if(child->data)
         g_free(child->data);

    g_free(child);

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

    list = plot_canvas->childs;
  }


  gtk_object_destroy(GTK_OBJECT(plot_canvas->pc));

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

  gtk_psfont_unref();
}

static void
gtk_plot_canvas_remove(GtkContainer *container, GtkWidget *child)
{
  GtkPlotCanvas *canvas;
  GList *list;

  canvas = GTK_PLOT_CANVAS(container);
  gtk_plot_canvas_cancel_action(canvas);

  list = canvas->plots;
  while(list){
   if(list->data == child){
      canvas->plots = g_list_remove_link(canvas->plots, list);
      g_list_free_1(list);
      canvas->num_plots--;
      break;
   }
   list = list->next;
  }
 
  GTK_CONTAINER_CLASS(parent_class)->remove(container, child);
}

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_set_plots_pixmap(canvas);

  gtk_plot_canvas_paint(canvas);

  gdk_draw_pixmap(widget->window,
                  widget->style->fg_gc[GTK_STATE_NORMAL],
                  canvas->pixmap,
                  0, 0,
                  0, 0,
                  -1, -1);
}

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

  widget = GTK_WIDGET(canvas);

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

  gtk_plot_pc_gsave(canvas->pc);
  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);
  plots = canvas->plots;
  while(plots)
    {
      GtkPlot *plot;
      GtkPlotPC *pc;

      plot = GTK_PLOT(plots->data);

      pc = plot->pc;
      plot->pc = canvas->pc;
      plot->magnification = canvas->magnification;

      gtk_plot_set_drawable(plot, canvas->pixmap);
      gtk_plot_paint(GTK_PLOT(plot));

      plot->pc = pc;

      plots = plots->next;
    }

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

     child = (GtkPlotCanvasChild *) childs->data;
     gtk_plot_canvas_draw_child(canvas, child);
     childs = childs->next;
   }

  gtk_plot_pc_grestore(canvas->pc);
}

void
gtk_plot_canvas_refresh(GtkPlotCanvas *canvas)
{
  if(!GTK_WIDGET_REALIZED(GTK_WIDGET(canvas))) return;
  if(!canvas->pixmap) return;

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

static void
gtk_plot_canvas_draw_grid(GtkPlotCanvas *canvas)
{
  gint 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, x, 0, x, canvas->pixmap_height);

  for(y = 0; y < canvas->pixmap_height; y += canvas->grid_step)
      gtk_plot_pc_draw_line(canvas->pc, 0, y, canvas->pixmap_width, 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);
      return;
  }

  gdk_window_set_cursor(widget->window, plot_canvas->cursor);
}


static gint 
gtk_plot_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
{
  GtkPlotCanvas *canvas;
  GtkPlot *active_plot;
  GtkPlotData *active_dataset;
  gint active_point;
  GtkAllocation *allocation = NULL;
  GdkRectangle area;
  gint x, y;
  gint new_x, new_y;
  gint new_width, new_height;
  gint cursor;
  gint pivot_x, pivot_y;

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

  area = canvas->active_item.allocation;
  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
       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);
  }    
    

  active_plot = canvas->active_plot;
  active_dataset = canvas->active_data;
  active_point = canvas->active_point;
 
  if(canvas->action == GTK_PLOT_CANVAS_ACTION_INACTIVE) return TRUE;
  if(active_plot == NULL && canvas->action != GTK_PLOT_CANVAS_ACTION_SELECTION)
     return TRUE;

  if(active_plot)
         allocation = &GTK_WIDGET(canvas->active_plot)->allocation;

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

         draw_selection(canvas, canvas->drag_area, TRUE);
         canvas->pointer_x = x;
         canvas->pointer_y = y;
         dx = x - canvas->drag_x;
         dy = y - canvas->drag_y;
         area.x = canvas->active_item.allocation.x + dx; 
         area.y = canvas->active_item.allocation.y + dy;
         draw_selection(canvas, area, TRUE);
         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.flags & GTK_PLOT_CANVAS_CAN_X_RESIZE){
                    new_x = MIN(x, pivot_x);
                    new_width = abs(x - pivot_x);
               }
            case GTK_PLOT_CANVAS_TOP:
               if(canvas->active_item.flags & GTK_PLOT_CANVAS_CAN_Y_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.flags & GTK_PLOT_CANVAS_CAN_X_RESIZE){
                    new_x = MIN(x, pivot_x);
                    new_width = abs(x - pivot_x);
               }
            case GTK_PLOT_CANVAS_BOTTOM:
               if(canvas->active_item.flags & GTK_PLOT_CANVAS_CAN_Y_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.flags & GTK_PLOT_CANVAS_CAN_X_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(new_width >= canvas->active_item.min_width &&
          new_height >= canvas->active_item.min_height){ 

                canvas->pointer_x = x;
                canvas->pointer_y = y;
                draw_selection(canvas, canvas->drag_area, TRUE);
                area.x = new_x;
                area.y = new_y;
                area.width = new_width;
                area.height = new_height;
                draw_selection(canvas, area, TRUE);
                canvas->drag_area = area;

       }

       break;
     case GTK_PLOT_CANVAS_ACTION_SELECTION:
         draw_selection(canvas, canvas->drag_area, FALSE);
         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, canvas->drag_area, FALSE);
 	 break;
     case GTK_PLOT_CANVAS_ACTION_INACTIVE:
     default:
         break;
  }

  return TRUE;

}

static gint
gtk_plot_canvas_button_press(GtkWidget *widget, GdkEventButton *event)
{
  GtkPlotCanvas *canvas = NULL;
  GtkPlotCanvasChild active_item;
  GtkPlot *active_plot = NULL;
  GList *plots = NULL;
  GList *dataset = NULL;
  GList *childs = NULL;
  GtkPlot *plot = NULL;
  GtkPlot *click_plot = NULL;
  GtkPlotData *data = NULL;
  GtkPlotText *child_text;
  GtkAllocation internal_allocation;
  GtkAllocation legends_allocation;
  GdkRectangle area, drag_area;
  GtkPlotAxis *axis[4];
  GdkModifierType mods;
  gint i = 0;
  gint x = 0, y = 0;
  gdouble xi = 0.0, yi = 0.0;
  gint px = 0, py = 0;
  gint pwidth = 0, pheight = 0;
  gint tx = 0, ty = 0;
  gint rx = 0, ry = 0;
  gint twidth = 0, theight = 0;
  gboolean veto;
  gboolean new_selection = FALSE;
  gboolean double_click = FALSE;
  guint state = GTK_STATE_NORMAL;
  GtkPlotCanvasPos pos, new_pos = 0;
  gdouble m;

  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);
  active_plot = canvas->active_plot;
  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);

/**********************************************************************/
  childs = canvas->childs;

  while(childs)
    {
      GtkPlotCanvasChild *child;
      gint x1, x2, y1, y2;
      gint width, height;

      child = (GtkPlotCanvasChild *)childs->data;
      gtk_plot_canvas_get_pixel(GTK_PLOT_CANVAS(widget), 
                                child->rx1, child->ry1,
                                &x1, &y1);
      gtk_plot_canvas_get_pixel(GTK_PLOT_CANVAS(widget), 
                                child->rx2, child->ry2,
                                &x2, &y2);

      width = abs(x2 - x1);
      height = abs(y2 - y1);

      area.x = MIN(x1, x2);
      area.y = MIN(y1, y2);
      area.width = width;
      area.height = height;

      if((pos = posible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){ 
          active_item.data = child;
          active_item.allocation = area;
          active_item.min_width = 0;
          active_item.min_height = 0;
          active_item.type = child->type;
          active_item.state = GTK_STATE_SELECTED;
          active_item.flags = child->flags;
          state = GTK_STATE_SELECTED;
          new_selection = TRUE;
          new_pos = pos;
      }

      childs = childs->next;
    }

/**********************************************************************/
/*  text = canvas->text;

  while(text)
    {
      child_text = (GtkPlotText *)text->data;
      gtk_plot_canvas_get_pixel(GTK_PLOT_CANVAS(widget), 
                                child_text->x, child_text->y,
                                &tx, &ty);

      gtk_plot_text_get_area(child_text->text,
                             child_text->angle,
			     child_text->justification,
			     child_text->font,
			     roundint(m * child_text->height), 
                             &rx, &ry, &twidth, &theight); 

      area.x = tx + rx;
      area.y = ty + ry;
      area.width = twidth;
      area.height = theight;

      if((pos = posible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){ 
          active_item.data = text->data;
          active_item.allocation = area;
          active_item.type = GTK_PLOT_CANVAS_TEXT;
          active_item.state = GTK_STATE_SELECTED;
          active_item.flags = GTK_PLOT_CANVAS_CAN_MOVE;
          state = GTK_STATE_SELECTED;
          new_selection = TRUE;
          new_pos = pos;
      }

      text = text->next;
    }
*/
/**********************************************************************/
  plots = canvas->plots;
  while(plots)
     {
       plot = (GtkPlot *)plots->data;

       if(!GTK_IS_PLOT3D(plot)){
         axis[0]=plot->left;
         axis[1]=plot->right;
         axis[2]=plot->top;
         axis[3]=plot->bottom;

         for(i = 0; i <= 3; i++){
            if(axis[i]->title_visible){
              child_text = &axis[i]->title;
              gtk_plot_canvas_get_real_pixel(GTK_WIDGET(plot), 
                                             child_text->x, child_text->y,
                                             &tx, &ty);
              gtk_plot_text_get_area(child_text->text,
  				     child_text->angle,
  				     child_text->justification,
  		  	  	     child_text->font,
				     roundint(child_text->height * m),
                                     &rx, &ry, &twidth, &theight);

              area.x = tx + rx;
              area.y = ty + ry;
              area.width = twidth;
              area.height = theight;

              if((pos = posible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){ 
                active_item.data = axis[i];
                active_item.allocation = area;
                active_item.type = GTK_PLOT_CANVAS_TITLE;
                active_item.state = GTK_STATE_SELECTED;
                active_item.flags = GTK_PLOT_CANVAS_CAN_MOVE;
                state = GTK_STATE_SELECTED;
                click_plot = plot;
                new_selection = TRUE;
                new_pos = pos;
                break;
              }
            }
         }
       }

       plots = plots->next;
     }

/**********************************************************************/
 
  plots = canvas->plots;
  while(plots)
   {
     plot = (GtkPlot *)plots->data;

     if(plot->show_legends){

       legends_allocation = gtk_plot_legends_get_allocation (plot);
       area.x = legends_allocation.x;
       area.y = legends_allocation.y;
       area.width = legends_allocation.width;
       area.height = legends_allocation.height;

       if((pos = posible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){ 
           active_item.data = plot;
           active_item.allocation = area;
           active_item.type = GTK_PLOT_CANVAS_LEGENDS;
           active_item.state = GTK_STATE_SELECTED;
           active_item.flags = GTK_PLOT_CANVAS_CAN_MOVE;
           state = GTK_STATE_SELECTED;
           click_plot = plot;
           new_selection = TRUE;
           new_pos = pos;
           break;
       }
     }
     plots=plots->next;
   } 

/**********************************************************************/
  if(!new_selection){

     plots = canvas->plots;
     while(plots)
        {
          plot = (GtkPlot *)plots->data;

          internal_allocation = gtk_plot_get_internal_allocation (plot);
          px = internal_allocation.x;
          py = internal_allocation.y;
          pwidth = internal_allocation.width;
          pheight = internal_allocation.height;
  
          if(!GTK_IS_PLOT3D(plot)){
            area.x = px - 6;
            area.y = py;
            area.width = 6;
            area.height = pheight;
            if((pos = posible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){
                active_item.data = &plot->left;
                active_item.allocation = area;
                active_item.type = GTK_PLOT_CANVAS_AXIS;
                active_item.state = GTK_STATE_SELECTED;
                active_item.flags = GTK_PLOT_CANVAS_FROZEN;
                state = GTK_STATE_SELECTED;
                new_pos = pos;
                click_plot = plot;
            }
            area.x = px + pwidth;
            if((pos = posible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){
                active_item.data = &plot->right;
                active_item.allocation = area;
                active_item.type = GTK_PLOT_CANVAS_AXIS;
                active_item.state = GTK_STATE_SELECTED;
                active_item.flags = GTK_PLOT_CANVAS_FROZEN;
                state = GTK_STATE_SELECTED;
                new_pos = pos;
                click_plot = plot;
            }
            area.x = px;
            area.y = py - 6;
            area.width = pwidth;
            area.height = 6;
            if((pos = posible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){
                active_item.data = &plot->top;
                active_item.allocation = area;
                active_item.type = GTK_PLOT_CANVAS_AXIS;
                active_item.state = GTK_STATE_SELECTED;
                active_item.flags = GTK_PLOT_CANVAS_FROZEN;
                state = GTK_STATE_SELECTED;
                new_pos = pos;
                click_plot = plot;
            }
            area.y = py + pheight;
            if((pos = posible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){
                active_item.data = &plot->bottom;
                active_item.allocation = area;
                active_item.type = GTK_PLOT_CANVAS_AXIS;
                active_item.state = GTK_STATE_SELECTED;
                active_item.flags = GTK_PLOT_CANVAS_FROZEN;
                state = GTK_STATE_SELECTED;
                new_pos = pos;
                click_plot = plot;
            }
          }
  
          area.x = px;
          area.y = py;
          area.width = pwidth;
          area.height = pheight;

          if((pos = posible_selection(area, x, y)) != GTK_PLOT_CANVAS_OUT){
              active_item.data = plot;
              active_item.allocation = area;
              active_item.min_width = 2 * DEFAULT_MARKER_SIZE;
              active_item.min_height = 2 * DEFAULT_MARKER_SIZE;
              active_item.type = GTK_PLOT_CANVAS_PLOT;
              active_item.state = GTK_STATE_SELECTED;
              active_item.flags = GTK_PLOT_CANVAS_CAN_MOVE |
                                  GTK_PLOT_CANVAS_CAN_X_RESIZE |
                                  GTK_PLOT_CANVAS_CAN_Y_RESIZE;
              state = GTK_STATE_SELECTED;
              new_pos = pos;
              click_plot = plot;
          }

          plots=plots->next;
        } 
  }

/**********************************************************************/
  if(!active_plot) active_plot = click_plot;

  if(active_plot && !GTK_IS_PLOT3D(active_plot)){
    dataset = active_plot->data_sets;

    while(dataset)
     {
       data = GTK_PLOT_DATA(dataset->data);

       if(!data->is_function){
         for(i = 0; i < data->num_points; i++){
           gtk_plot_get_pixel(GTK_PLOT(active_plot), data->x[i], data->y[i], &xi, &yi);
           if(abs(xi-x) <= DEFAULT_MARKER_SIZE && 
              abs(yi-y) <= DEFAULT_MARKER_SIZE){
                gdouble ax, ay;
                active_item.type = GTK_PLOT_CANVAS_DATA;
                active_item.state = GTK_STATE_SELECTED;
                active_item.flags = GTK_PLOT_CANVAS_CAN_MOVE;
                gtk_plot_get_pixel(GTK_PLOT(active_plot), 
                                   canvas->active_x,
                                   canvas->active_y,
                                   &ax, &ay);
                x = ax;
                y = ay;
                active_item.allocation.x = xi;
                active_item.allocation.y = yi;
                active_item.allocation.width = 20;
                active_item.allocation.height = 20;
                active_item.data = data;
                new_pos = GTK_PLOT_CANVAS_IN;
                state = GTK_STATE_SELECTED;
                break;
           }
         }
       }
       if(active_item.type == GTK_PLOT_CANVAS_DATA) break;
       dataset = dataset->next;
     }
  }

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

  if(state == GTK_STATE_SELECTED){
    gboolean new_item;
    GtkPlot *aux_plot = canvas->active_plot;
    GtkPlotData *aux_data = canvas->active_data;
    gint aux_point = canvas->active_point;
    gdouble aux_x = canvas->active_x;
    gdouble aux_y = canvas->active_y;

    if(active_item.type == GTK_PLOT_CANVAS_DATA)
       {
                canvas->active_data = data;
                canvas->active_point = i;
                canvas->active_x = data->x[i];
                canvas->active_y = data->y[i];
       }

    if(click_plot) canvas->active_plot = click_plot;

    veto = TRUE;
    gtk_signal_emit(GTK_OBJECT(canvas), canvas_signals[SELECT_ITEM],
                    event, &active_item, &veto);

    if(!veto)
              canvas->active_plot = aux_plot;

    new_item = (canvas->state != GTK_STATE_SELECTED ||
                active_item.type != canvas->active_item.type ||
                active_item.data != canvas->active_item.data);

    if(!new_item && active_item.type != GTK_PLOT_CANVAS_TEXT &&
       active_item.type != GTK_PLOT_CANVAS_ELLIPSE &&
       active_item.type != GTK_PLOT_CANVAS_RECTANGLE &&
       active_item.type != GTK_PLOT_CANVAS_LINE &&
       active_item.type != GTK_PLOT_CANVAS_CUSTOM &&
       click_plot != canvas->active_plot) new_item = TRUE;

    if(canvas->active_item.type == GTK_PLOT_CANVAS_DATA &&
       active_item.type == GTK_PLOT_CANVAS_DATA &&
       active_item.data == canvas->active_item.data)
       {
                new_item = !(aux_point == i); 
       }

    if(new_item &&
       ((active_item.type == GTK_PLOT_CANVAS_DATA &&
        GTK_PLOT_CANVAS_CAN_SELECT_POINT(canvas)) ||
        (active_item.type != GTK_PLOT_CANVAS_DATA &&
         GTK_PLOT_CANVAS_CAN_SELECT_ITEM(canvas)))) {

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

           draw_selection(canvas, active_item.allocation, TRUE);

           return TRUE;

         } else {

           if(active_item.type == GTK_PLOT_CANVAS_DATA){
       	       canvas->active_data = aux_data;
               canvas->active_point = aux_point;
               canvas->active_x = aux_x;
               canvas->active_y = aux_y;
           }

         }
    }

    if(!new_item &&
       ((active_item.type == GTK_PLOT_CANVAS_DATA &&
        GTK_PLOT_CANVAS_CAN_DND_POINT(canvas)) ||
        (active_item.type != GTK_PLOT_CANVAS_DATA &&
         GTK_PLOT_CANVAS_CAN_DND(canvas)))) {

         switch(new_pos){
          case GTK_PLOT_CANVAS_IN:
              canvas->action = GTK_PLOT_CANVAS_ACTION_DRAG;
              break;
          default:
              canvas->action = GTK_PLOT_CANVAS_ACTION_RESIZE;
         }
         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 = new_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)){
         active_item.data = NULL;
         active_item.allocation.x = x;
         active_item.allocation.y = y;
         active_item.allocation.width = 0;
         active_item.allocation.height = 0;
         drag_area = active_item.allocation;
         active_item.type = GTK_PLOT_CANVAS_NONE;
         active_item.state = GTK_STATE_SELECTED;
         active_item.flags = GTK_PLOT_CANVAS_CAN_X_RESIZE |
                             GTK_PLOT_CANVAS_CAN_Y_RESIZE;
         veto = TRUE;

         gtk_signal_emit(GTK_OBJECT(canvas), canvas_signals[SELECT_ITEM],
                         event, &canvas->active_item, &veto);
         if(veto){
           canvas->active_item = active_item;
           canvas->active_item.data = click_plot;
           canvas->active_plot = click_plot;
           canvas->state = GTK_STATE_SELECTED;
           canvas->action = GTK_PLOT_CANVAS_ACTION_SELECTION;
           canvas->drag_point = new_pos;
           canvas->drag_x = x;
           canvas->drag_y = y;
           canvas->pointer_x = x;
           canvas->pointer_y = y;
           canvas->drag_area = active_item.allocation;
           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, active_item.allocation, FALSE);
         }
  }

  return TRUE;  
}

 
static gint
gtk_plot_canvas_button_release(GtkWidget *widget, GdkEventButton *event)
{
  GtkPlotCanvas *canvas;
  GtkPlotCanvasChild *child;
  GtkPlotText *text;
  GtkPlotAxis *axis = NULL;
  GtkPlot *active_plot;
  gdouble x, y;
  gdouble dx, dy;
  gdouble new_x, new_y;
  gdouble new_width, new_height;
  gdouble x1 = 0., y1 = 0., x2 = 0., y2 = 0.;
  gboolean veto = TRUE;

  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);
  }

  active_plot = canvas->active_plot;
  if(canvas->action == GTK_PLOT_CANVAS_ACTION_INACTIVE) return TRUE;
  if(active_plot == NULL && canvas->action != GTK_PLOT_CANVAS_ACTION_SELECTION)
     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);

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


  if(canvas->action != GTK_PLOT_CANVAS_ACTION_SELECTION) {
   switch(canvas->active_item.type){
    case GTK_PLOT_CANVAS_TEXT:
         child = (GtkPlotCanvasChild *)canvas->active_item.data;
         text = (GtkPlotText *)child->data;
         gtk_signal_emit(GTK_OBJECT(canvas), canvas_signals[MOVE_ITEM],
                         &canvas->active_item, 
                         text->x + dx, text->y + dy, &veto);
         if(!veto) break;
         text->x += dx;
         text->y += dy;
         x1 += dx;
         x2 += dx;
         y1 += dy;
         y2 += dy;

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

         break;
    case GTK_PLOT_CANVAS_RECTANGLE:
    case GTK_PLOT_CANVAS_LINE:
    case GTK_PLOT_CANVAS_ELLIPSE:
    case GTK_PLOT_CANVAS_CUSTOM:
         child = (GtkPlotCanvasChild *)canvas->active_item.data;
         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_signal_emit(GTK_OBJECT(canvas), canvas_signals[MOVE_ITEM],
                         child, 
                         x1, x2, &veto);
         if(canvas->action != GTK_PLOT_CANVAS_ACTION_DRAG)
            gtk_signal_emit(GTK_OBJECT(canvas), 
                            canvas_signals[RESIZE_ITEM],
                            child, new_width, new_height, &veto);
         if(!veto) break;
         if(child->rx1 <= child->rx2) {
              child->rx1 = x1; 
              child->rx2 = x2; 
         } else {
              child->rx1 = x2; 
              child->rx2 = x1; 
         }
         if(child->ry1 <= child->ry2) {
              child->ry1 = y1; 
              child->ry2 = y2; 
         } else {
              child->ry1 = y2; 
              child->ry2 = y1; 
         }
         gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
         break;
    case GTK_PLOT_CANVAS_TITLE:
         axis = (GtkPlotAxis *)canvas->active_item.data;
         text = &axis->title;
         gtk_signal_emit(GTK_OBJECT(canvas), canvas_signals[MOVE_ITEM],
                         &canvas->active_item, 
                         text->x + dx, text->y + dy, &veto);
         if(!veto) break;
         text->x += dx;
         text->y += dy;
         gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
         break;
    case GTK_PLOT_CANVAS_LEGENDS:
         gtk_plot_legends_get_position(active_plot, &new_x, &new_y);
         new_x += dx / active_plot->width;
         new_y += dy / active_plot->height;
         gtk_signal_emit(GTK_OBJECT(canvas), canvas_signals[MOVE_ITEM],
                         &canvas->active_item, new_x, new_y, 
                         &veto);
         if(!veto) break;
         gtk_plot_legends_move(active_plot, new_x, new_y);
         gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
         break;
    case GTK_PLOT_CANVAS_PLOT:
         gtk_signal_emit(GTK_OBJECT(canvas), 
                         canvas_signals[MOVE_ITEM],
                         &canvas->active_item, new_x, new_y, &veto);
         if(canvas->action != GTK_PLOT_CANVAS_ACTION_DRAG)
            gtk_signal_emit(GTK_OBJECT(canvas), 
                            canvas_signals[RESIZE_ITEM],
                            &canvas->active_item, new_width, new_height, &veto);
         if(!veto) break;
         gtk_plot_move_resize(active_plot, 
                              new_x, 
                              new_y,
                              new_width,
                              new_height);
         gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
         break;
    case GTK_PLOT_CANVAS_DATA:
         if(canvas->active_data == NULL) break;
         if(canvas->active_point == -1) break;

         gtk_plot_get_pixel(GTK_PLOT(canvas->active_plot), 
                            canvas->active_data->x[canvas->active_point],
                            canvas->active_data->y[canvas->active_point],
                            &x, &y);

         x += (canvas->pointer_x - canvas->drag_x);
         y += (canvas->pointer_y - canvas->drag_y);

         gtk_plot_get_point(canvas->active_plot, x, y, &new_x, &new_y);

         gtk_signal_emit(GTK_OBJECT(canvas), canvas_signals[MOVE_ITEM],
                         &canvas->active_item, new_x, new_y, &veto);
         if(!veto) break;

         canvas->active_data->x[canvas->active_point] = new_x;
         canvas->active_data->y[canvas->active_point] = new_y;

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

         break;
    case GTK_PLOT_CANVAS_AXIS:
    case GTK_PLOT_CANVAS_NONE:
    default:
         break;
   }
  } 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);
    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; 
  canvas->active_item.allocation.x = canvas->drag_area.x;
  canvas->active_item.allocation.y = canvas->drag_area.y;

  if(veto){
      gtk_plot_canvas_paint(canvas);
      gtk_plot_canvas_refresh(canvas);
      canvas->active_item.allocation = canvas->drag_area;
      if((canvas->active_item.type == GTK_PLOT_CANVAS_DATA &&
          canvas->action == GTK_PLOT_CANVAS_ACTION_SELECTION) ||
         (canvas->active_item.type != GTK_PLOT_CANVAS_DATA &&
          canvas->action != GTK_PLOT_CANVAS_ACTION_SELECTION))
             draw_selection(canvas, canvas->active_item.allocation, TRUE);
  } else {
      canvas->state = GTK_STATE_NORMAL;
  }  

  if(canvas->active_item.type == GTK_PLOT_CANVAS_DATA &&
     canvas->action != GTK_PLOT_CANVAS_ACTION_SELECTION)
             canvas->state = GTK_STATE_NORMAL;
  canvas->active_data = NULL;
  canvas->active_point = -1;
  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_add_plot (GtkPlotCanvas *plot_canvas, 
			  GtkPlot *plot, gdouble x, gdouble y)
{
  gint width, height;

  width = plot_canvas->pixmap_width, 
  height = plot_canvas->pixmap_height;

  gtk_plot_set_magnification(plot, plot_canvas->magnification);
  gtk_widget_set_usize(GTK_WIDGET(plot), width, height); 
  gtk_plot_move(plot, x, y);

  plot->left->title.x = plot->x - .1;
  plot->right->title.x = plot->x + plot->width + .1;
  plot->top->title.y = plot->y - .05;
  plot->bottom->title.y = plot->y + plot->height + .055;

  plot_canvas->plots = g_list_append(plot_canvas->plots, plot);
  gtk_plot_canvas_set_plots_pixmap(plot_canvas);

  gtk_fixed_put(GTK_FIXED(plot_canvas), GTK_WIDGET(plot), 0, 0);

  GTK_WIDGET(plot)->allocation.width = width;
  GTK_WIDGET(plot)->allocation.height = height;

  plot_canvas->active_plot = plot;

  plot_canvas->num_plots++;
  gtk_signal_emit (GTK_OBJECT(plot_canvas), canvas_signals[CHANGED]);
}

static void
gtk_plot_canvas_set_plots_pixmap(GtkPlotCanvas *plot_canvas)
{
  GdkRectangle area;
  GList *plots;
  GtkPlot *plot;

  if(!plot_canvas->pixmap) return;
  plots = plot_canvas->plots;
  while(plots)
    {
      plot = GTK_PLOT(plots->data);
      gtk_plot_set_drawable(plot, plot_canvas->pixmap);
      area.x = GTK_WIDGET(plot)->allocation.x;
      area.y = GTK_WIDGET(plot)->allocation.y;
      area.width = GTK_WIDGET(plot)->allocation.width;
      area.height = GTK_WIDGET(plot)->allocation.height;
      gtk_widget_draw(GTK_WIDGET(plot), &area);
      plots = plots->next;
    }

}

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));

  canvas->background = *color;

  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 = canvas->pixmap_width * px;
  *y = 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;
}

static void
gtk_plot_canvas_get_real_pixel(GtkWidget *widget, gdouble px, gdouble py,
                               gint *x, gint *y)
{
  *x = widget->allocation.x + widget->allocation.width * px;
  *y = widget->allocation.y + widget->allocation.height * py;
}

void
gtk_plot_canvas_set_active_plot (GtkPlotCanvas *plot_canvas, GtkPlot *plot)
{
  plot_canvas->active_plot = plot;
}

void
gtk_plot_canvas_unselect (GtkPlotCanvas *plot_canvas)
{

  if(plot_canvas->state == GTK_STATE_SELECTED){
    if(plot_canvas->active_item.type == GTK_PLOT_CANVAS_NONE)
       draw_selection(plot_canvas, plot_canvas->drag_area, FALSE); 
    else
       draw_selection(plot_canvas, plot_canvas->drag_area, TRUE); 
  }

  plot_canvas->action = GTK_PLOT_CANVAS_ACTION_INACTIVE;
  plot_canvas->state = GTK_STATE_NORMAL;
  plot_canvas->active_item.type = GTK_PLOT_CANVAS_NONE;
  plot_canvas->active_item.data = 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);
}


GtkPlot *
gtk_plot_canvas_get_active_plot(GtkPlotCanvas *canvas)
{
  return canvas->active_plot;
}


GtkPlotData *
gtk_plot_canvas_get_active_data(GtkPlotCanvas *canvas)
{
  return canvas->active_data;
}

gint
gtk_plot_canvas_get_active_point(GtkPlotCanvas *canvas, gdouble *x, gdouble *y)
{
  if (canvas->active_point >= 0 && canvas->active_data != NULL) {
    *x = canvas->active_data->x[canvas->active_point];
    *y = canvas->active_data->y[canvas->active_point];
  } else {
    *x = 0.0;
    *y = 0.0;
  }
/* Returns -1 if no point is active. */
  return canvas->active_point;
}

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

void
gtk_plot_canvas_set_size(GtkPlotCanvas *canvas, gint width, gint height)
{
  GList *plots;
  GtkPlot *plot;
  gdouble m = canvas->magnification;

  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);
  }

  plots = canvas->plots;
  while(plots)
     {
       plot = (GtkPlot *)plots->data;
 
       gtk_widget_set_usize(GTK_WIDGET(plot), 
                            canvas->pixmap_width, canvas->pixmap_height); 

       plots = plots->next;
     }
  gtk_widget_set_usize(GTK_WIDGET(canvas), 
                       canvas->pixmap_width, canvas->pixmap_height);

  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(GTK_IS_PLOT_GDK(canvas->pc)){
       GTK_PLOT_GDK(canvas->pc)->drawable = canvas->pixmap;
       GTK_PLOT_GDK(canvas->pc)->window = GTK_WIDGET(canvas)->window;
  }

  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_set_plots_pixmap(canvas);

}

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);
  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 *plots;
  GtkPlot *plot;
  gdouble m;

  m = canvas->magnification = magnification;

  plots = canvas->plots;
  while(plots)
     {
       plot = (GtkPlot *)plots->data;
 
       gtk_plot_set_magnification(plot, m); 

       plots = plots->next;
     }

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

static void
draw_selection (GtkPlotCanvas *canvas, 
                GdkRectangle area,
                gboolean markers)
{
  GdkGC *xor_gc, *gc;
  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.type == GTK_PLOT_CANVAS_DATA){
    gint x, y;
    gdouble old_x, old_y;
    gdouble new_x, new_y;

    area.x -= area.width / 2;
    area.y -= area.height / 2;
    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(canvas->action == GTK_PLOT_CANVAS_ACTION_DRAG){
      gdouble ax, ay;

      old_x = canvas->active_data->x[canvas->active_point];
      old_y = canvas->active_data->y[canvas->active_point];

      gtk_plot_get_pixel(GTK_PLOT(canvas->active_plot), old_x, old_y, &ax, &ay);

      x = ax + canvas->pointer_x - canvas->drag_x;
      y = ay + canvas->pointer_y - canvas->drag_y;

      gtk_plot_get_point(canvas->active_plot, x, y, &new_x, &new_y);

      canvas->active_data->x[canvas->active_point] = new_x;
      canvas->active_data->y[canvas->active_point] = new_y;

      gtk_plot_set_drawable(canvas->active_plot, GTK_WIDGET(canvas)->window);
     
      gc = GTK_PLOT_GDK(canvas->active_data->plot->pc)->gc;
      GTK_PLOT_GDK(canvas->active_data->plot->pc)->gc = xor_gc;
 
      gtk_plot_data_draw_points(canvas->active_data, canvas->active_data->num_points);
      GTK_PLOT_GDK(canvas->active_data->plot->pc)->gc = gc;
      gtk_plot_set_drawable(canvas->active_plot, canvas->pixmap);

      canvas->active_data->x[canvas->active_point] = old_x;
      canvas->active_data->y[canvas->active_point] = old_y;
    }

    return;
  }


  if(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);         
  }
  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);
}

static GtkPlotCanvasPos
posible_selection(GdkRectangle 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, gint 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]);
}

/**********************************************************************/
GtkPlotCanvasChild *
gtk_plot_canvas_put_text (GtkPlotCanvas *canvas,
                          gdouble x, gdouble y, 
                          const gchar *font, gint height, gint angle,
                          const GdkColor *fg, const GdkColor *bg,
			  gboolean transparent,
                          GtkJustification justification,
                          const gchar *text)
{
  GtkWidget *widget;
  GtkPlotText *text_attr;
  GtkPlotCanvasChild *child;

  widget = GTK_WIDGET(canvas);

  child = gtk_plot_canvas_child_new(GTK_PLOT_CANVAS_TEXT);

  text_attr = (GtkPlotText *)child->data;

  text_attr->x = x;
  text_attr->y = y;
  text_attr->angle = angle;
  text_attr->fg = widget->style->black;
  text_attr->bg = widget->style->white;
  text_attr->justification = justification;
  text_attr->transparent = transparent;
  text_attr->border = 0;
  text_attr->border_width = 0;
  text_attr->shadow_width = 0;

  if(!font) {
    text_attr->font = g_strdup(DEFAULT_FONT);
    text_attr->height = DEFAULT_FONT_HEIGHT;
  } else {
    text_attr->font = g_strdup(font);
    text_attr->height = height;
  }

  text_attr->text = NULL;
  if(text) text_attr->text = g_strdup(text);

  if(fg != NULL)
    text_attr->fg = *fg;

  if(bg != NULL)
    text_attr->bg = *bg;

  canvas->childs = g_list_append(canvas->childs, child);
  gtk_plot_canvas_draw_child(canvas, child);

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

GtkPlotCanvasChild *
gtk_plot_canvas_put_line(GtkPlotCanvas *canvas,
                         gdouble x1, gdouble y1, 
                         gdouble x2, gdouble y2, 
		 	 GtkPlotLineStyle style,
			 gfloat width,
			 const GdkColor *color,
			 GtkPlotCanvasArrow arrow_mask)
{
  GtkPlotCanvasChild *child;
  GtkPlotCanvasLine *line;

  child = gtk_plot_canvas_child_new(GTK_PLOT_CANVAS_LINE);
  line = (GtkPlotCanvasLine *)child->data;

  gdk_color_black(gdk_colormap_get_system(), &line->line.color); 
  
  line->arrow_length = 8;
  line->arrow_width = 8;
  line->arrow_style = GTK_PLOT_SYMBOL_FILLED;

  gtk_plot_canvas_line_set_attributes(child, style, width, color, arrow_mask);

  gtk_plot_canvas_put_child(canvas, child, x1, y1, x2, y2);
  return child;
}

GtkPlotCanvasChild *
gtk_plot_canvas_put_rectangle(GtkPlotCanvas *canvas,
                              gdouble x1, gdouble y1, 
                              gdouble x2, gdouble y2, 
		  	      GtkPlotLineStyle style,
			      gfloat width,
			      const GdkColor *fg,
			      const GdkColor *bg,
			      GtkPlotBorderStyle border,
			      gboolean fill)
{
  GtkPlotCanvasChild *child;
  GtkPlotCanvasRectangle *rectangle;

  child = gtk_plot_canvas_child_new(GTK_PLOT_CANVAS_RECTANGLE);
  rectangle = (GtkPlotCanvasRectangle *)child->data;
 
  gdk_color_black(gdk_colormap_get_system(), &rectangle->line.color); 
  gdk_color_white(gdk_colormap_get_system(), &rectangle->bg); 

  gtk_plot_canvas_rectangle_set_attributes(child, style, width, 
                                           fg, bg, border, fill);
  rectangle->shadow_width = SHADOW_WIDTH;

  gtk_plot_canvas_put_child(canvas, child, x1, y1, x2, y2);
  return child;
}

GtkPlotCanvasChild *
gtk_plot_canvas_put_ellipse(GtkPlotCanvas *canvas,
                            gdouble x1, gdouble y1, 
                            gdouble x2, gdouble y2, 
		  	    GtkPlotLineStyle style,
			    gfloat width,
			    const GdkColor *fg,
			    const GdkColor *bg,
			    gboolean fill)
{
  GtkPlotCanvasChild *child;
  GtkPlotCanvasEllipse *ellipse;

  child = gtk_plot_canvas_child_new(GTK_PLOT_CANVAS_ELLIPSE);
  ellipse = (GtkPlotCanvasEllipse *)child->data;
 
  gdk_color_black(gdk_colormap_get_system(), &ellipse->line.color); 
  gdk_color_white(gdk_colormap_get_system(), &ellipse->bg); 

  gtk_plot_canvas_ellipse_set_attributes(child, style, width, 
                                         fg, bg, fill);

  gtk_plot_canvas_put_child(canvas, child, x1, y1, x2, y2);
  return child;
}

void
gtk_plot_canvas_line_set_attributes(GtkPlotCanvasChild *child,
		 	            GtkPlotLineStyle style,
			            gfloat width,
			            const GdkColor *color,
			            GtkPlotCanvasArrow mask)
{
  GtkPlotCanvasLine *line;

  g_return_if_fail(child->type == GTK_PLOT_CANVAS_LINE);

  line = (GtkPlotCanvasLine *)child->data;  
  if(color)
      line->line.color = *color;
  line->line.line_width = width;
  line->line.line_style = style;
  line->arrow_mask = mask;
}

void
gtk_plot_canvas_rectangle_set_attributes(GtkPlotCanvasChild *child,
		  	                 GtkPlotLineStyle style,
			                 gfloat width,
			                 const GdkColor *fg,
			                 const GdkColor *bg,
				         GtkPlotBorderStyle border,
			                 gboolean fill)
{
  GtkPlotCanvasRectangle *rectangle;

  g_return_if_fail(child->type == GTK_PLOT_CANVAS_RECTANGLE);

  rectangle = (GtkPlotCanvasRectangle *)child->data;  
  if(fg)
    rectangle->line.color = *fg;
  if(bg)
    rectangle->bg = *bg;
  rectangle->line.line_width = width;
  rectangle->line.line_style = style;
  rectangle->filled = fill;
  rectangle->border = border;
}

void
gtk_plot_canvas_ellipse_set_attributes(GtkPlotCanvasChild *child,
		  	               GtkPlotLineStyle style,
			               gfloat width,
			               const GdkColor *fg,
			               const GdkColor *bg,
			               gboolean fill)
{
  GtkPlotCanvasEllipse *ellipse;

  g_return_if_fail(child->type == GTK_PLOT_CANVAS_ELLIPSE);

  ellipse = (GtkPlotCanvasEllipse *)child->data;  
  if(fg)
    ellipse->line.color = *fg;
  if(bg)
    ellipse->bg = *bg;
  ellipse->line.line_width = width;
  ellipse->line.line_style = style;
  ellipse->filled = fill;
}


GtkPlotCanvasChild *
gtk_plot_canvas_child_new(GtkPlotCanvasType type)
{
  GtkPlotCanvasChild *child;
  child = g_new0(GtkPlotCanvasChild, 1);
  child->type = type;

  child->flags = GTK_PLOT_CANVAS_CAN_MOVE | 
                 GTK_PLOT_CANVAS_CAN_X_RESIZE |
                 GTK_PLOT_CANVAS_CAN_Y_RESIZE;

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

  switch(type){
    case GTK_PLOT_CANVAS_LINE:
	child->data = g_new0(GtkPlotCanvasLine, 1);
        break;    
    case GTK_PLOT_CANVAS_RECTANGLE:
	child->data = g_new0(GtkPlotCanvasRectangle, 1);
        break;    
    case GTK_PLOT_CANVAS_ELLIPSE:
	child->data = g_new0(GtkPlotCanvasEllipse, 1);
        break;    
    case GTK_PLOT_CANVAS_TEXT:
	child->data = g_new0(GtkPlotText, 1);
        child->flags = GTK_PLOT_CANVAS_CAN_MOVE;
        break;    
    case GTK_PLOT_CANVAS_CUSTOM:
        child->data = NULL;
        child->flags = GTK_PLOT_CANVAS_CAN_MOVE;
    default:
	break;
  }
  
  child->draw_child = NULL;
  child->print_child = NULL;
  return child;
}

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;

  canvas->childs = g_list_append(canvas->childs, child);
  gtk_plot_canvas_draw_child(canvas, child);
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

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;

  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;

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

gboolean
gtk_plot_canvas_remove_child(GtkPlotCanvas *canvas, 
                             GtkPlotCanvasChild *child)
{
  GList *list;
  gpointer data;

  list = canvas->childs;

  while(list)
   {
     data = list->data;

     if((GtkPlotCanvasChild *)data == child){
        gboolean veto = TRUE;

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

        if(veto){
          g_free(child->data);
          g_free(child);

          canvas->childs = g_list_remove_link(canvas->childs, list);
          g_list_free_1(list);
          gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
        }

        return TRUE;
     }
     list = list->next;
   }

  return FALSE;

}


/**********************************************************************/
static void
gtk_plot_canvas_draw_child(GtkPlotCanvas *canvas,
                           GtkPlotCanvasChild *child)
{
  GtkPlotCanvasLine *line = NULL;
  GtkPlotCanvasRectangle *rectangle = NULL;
  GtkPlotCanvasEllipse *ellipse = NULL;
  GtkPlotText *text = NULL;
  GtkPlotPoint arrow[3];
  gint rx1 = 0, ry1 = 0, rx2 = 0, ry2 = 0;
  gint xmin, xmax, ymin, ymax;
  gint width = 0, height = 0;
  gint xm = 0, ym = 0;
  gdouble angle = 0.;
  gdouble m = canvas->magnification;

  if(!canvas->pixmap) return;

  gtk_plot_pc_gsave(canvas->pc);

  gtk_plot_canvas_get_pixel(canvas, child->rx1, child->ry1, &rx1, &ry1);
  gtk_plot_canvas_get_pixel(canvas, child->rx2, child->ry2, &rx2, &ry2);

  xmin = MIN(rx1, rx2);
  xmax = MAX(rx1, rx2);
  ymin = MIN(ry1, ry2);
  ymax = MAX(ry1, ry2);

  width = abs(rx2 - rx1);
  height = abs(ry2 - ry1);

  child->allocation.x = xmin;
  child->allocation.y = ymin;
  child->allocation.width = width;
  child->allocation.height = height;

  switch(child->type){
    case GTK_PLOT_CANVAS_LINE:
        line = (GtkPlotCanvasLine *)child->data;

        if(width == 0 && height == 0) return;
        if(width != 0) 
            angle = atan2((gdouble)(ry2 - ry1), (gdouble)(rx2 - rx1));  
        else   
            angle = asin((ry2 - ry1)/height);

        gtk_plot_canvas_set_line_attributes(canvas, line->line);
        gtk_plot_pc_set_lineattr(canvas->pc, line->line.line_width, 0, 0, 0);
        gtk_plot_pc_draw_line(canvas->pc, rx1, ry1, rx2, ry2);
        if(line->arrow_mask & GTK_PLOT_ARROW_END){
           arrow[1].x = rx2;
           arrow[1].y = ry2;
           xm = rx2 - cos(angle) * line->arrow_length * m;
           ym = ry2 - sin(angle) * line->arrow_length * m;
           arrow[0].x = xm + sin(angle)* line->arrow_width * m / 2.0;
           arrow[0].y = ym - cos(angle)* line->arrow_width * m / 2.0;
           arrow[2].x = xm - sin(angle)* line->arrow_width * m / 2.0;
           arrow[2].y = ym + cos(angle)* line->arrow_width * m / 2.0;
           switch(line->arrow_style){
             case GTK_PLOT_SYMBOL_EMPTY:
               gtk_plot_pc_draw_lines (canvas->pc, arrow, 3);
               break;
             case GTK_PLOT_SYMBOL_OPAQUE:
               gtk_plot_pc_set_color(canvas->pc, &canvas->background);
               gtk_plot_pc_draw_polygon (canvas->pc, TRUE, arrow, 3);
               gtk_plot_pc_set_color(canvas->pc, &line->line.color);
               gtk_plot_pc_draw_polygon (canvas->pc, FALSE, arrow, 3);
               break;
             case GTK_PLOT_SYMBOL_FILLED:
               gtk_plot_pc_draw_polygon (canvas->pc, TRUE, arrow, 3);
           }
        }
        if(line->arrow_mask & GTK_PLOT_ARROW_ORIGIN){
           arrow[1].x = rx1;
           arrow[1].y = ry1;
           xm = rx1 + cos(angle) * line->arrow_length * m;
           ym = ry1 + sin(angle) * line->arrow_length * m;
           arrow[0].x = xm + sin(angle)* line->arrow_width * m;
           arrow[0].y = ym - cos(angle)* line->arrow_width * m;
           arrow[2].x = xm - sin(angle)* line->arrow_width * m;
           arrow[2].y = ym + cos(angle)* line->arrow_width * m;
           switch(line->arrow_style){
             case GTK_PLOT_SYMBOL_EMPTY:
               gtk_plot_pc_draw_lines (canvas->pc, arrow, 3);
               break;
             case GTK_PLOT_SYMBOL_OPAQUE:
               gtk_plot_pc_set_color(canvas->pc, &canvas->background);
               gtk_plot_pc_draw_polygon (canvas->pc, TRUE, arrow, 3);
               gtk_plot_pc_set_color(canvas->pc, &line->line.color);
               gtk_plot_pc_draw_polygon (canvas->pc, FALSE, arrow, 3);
               break;
             case GTK_PLOT_SYMBOL_FILLED:
               gtk_plot_pc_draw_polygon (canvas->pc, TRUE, arrow, 3);
           }
        }
        break;    
    case GTK_PLOT_CANVAS_RECTANGLE:
        rectangle = (GtkPlotCanvasRectangle *)child->data;
        if(rectangle->filled){
           gtk_plot_pc_set_color(canvas->pc, &rectangle->bg);
           gtk_plot_pc_draw_rectangle(canvas->pc, TRUE, 
                                     xmin, ymin, width, height);
        }
        if(rectangle->line.line_style != GTK_PLOT_LINE_NONE &&
           rectangle->border != GTK_PLOT_BORDER_NONE){

            gtk_plot_canvas_set_line_attributes(canvas, rectangle->line);
            gtk_plot_pc_draw_rectangle(canvas->pc, FALSE, 
                                      xmin, ymin, width, height);
            if(rectangle->border == GTK_PLOT_BORDER_SHADOW){
              gtk_plot_pc_draw_rectangle(canvas->pc,
                                 TRUE,
                                 xmin + roundint(rectangle->shadow_width * m),
                                 ymin + height,
                                 width, roundint(rectangle->shadow_width * m));
              gtk_plot_pc_draw_rectangle(canvas->pc,
                                 TRUE,
                                 xmin + width,
                                 ymin + roundint(rectangle->shadow_width * m),
                                 roundint(rectangle->shadow_width * m), height);
            }
        }
        break;    
    case GTK_PLOT_CANVAS_ELLIPSE:
        ellipse = (GtkPlotCanvasEllipse *)child->data;
        if(ellipse->filled){
           gtk_plot_pc_set_color(canvas->pc, &ellipse->bg);
           gtk_plot_pc_draw_ellipse(canvas->pc, TRUE, 
                                   xmin, ymin, width, height);
        }
        gtk_plot_canvas_set_line_attributes(canvas, ellipse->line);
        if(ellipse->line.line_style != GTK_PLOT_LINE_NONE)
           gtk_plot_pc_draw_ellipse(canvas->pc, FALSE, 
                                   xmin, ymin, width, height);
        break;    
    case GTK_PLOT_CANVAS_TEXT:
        text = (GtkPlotText *)child->data;
        gtk_plot_canvas_draw_text(canvas, child);
        break;
    case GTK_PLOT_CANVAS_CUSTOM:
    default:
        if(child->draw_child) child->draw_child(canvas, child);
        break;
  }

  gtk_plot_pc_grestore(canvas->pc);
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

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

static void
gtk_plot_canvas_draw_text(GtkPlotCanvas *canvas,
                          GtkPlotCanvasChild *child)
{
  GtkPlotText *text;
  gint x, y;
  gint width, height;
  gdouble m = canvas->magnification;
  gint tx, ty;

  if(!canvas->pixmap) return;

  text = (GtkPlotText *)child->data;

  x = text->x * canvas->pixmap_width;
  y = text->y * canvas->pixmap_height;

  gtk_plot_text_get_area(text->text, text->angle, text->justification, 
                         text->font, roundint(m * text->height), 
                         &tx, &ty, &width, &height);

  tx += x;
  ty += y;

  gtk_plot_canvas_get_position(canvas, tx, ty,
                               &child->rx1, &child->ry1);
  gtk_plot_canvas_get_position(canvas, tx + width, ty + height,
                               &child->rx2, &child->ry2);


  if(GTK_IS_PLOT_GDK(canvas->pc))
        GTK_PLOT_GDK(canvas->pc)->window = GTK_WIDGET(canvas)->window;

  gtk_plot_pc_draw_string(canvas->pc,
                         x, y,
                         text->angle,
                         &text->fg,
                         &text->bg,
                         text->transparent,
                         text->border,
                         text->border_width,
                         text->shadow_width,
                         text->font,
                         roundint(text->height * m),
                         text->justification,
                         text->text);
  gtk_signal_emit (GTK_OBJECT(canvas), canvas_signals[CHANGED]);
}

static gint
roundint (gdouble x)
{
 gint sign = 1;

/* if(x <= 0.) sign = -1;
*/
 return (x+sign*.50999999471);

}

static 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;
  } 
}

