/* Handled box type for GnomeCanvas widget
 *
 * Copyright (C) 1998 Free Software Foundation
 *
 * Developed by Havoc Pennington <hp@pobox.com>
 *
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gnome.h>
#include <libgnomeui/gnome-canvas-util.h> /* FIXME */
#include "gnome-canvas-handle-box.h"

#include <math.h>
#include <time.h>

#include <gtk/gtksignal.h>

static void gnome_canvas_handle_box_class_init (GnomeCanvasHandleBoxClass *class);
static void gnome_canvas_handle_box_init       (GnomeCanvasHandleBox      *handle);
static void gnome_canvas_handle_box_destroy    (GtkObject            *object);
static void gnome_canvas_handle_box_set_arg    (GtkObject            *object,
						 GtkArg               *arg,
						 guint                 arg_id);
static void gnome_canvas_handle_box_get_arg    (GtkObject            *object,
						 GtkArg               *arg,
						 guint                 arg_id);

static void gnome_canvas_handle_box_create    (GnomeCanvasHandled* handled, GdkEventButton* event);

static void gnome_canvas_handle_box_scale(GnomeCanvasHandled* handled,
					  gdouble x1, gdouble y1,
					  gdouble x2, gdouble y2);

static void gnome_canvas_handle_box_size (GnomeCanvasHandled* handled, 
					  double* x1, double* y1, 
					  double* x2, double* y2);

static void gnome_canvas_handle_box_realize (GnomeCanvasItem *item);

static void gnome_canvas_handle_box_unrealize   (GnomeCanvasItem *item);

static void gnome_canvas_handle_box_reconfigure (GnomeCanvasItem *item);

static void gnome_canvas_handle_box_handle_moved(GnomeCanvasItem* item, gdouble x, gdouble y,
						 gpointer data);

static gint gnome_canvas_handle_box_item_event(GnomeCanvasItem* item, GdkEvent* event, 
					       GnomeCanvasHandleBox* handle_box);

typedef enum {
  NW,
  N,
  NE,
  W,
  E,
  SW,
  S,
  SE,
  LAST_BOX_HANDLE
} BoxHandle;

/* Cute idea from Miguel */
#define HANDLE(x) (1 << (x))
#define ALL_HANDLES (HANDLE(NW) | HANDLE(N) | HANDLE(NE) | HANDLE(W) | \
		     HANDLE(E) | HANDLE(SW) | HANDLE(S) | HANDLE(SE))

#define CORNER_HANDLES (HANDLE(NW) | HANDLE(NE) | HANDLE(SW) | HANDLE(SE))
#define VERTICAL_HANDLES (HANDLE(N) | HANDLE(S))
#define HORIZONTAL_HANDLES (HANDLE(W) | HANDLE(E))
#define WEST_HANDLES (HANDLE(SW) | HANDLE(NW) | HANDLE(W))
#define EAST_HANDLES (HANDLE(SE) | HANDLE(NE) | HANDLE(E))
#define NORTH_HANDLES (HANDLE(NW) | HANDLE(N) | HANDLE(NE))
#define SOUTH_HANDLES (HANDLE(SW) | HANDLE(S) | HANDLE(SE))

enum {
  ARG_0,
  ARG_X1,
  ARG_Y1,
  ARG_X2,
  ARG_Y2
};


enum {
  LAST_SIGNAL
};

static GnomeCanvasHandledClass *parent_class;

/* static gint handle_box_signals[LAST_SIGNAL] = { 0 };*/

GtkType
gnome_canvas_handle_box_get_type (void)
{
  static GtkType handle_box_type = 0;

  if (!handle_box_type) {
    GtkTypeInfo handle_box_info = {
      "GnomeCanvasHandleBox",
      sizeof (GnomeCanvasHandleBox),
      sizeof (GnomeCanvasHandleBoxClass),
      (GtkClassInitFunc) gnome_canvas_handle_box_class_init,
      (GtkObjectInitFunc) gnome_canvas_handle_box_init,
      NULL, /* reserved_1 */
      NULL, /* reserved_2 */
      (GtkClassInitFunc) NULL
    };

    handle_box_type = gtk_type_unique (gnome_canvas_handled_get_type (), &handle_box_info);
  }

  return handle_box_type;
}

static void
gnome_canvas_handle_box_class_init (GnomeCanvasHandleBoxClass *klass)
{
  GtkObjectClass *object_class;
  GnomeCanvasItemClass *item_class;
  GnomeCanvasHandledClass *handled_class;

  object_class = (GtkObjectClass *) klass;
  item_class = (GnomeCanvasItemClass *) klass;
  handled_class = (GnomeCanvasHandledClass*) klass;

  parent_class = gtk_type_class (gnome_canvas_handled_get_type ());

  gtk_object_add_arg_type ("GnomeCanvasHandleBox::x1", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X1);
  gtk_object_add_arg_type ("GnomeCanvasHandleBox::y1", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y1);
  gtk_object_add_arg_type ("GnomeCanvasHandleBox::x2", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X2);
  gtk_object_add_arg_type ("GnomeCanvasHandleBox::y2", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y2);

  klass->sync_item   = NULL;
  klass->create_item = NULL;

  handled_class->create         = gnome_canvas_handle_box_create;
  handled_class->scale          = gnome_canvas_handle_box_scale;
  handled_class->size           = gnome_canvas_handle_box_size;

  item_class->realize     = gnome_canvas_handle_box_realize;
  //  item_class->reconfigure = gnome_canvas_handle_box_reconfigure;
  item_class->unrealize   = gnome_canvas_handle_box_unrealize;

  object_class->destroy = gnome_canvas_handle_box_destroy;
  object_class->set_arg = gnome_canvas_handle_box_set_arg;
  object_class->get_arg = gnome_canvas_handle_box_get_arg;
}

static void
gnome_canvas_handle_box_init (GnomeCanvasHandleBox *handle_box)
{
  handle_box->item = NULL;
  
  handle_box->handles = NULL;

  handle_box->coords[0] = 0.0;
  handle_box->coords[1] = 0.0;
  handle_box->coords[2] = 0.0;
  handle_box->coords[3] = 0.0;
}

static void
create_handles(GnomeCanvasHandleBox* handle_box)
{
  g_return_if_fail(GNOME_CANVAS_HANDLED_IS_SELECTED(GNOME_CANVAS_HANDLED(handle_box)));

  if (handle_box->handles == NULL) {
    GnomeCanvasHandle** newhandles = g_new(GnomeCanvasHandle*,LAST_BOX_HANDLE);
    BoxHandle i = NW;

    while ( i < LAST_BOX_HANDLE ) {
      newhandles[i] = GNOME_CANVAS_HANDLE(gnome_canvas_item_new(GNOME_CANVAS_GROUP(handle_box),
								gnome_canvas_handle_get_type(),
								NULL));

      gtk_object_set_user_data(GTK_OBJECT(newhandles[i]), handle_box);

      gtk_signal_connect(GTK_OBJECT(newhandles[i]), "moved",
			 GTK_SIGNAL_FUNC(gnome_canvas_handle_box_handle_moved),
			 GINT_TO_POINTER(i));
      ++i;
    }

    handle_box->handles = newhandles;
  }
}

static void
destroy_handles(GnomeCanvasHandleBox* handle_box)
{
  g_return_if_fail(GNOME_IS_CANVAS_HANDLE_BOX(handle_box));
  g_return_if_fail(!GNOME_CANVAS_HANDLED_IS_SELECTED(GNOME_CANVAS_HANDLED(handle_box)));

  if (handle_box->handles) {
    BoxHandle i = NW;
    while ( i < LAST_BOX_HANDLE ) {
      gtk_object_destroy(GTK_OBJECT(handle_box->handles[i]));
      ++i;
    }
    g_free(handle_box->handles);
    handle_box->handles = NULL;
  }
}

static void
show_handles(GnomeCanvasHandleBox* handle_box)
{
  g_return_if_fail(GNOME_IS_CANVAS_HANDLE_BOX(handle_box));
  g_return_if_fail(GNOME_CANVAS_HANDLED_IS_SELECTED(GNOME_CANVAS_HANDLED(handle_box)));
  g_return_if_fail(handle_box->handles);

  if (handle_box->handles) {
    BoxHandle i = NW;
    while ( i < LAST_BOX_HANDLE ) {
      gnome_canvas_item_show(GNOME_CANVAS_ITEM(handle_box->handles[i]));
      ++i;
    }
  }
}

static void
hide_handles(GnomeCanvasHandleBox* handle_box)
{
  g_return_if_fail(GNOME_IS_CANVAS_HANDLE_BOX(handle_box));
  g_return_if_fail(GNOME_CANVAS_HANDLED_IS_SELECTED(GNOME_CANVAS_HANDLED(handle_box)));
  g_return_if_fail(handle_box->handles);

  if (handle_box->handles) {
    BoxHandle i = NW;
    while ( i < LAST_BOX_HANDLE ) {
      gnome_canvas_item_hide(GNOME_CANVAS_ITEM(handle_box->handles[i]));
      ++i;
    }
  }
}

/* What goes in 'unrealize' and what in 'destroy'? I think either or 
   both can be called. So both are OK with this object. */
static void
gnome_canvas_handle_box_destroy (GtkObject *object)
{
  GnomeCanvasHandleBox *handle_box;

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

  handle_box = GNOME_CANVAS_HANDLE_BOX (object);

  /* Group frees it. */
  handle_box->item = NULL;

  /* Want to emit unselect if appropriate */
  if (GNOME_CANVAS_HANDLED_IS_SELECTED(GNOME_CANVAS_HANDLED(object))) 
    gnome_canvas_handled_set_selected(GNOME_CANVAS_HANDLED(object), FALSE);

  /* Group destroy method will destroy the handles and item */
  g_free(handle_box->handles);
  handle_box->handles = NULL;

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

static void
gnome_canvas_handle_box_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
  GnomeCanvasItem *item;
  GnomeCanvasHandleBox *handle_box;

  item = GNOME_CANVAS_ITEM (object);
  handle_box = GNOME_CANVAS_HANDLE_BOX (object);

  switch (arg_id) {
  case ARG_X1:
    handle_box->coords[0] = GTK_VALUE_DOUBLE (*arg);
    break;
  case ARG_Y1:
    handle_box->coords[1] = GTK_VALUE_DOUBLE (*arg);
    break;
  case ARG_X2:
    handle_box->coords[2] = GTK_VALUE_DOUBLE (*arg);
    break;
  case ARG_Y2:
    handle_box->coords[3] = GTK_VALUE_DOUBLE (*arg);
    break;
  default:
    g_warning("GnomeCanvasHandleBox got an unknown arg type.");
    break;
  }

  gnome_canvas_handle_box_reconfigure(item);
}

static void
gnome_canvas_handle_box_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
  GnomeCanvasHandleBox *handle_box;

  handle_box = GNOME_CANVAS_HANDLE_BOX (object);

  switch (arg_id) {
  case ARG_X1:
    GTK_VALUE_DOUBLE (*arg) = handle_box->coords[0];
    break;
  case ARG_Y1:
    GTK_VALUE_DOUBLE (*arg) = handle_box->coords[1];
    break;
  case ARG_X2:
    GTK_VALUE_DOUBLE (*arg) = handle_box->coords[2];
    break;
  case ARG_Y2:
    GTK_VALUE_DOUBLE (*arg) = handle_box->coords[3];
    break;
  default:
    arg->type = GTK_TYPE_INVALID;
    break;
  }
}

static void
gnome_canvas_handle_box_realize (GnomeCanvasItem *item)
{
  GnomeCanvasHandleBox* handle_box = GNOME_CANVAS_HANDLE_BOX(item);

  g_return_if_fail(handle_box->item == NULL);

  if (GNOME_CANVAS_ITEM_CLASS(parent_class)->realize)
    (* GNOME_CANVAS_ITEM_CLASS(parent_class)->realize) (item);

  /* This is a horrible hack - it should be in gnome_canvas_handle_box_new(GnomeCanvasGroup*),
     which does not exist and would be all weird in the canvas context. 
     However, people might want to change the box characteristics at creation time. 
     Which is currently impossible. */

  /* (Colors are just a debug thing) */


  if (GNOME_CANVAS_HANDLE_BOX_CLASS(GTK_OBJECT(item)->klass)->create_item)
    (*GNOME_CANVAS_HANDLE_BOX_CLASS(GTK_OBJECT(item)->klass)->create_item)(handle_box);

  if (handle_box->item) {
    gtk_signal_connect(GTK_OBJECT(handle_box->item), "event",
		       GTK_SIGNAL_FUNC(gnome_canvas_handle_box_item_event),
		       handle_box);
  }

  gnome_canvas_handle_box_reconfigure(item);
}


static void 
gnome_canvas_handle_box_unrealize   (GnomeCanvasItem *item)
{
  GnomeCanvasHandleBox *handle_box;

  g_return_if_fail (item != NULL);
  g_return_if_fail (GNOME_IS_CANVAS_HANDLE_BOX (item));

  handle_box = GNOME_CANVAS_HANDLE_BOX (item);

  /* Want to emit unselect if appropriate */
  if (GNOME_CANVAS_HANDLED_IS_SELECTED(GNOME_CANVAS_HANDLED(item))) {
    gnome_canvas_handled_set_selected(GNOME_CANVAS_HANDLED(item), FALSE);
  }

  /* Group unrealize method will unrealize item, handles 
     went away in above call. */

  if (GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize)
    (* GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize) (item);
}

static void
sync_item(GnomeCanvasHandleBox* handle_box, GtkAnchorType which_handle)
{
  g_return_if_fail(GNOME_IS_CANVAS_HANDLE_BOX(handle_box));

  if (GNOME_CANVAS_HANDLE_BOX_CLASS(GTK_OBJECT(handle_box)->klass)->sync_item)
    (*GNOME_CANVAS_HANDLE_BOX_CLASS(GTK_OBJECT(handle_box)->klass)->sync_item)(handle_box, which_handle);  
}

static GtkAnchorType normal_anchors[LAST_BOX_HANDLE] = {
  GTK_ANCHOR_NW,     /* NW */
  GTK_ANCHOR_NORTH,  /* N  */
  GTK_ANCHOR_NE,     /* NE */
  GTK_ANCHOR_WEST,   /* W  */
  GTK_ANCHOR_EAST,   /* E  */
  GTK_ANCHOR_SW,     /* SW */
  GTK_ANCHOR_SOUTH,  /* S  */
  GTK_ANCHOR_SE      /* SE */
};

static GtkAnchorType x_reversed_anchors[LAST_BOX_HANDLE] = {
  GTK_ANCHOR_NE,     /* NW */
  GTK_ANCHOR_NORTH,  /* N  */
  GTK_ANCHOR_NW,     /* NE */
  GTK_ANCHOR_EAST,   /* W  */
  GTK_ANCHOR_WEST,   /* E  */
  GTK_ANCHOR_SE,     /* SW */
  GTK_ANCHOR_SOUTH,  /* S  */
  GTK_ANCHOR_SW      /* SE */
};
  
static GtkAnchorType y_reversed_anchors[LAST_BOX_HANDLE] = {
  GTK_ANCHOR_SW,     /* NW */
  GTK_ANCHOR_SOUTH,  /* N  */
  GTK_ANCHOR_SE,     /* NE */
  GTK_ANCHOR_WEST,   /* W  */
  GTK_ANCHOR_EAST,   /* E  */
  GTK_ANCHOR_NW,     /* SW */
  GTK_ANCHOR_NORTH,  /* S  */
  GTK_ANCHOR_NE      /* SE */
};

  
static GtkAnchorType both_reversed_anchors[LAST_BOX_HANDLE] = {
  GTK_ANCHOR_SE,     /* NW */
  GTK_ANCHOR_SOUTH,  /* N  */
  GTK_ANCHOR_SW,     /* NE */
  GTK_ANCHOR_EAST,   /* W  */
  GTK_ANCHOR_WEST,   /* E  */
  GTK_ANCHOR_NE,     /* SW */
  GTK_ANCHOR_NORTH,  /* S  */
  GTK_ANCHOR_NW      /* SE */
};


static void 
sync_handles(GnomeCanvasHandleBox* handle_box, guint which_handles)
{
  double x1, y1, x2, y2, midx, midy;
  GnomeCanvasHandled* handled = GNOME_CANVAS_HANDLED(handle_box);
  GnomeCanvasHandle** h = handle_box->handles; /* Save typing; lazy. */
  GtkAnchorType* anchors;
  gboolean x_reversed;
  gboolean y_reversed;

  g_assert(h != NULL);

  if (which_handles == 0) return;

  x1 = handle_box->coords[0];
  x2 = handle_box->coords[2];
  y1 = handle_box->coords[1];
  y2 = handle_box->coords[3];
  midx = x1 + (x2-x1)/2;
  midy = y1 + (y2-y1)/2;

  x_reversed = (x1 > x2);
  y_reversed = (y1 > y2);

  if (x_reversed && y_reversed) {
    anchors = both_reversed_anchors;
  }
  else if (x_reversed) {
    anchors = x_reversed_anchors;
  }
  else if (y_reversed) {
    anchors = y_reversed_anchors;
  }
  else {
    anchors = normal_anchors;
  }

  if (which_handles & HANDLE(NW)) {
    gnome_canvas_item_set(GNOME_CANVAS_ITEM(h[NW]),
			  "x", x1,
			  "y", y1,
			  "anchor", anchors[NW],
			  "sensitive", 
			  GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled),
			  NULL);
  }
  if (which_handles & HANDLE(N)) {
    gnome_canvas_item_set(GNOME_CANVAS_ITEM(h[N]),
			  "x", midx,
			  "y", y1,
			  "anchor", anchors[N],
			  "sensitive", 
			  GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled),
			  NULL);
  }    

  if (which_handles & HANDLE(NE)) {
    gnome_canvas_item_set(GNOME_CANVAS_ITEM(h[NE]),
			  "x", x2,
			  "y", y1,
			  "anchor", anchors[NE],
			  "sensitive", 
			  GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled),
			  NULL);
  }

  if (which_handles & HANDLE(W)) {
    gnome_canvas_item_set(GNOME_CANVAS_ITEM(h[W]),
			  "x", x1,
			  "y", midy,
			  "anchor", anchors[W],
			  "sensitive", 
			  GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled),
			  NULL);
  }

  if (which_handles & HANDLE(E)) {
    gnome_canvas_item_set(GNOME_CANVAS_ITEM(h[E]),
			  "x", x2,
			  "y", midy,
			  "anchor", anchors[E],
			  "sensitive", 
			  GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled),
			  NULL);
  }

  if (which_handles & HANDLE(SW)) {
    gnome_canvas_item_set(GNOME_CANVAS_ITEM(h[SW]),
			  "x", x1,
			  "y", y2,
			  "anchor", anchors[SW],
			  "sensitive", 
			  GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled),
			  NULL);
  }

  if (which_handles & HANDLE(S)) {
    gnome_canvas_item_set(GNOME_CANVAS_ITEM(h[S]),
			  "x", midx,
			  "y", y2,
			  "anchor", anchors[S],
			  "sensitive", 
			  GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled),
			  NULL);
  }

  if (which_handles & HANDLE(SE)) {
    gnome_canvas_item_set(GNOME_CANVAS_ITEM(h[SE]),
			  "x", x2,
			  "y", y2,
			  "anchor", anchors[SE],
			  "sensitive", 
			  GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled),
			  NULL);
  }
}

static void 
gnome_canvas_handle_box_reconfigure (GnomeCanvasItem *item)
{
  GnomeCanvasHandleBox* handle_box = GNOME_CANVAS_HANDLE_BOX(item);
  GnomeCanvasHandled* handled =        GNOME_CANVAS_HANDLED(item);

  
  if (GNOME_CANVAS_HANDLED_IS_SELECTED(handled)) {

    create_handles(handle_box); /* checks for handles == NULL */
    
    sync_handles(handle_box, ALL_HANDLES);
  }
  /* Not selected/sensitive */
  else {
    destroy_handles(handle_box); /* checks for NULL */
  }

  sync_item(handle_box, GTK_ANCHOR_CENTER);
}

static gint 
gnome_canvas_handle_box_item_event(GnomeCanvasItem* item, GdkEvent* event, 
				   GnomeCanvasHandleBox* handle_box)
{
  /*  GnomeCanvasBox* box = GNOME_CANVAS_BOX(item); */
  static double lastx = 0.0, lasty = 0.0;
  GdkCursor* ptr;
  GnomeCanvasHandled* handled = GNOME_CANVAS_HANDLED(handle_box);

  switch (event->type){

  case GDK_ENTER_NOTIFY:
    if (!GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled)) return FALSE;
    ptr = gdk_cursor_new(GDK_FLEUR);
    gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, ptr);
    gdk_cursor_destroy (ptr);
    break;
    
  case GDK_LEAVE_NOTIFY:
    /* Unset the cursor, even if insensitive 
       (in case we set it while sensitive) */
    gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, NULL);
    break;
    
  case GDK_BUTTON_PRESS:
    if (!GNOME_CANVAS_HANDLED_IS_SENSITIVE(handled)) return FALSE;
    switch (event->button.button) {
    case 1:
      if (!handled->dragging) {
	gnome_canvas_item_grab (item,
				GDK_POINTER_MOTION_MASK | 
				GDK_BUTTON_RELEASE_MASK | 
				GDK_BUTTON_PRESS_MASK,
				NULL, /* Set on enter */
				event->button.time);

	lastx = event->button.x;
	lasty = event->button.y;
	gnome_canvas_item_w2i(item, &lastx, &lasty);

	
	handled->dragging = TRUE;
	gnome_canvas_handled_set_selected(handled, TRUE); /* implies reconfigure */
	hide_handles(handle_box);
      } else return FALSE;
      break;
      
    case 2:
      if (!handled->sensitive) return FALSE;

      if (event->button.state & GDK_CONTROL_MASK) 
	gnome_canvas_item_lower(GNOME_CANVAS_ITEM(handle_box),1);
      else 
	gnome_canvas_item_raise(GNOME_CANVAS_ITEM(handle_box),1);

      gnome_canvas_handle_box_reconfigure(GNOME_CANVAS_ITEM(handle_box));
      break;
      
    default:
      return FALSE;
      break;
    }
    break; /* Button press */
    
  case GDK_BUTTON_RELEASE:
    switch (event->button.button) {
    case 1:
      if (handled->dragging) {
	gnome_canvas_item_ungrab(item, event->button.time);
	handled->dragging = FALSE;
	show_handles(handle_box);
	/* This is so the translucency turns off */
	sync_item(handle_box, GTK_ANCHOR_CENTER);
      } else return FALSE;
      break;

    default:
      return FALSE;
      break;
    }
    break; /* Button release */
    
  case GDK_MOTION_NOTIFY:
    /* Want to keep dragging even if insensitive */
    if (handled->dragging) {
      double newx, newy, dx, dy;

      newx = event->motion.x;
      newy = event->motion.y;

      gnome_canvas_item_w2i(item, &newx, &newy);

      dx = newx - lastx;
      dy = newy - lasty;
      lastx = newx;
      lasty = newy;

      handle_box->coords[0] += dx;
      handle_box->coords[1] += dy;
      handle_box->coords[2] += dx;
      handle_box->coords[3] += dy;
      gnome_canvas_handle_box_reconfigure(GNOME_CANVAS_ITEM(handle_box));
      gnome_canvas_handled_resized(handled,
				   handle_box->coords[0], 
				   handle_box->coords[1],
				   handle_box->coords[2],
				   handle_box->coords[3]);
    } else return FALSE;
    break; /* Motion notify */
    
  default:
    return FALSE;
  }
  
  return TRUE;
}

static void 
gnome_canvas_handle_box_create    (GnomeCanvasHandled* handled, GdkEventButton* event)
{
  GnomeCanvasHandleBox* handle_box = GNOME_CANVAS_HANDLE_BOX(handled);  
  double x1, y1, x2, y2;

  handled->selected = TRUE;

  if (event == NULL) {
    /* Make up some defaults */
    gnome_canvas_get_scroll_region(GNOME_CANVAS_ITEM(handled)->canvas,
				   &x1, &y1, &x2, &y2);
    gnome_canvas_item_w2i(GNOME_CANVAS_ITEM(handled),
			  &x1, &y1);
    gnome_canvas_item_w2i(GNOME_CANVAS_ITEM(handled),
			  &x2, &y2);
    
    handle_box->coords[0] = x1;
    handle_box->coords[1] = y1;
    handle_box->coords[2] = x1+(x2-x1)/2;
    handle_box->coords[3] = y1+(y2-y1)/2;

    gnome_canvas_handle_box_reconfigure(GNOME_CANVAS_ITEM(handled));
  }
  else {
    x1 = event->x;
    y1 = event->y;
    gnome_canvas_item_w2i(GNOME_CANVAS_ITEM(handled), &x1, &y1);

    handle_box->coords[0] = handle_box->coords[2] = x1;
    handle_box->coords[1] = handle_box->coords[3] = y1;

    gnome_canvas_handle_box_reconfigure(GNOME_CANVAS_ITEM(handled));

    gnome_canvas_handle_transfer_drag(NULL,handle_box->handles[SE],event->time);
  }
}

static void 
gnome_canvas_handle_box_handle_moved(GnomeCanvasItem* item, gdouble x, gdouble y,
				     gpointer data)
{
  BoxHandle whichone = GPOINTER_TO_INT(data);
  GnomeCanvasHandleBox* handle_box;
  GnomeCanvasHandled* handled;
  guint need_sync = 0x0;
  
  handle_box = gtk_object_get_user_data(GTK_OBJECT(item));
  g_assert(handle_box != NULL);
  handled = GNOME_CANVAS_HANDLED(handle_box);

  /* Convert from item coordinates of the handle to item coordinates
     of the HandleBox. */
  gnome_canvas_item_i2w(item, &x, &y);
  gnome_canvas_item_w2i(GNOME_CANVAS_ITEM(handle_box), &x, &y);
  
  switch (whichone) {
  case NW:
    handle_box->coords[0] = x;
    handle_box->coords[1] = y;
    need_sync = ~HANDLE(SE);
    break;

  case N:
    handle_box->coords[1] = y;
    need_sync = ~SOUTH_HANDLES;
    break;

  case NE:
    handle_box->coords[2] = x;
    handle_box->coords[1] = y;
    need_sync = ~HANDLE(SW);
    break;
    
  case W:
    handle_box->coords[0] = x;
    need_sync = ~EAST_HANDLES;
    break;

  case E:
    handle_box->coords[2] = x;
    need_sync = ~WEST_HANDLES;
    break;

  case SW:
    handle_box->coords[0] = x;
    handle_box->coords[3] = y;
    need_sync = ~HANDLE(NE);
    break;

  case S:
    handle_box->coords[3] = y;
    need_sync = ~NORTH_HANDLES;
    break;

  case SE:
    handle_box->coords[2] = x;
    handle_box->coords[3] = y;
    need_sync = ~HANDLE(NW);
    break;

  default:
    g_assert_not_reached();
    break;
  }

  /* Override all of the above hassle. I thought I could get away with
     this; but due to possible anchor changes, all handles need updating
     every time. Need to clean this up eventually. */
  need_sync = ALL_HANDLES;

  sync_item(handle_box, GNOME_CANVAS_HANDLE(item)->anchor);
  sync_handles(handle_box, need_sync);

  gnome_canvas_handled_resized(handled,  
			       handle_box->coords[0], 
			       handle_box->coords[1],
			       handle_box->coords[2],
			       handle_box->coords[3]);
}

static void gnome_canvas_handle_box_scale(GnomeCanvasHandled* handled,
					  gdouble x1, gdouble y1,
					  gdouble x2, gdouble y2) 
{
  GnomeCanvasHandleBox* handle_box;
  gdouble* xmin, *xmax, *ymin, *ymax;

  g_return_if_fail(GNOME_IS_CANVAS_HANDLE_BOX(handled));

  handle_box = GNOME_CANVAS_HANDLE_BOX(handled);

  /* If we're out of order, reverse things. */
  if (handle_box->coords[0] > handle_box->coords[2]) {
    xmin = &handle_box->coords[2];
    xmax = &handle_box->coords[0];
  }
  else {
    xmax = &handle_box->coords[2];
    xmin = &handle_box->coords[0];
  }

  if (handle_box->coords[1] > handle_box->coords[3]) {
    ymin = &handle_box->coords[3];
    ymax = &handle_box->coords[1];
  }
  else {
    ymax = &handle_box->coords[3];
    ymin = &handle_box->coords[1];
  }

  *xmin = x1;
  *xmax = x2;
  *ymin = y1;
  *ymax = y2;

  sync_item(handle_box, GTK_ANCHOR_CENTER);
  
  if (handle_box->handles)
    sync_handles(handle_box, ALL_HANDLES);
}

static void gnome_canvas_handle_box_size (GnomeCanvasHandled* handled, 
					  double* x1, double* y1, 
					  double* x2, double* y2)
{
  GnomeCanvasHandleBox* handle_box;

  g_return_if_fail(GNOME_IS_CANVAS_HANDLE_BOX(handled));

  handle_box = GNOME_CANVAS_HANDLE_BOX(handled);
  
  if (x1) *x1 = MIN(handle_box->coords[0], handle_box->coords[2]);
  if (x2) *x2 = MAX(handle_box->coords[0], handle_box->coords[2]);
  if (y1) *y1 = MIN(handle_box->coords[1], handle_box->coords[3]);
  if (y2) *y2 = MAX(handle_box->coords[1], handle_box->coords[3]);
}

