/* dia-canvas-view.c
 * Copyright (C) 2000, 2001  James Henstridge, Arjan Molenaar
 *
 * 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 "dia-canvas-view.h"
#include <math.h>
#include <gtk/gtkadjustment.h>
#include <gtk/gtksignal.h>
#include <gdk/gdkkeysyms.h>
#include <libgnomecanvas/gnome-canvas-util.h>
#include <libart_lgpl/art_affine.h>
#include "dia-canvas-editable.h"
#include "dia-default-tool.h"
#include "dia-handle-layer.h"
#include "dia-canvas-i18n.h"
#include "dia-shape-art.h"
#include "dia-shape-x.h"
#include "diamarshal.h"

enum {
	FOCUS_ITEM,
	SELECT_ITEM,
	UNSELECT_ITEM,
	LAST_SIGNAL
};

enum {
	PROP_AA = 1,
	PROP_CANVAS,
	PROP_ACTIVE_VIEW,
	PROP_ZOOM,
	PROP_TOOL,
	PROP_DEFAULT_TOOL
};

//#define D(msg) G_STMT_START { g_print (G_STRLOC": "); g_print msg; g_print ("\n"); } G_STMT_END
#define D(msg)

#define MIN_TEXT_VIEW_WIDTH (15)
#define MIN_TEXT_VIEW_HEIGHT (15)

static void dia_canvas_view_init		(DiaCanvasView	*canvas_view);
static void dia_canvas_view_class_init		(DiaCanvasViewClass *klass);

static void dia_canvas_view_destroy		(GtkObject	*object);
static void dia_canvas_view_set_property	(GObject	*gobject,
						 guint		 property_id,
		                                 const GValue	*value,
						 GParamSpec	*pspec);
static void dia_canvas_view_get_property	(GObject	*gobject,
						 guint		 property_id,
		                                 GValue		*value,
						 GParamSpec	*pspec);

/* Events the CanvasView should react on: */
static void	dia_canvas_view_size_allocate	(GtkWidget	*widget,
						 GtkAllocation  *allocation);
static gboolean dia_canvas_view_button_press	(GtkWidget	*widget,
						 GdkEventButton	*event);
static gboolean dia_canvas_view_button_release	(GtkWidget	*widget,
						 GdkEventButton	*event);
static gboolean dia_canvas_view_motion_notify	(GtkWidget	*widget,
						 GdkEventMotion	*event);
static gboolean dia_canvas_view_key_press	(GtkWidget	*widget,
						 GdkEventKey	*event);
static gboolean dia_canvas_view_key_release	(GtkWidget	*widget,
						 GdkEventKey	*event);

static gboolean dia_canvas_view_focus_in	(GtkWidget	*widget,
						 GdkEventFocus	*event);
static gboolean dia_canvas_view_focus_out	(GtkWidget	*widget,
						 GdkEventFocus	*event);

static void dia_canvas_view_draw_background	(GnomeCanvas	*canvas,
						 GdkDrawable	*drawable,
						 int x, int y,
						 int width, int height);
static void dia_canvas_view_render_background	(GnomeCanvas	*canvas,
						 GnomeCanvasBuf	*buf);

static void redraw_view_cb			(DiaCanvas	*canvas,
						 DiaCanvasView	*view);

static void update_extents			(DiaCanvasView	*view);
static void empty_canvas_view			(DiaCanvasView	*view);

static GnomeCanvasClass *parent_class = NULL;

static DiaCanvasView *active_view = NULL;

static guint canvas_view_signals[LAST_SIGNAL] = { 0 };

GtkType
dia_canvas_view_get_type (void)
{
	static GtkType canvas_view_type = 0;

	if (!canvas_view_type) {
		static const GtkTypeInfo canvas_view_info =
		{
			"DiaCanvasView",
			sizeof (DiaCanvasView),
			sizeof (DiaCanvasViewClass),
			(GtkClassInitFunc) dia_canvas_view_class_init,
			(GtkObjectInitFunc) dia_canvas_view_init,
			/* reserved_1 */ NULL,
			/* reserved_2 */ NULL,
			(GtkClassInitFunc) NULL,
		};

		canvas_view_type = gtk_type_unique (gnome_canvas_get_type (),
						    &canvas_view_info);
	}

	return canvas_view_type;
}

static void
dia_canvas_view_class_init (DiaCanvasViewClass *klass)
{
	GObjectClass *gobject_class;
	GtkObjectClass *object_class;
	GtkWidgetClass *widget_class;
	GnomeCanvasClass *canvas_class;

	gobject_class = (GObjectClass*) klass;
	object_class = (GtkObjectClass*) klass;
	widget_class = (GtkWidgetClass*) klass;
	canvas_class = (GnomeCanvasClass*) klass;

	parent_class = g_type_class_peek_parent (klass);

	gobject_class->set_property = dia_canvas_view_set_property;
	gobject_class->get_property = dia_canvas_view_get_property;
	
	object_class->destroy = dia_canvas_view_destroy;

	widget_class->size_allocate = dia_canvas_view_size_allocate;
	widget_class->button_press_event = dia_canvas_view_button_press;
	widget_class->button_release_event = dia_canvas_view_button_release;
	widget_class->motion_notify_event = dia_canvas_view_motion_notify;
	widget_class->key_press_event = dia_canvas_view_key_press;
	widget_class->key_release_event = dia_canvas_view_key_release;
	widget_class->focus_in_event = dia_canvas_view_focus_in;
	widget_class->focus_out_event = dia_canvas_view_focus_out;

	canvas_class->draw_background = dia_canvas_view_draw_background;
	canvas_class->render_background = dia_canvas_view_render_background;

	klass->focus_item = NULL;
	klass->select_item = NULL;
	klass->unselect_item = NULL;

	canvas_view_signals[FOCUS_ITEM] = 
	  g_signal_new ("focus_item",
			G_TYPE_FROM_CLASS (object_class),
			G_SIGNAL_RUN_FIRST,
			G_STRUCT_OFFSET(DiaCanvasViewClass, focus_item),
			NULL, NULL,
			dia_marshal_VOID__OBJECT,
			G_TYPE_NONE, 1,
			DIA_TYPE_CANVAS_VIEW_ITEM);

	canvas_view_signals[SELECT_ITEM] = 
	  g_signal_new ("select_item",
			G_TYPE_FROM_CLASS (object_class),
			G_SIGNAL_RUN_FIRST,
			G_STRUCT_OFFSET(DiaCanvasViewClass, select_item),
			NULL, NULL,
			dia_marshal_VOID__OBJECT,
			G_TYPE_NONE, 1,
			DIA_TYPE_CANVAS_VIEW_ITEM);

	canvas_view_signals[UNSELECT_ITEM] = 
	  g_signal_new ("unselect_item",
			G_TYPE_FROM_CLASS (object_class),
			G_SIGNAL_RUN_FIRST,
			G_STRUCT_OFFSET(DiaCanvasViewClass, unselect_item),
			NULL, NULL,
			dia_marshal_VOID__OBJECT,
			G_TYPE_NONE, 1,
			DIA_TYPE_CANVAS_VIEW_ITEM);

	g_object_class_install_property (gobject_class,
					 PROP_AA,
					 g_param_spec_boolean ("aa",
						_("Anti-aliasing"),
						_("Set the canvas to anti-alias or X render mode. Only during initialization. You should also have pushed the right colormap (gdk_rgb_get_cmap() for AA, gdk_imlib_cmap() for X)."),
						FALSE,
						G_PARAM_READWRITE));

	g_object_class_install_property (gobject_class,
					 PROP_CANVAS,
					 g_param_spec_object ("canvas",
						_("Canvas"),
						_("Canvas to be displayed in the view"),
						DIA_TYPE_CANVAS,
						G_PARAM_READWRITE));

	g_object_class_install_property (gobject_class,
					 PROP_ACTIVE_VIEW,
					 g_param_spec_object ("active_view",
						_("Active view"),
						_("Returns the active view, even if the view itself is not the active view."),
						DIA_TYPE_CANVAS_VIEW,
						G_PARAM_READABLE));
	g_object_class_install_property (gobject_class,
					 PROP_ZOOM,
					 g_param_spec_double ("zoom",
						_("Zoom"),
						_("Set the zoom factor"),
						-G_MAXDOUBLE, 
						G_MAXDOUBLE, 
						0.0,
						G_PARAM_READWRITE));
	g_object_class_install_property (gobject_class,
					 PROP_TOOL,
					 g_param_spec_object ("tool",
						_("Tool"),
						_("Set a tool, may be NULL"),
						DIA_TYPE_TOOL,
						G_PARAM_READWRITE));
	g_object_class_install_property (gobject_class,
					 PROP_DEFAULT_TOOL,
					 g_param_spec_object ("default-tool",
						_("Default tool"),
						_("Set a tool"),
						DIA_TYPE_TOOL,
						G_PARAM_READWRITE));
}


static void
dia_canvas_view_init (DiaCanvasView *view)
{
	g_object_set (G_OBJECT (view),
		      "hadjustment", gtk_adjustment_new (0.0 /* value */,
							 0.0 /* lower */,
							 0.0 /* upper */,
							 20.0 /* step_incr */,
							 0.0 /* page_incr */,
							 0.0 /* page_size */),
		      "vadjustment", gtk_adjustment_new (0.0 /* value */,
							 0.0 /* lower */,
							 0.0 /* upper */,
							 20.0 /* step_incr */,
							 0.0 /* page_incr */,
							 0.0 /* page_size */),
		      NULL);

	view->canvas = NULL;
	view->root_item = NULL;
			
	view->tool = NULL;
	view->default_tool = dia_default_tool_new ();

	view->handle_layer =
		gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (view)->root),
				       dia_handle_layer_get_type (), NULL);
	g_object_ref (G_OBJECT (view->handle_layer));

	view->focus_item = NULL;
	view->selected_items = NULL;

	view->text_view = NULL;
	view->edited_item = NULL;
	view->edited_shape = NULL;

	view->last_item = NULL;
	view->button_press_handled = FALSE;
}

static void
dia_canvas_view_set_property (GObject *object, guint property_id,
			      const GValue *value, GParamSpec *pspec)
{
	DiaCanvasView *view = (DiaCanvasView*) object;

	switch (property_id) {
	case PROP_AA:
		if (!GTK_WIDGET_REALIZED (object))
			GNOME_CANVAS (view)->aa = g_value_get_boolean (value);
		else
			g_error ("Only set AA before the widget is realized.");
		break;
	case PROP_CANVAS:
		if (view->canvas)
			dia_canvas_view_unset_canvas (view);
		if (g_value_get_object (value))
			dia_canvas_view_set_canvas (view,
						    g_value_get_object (value));
		dia_canvas_view_request_update (view);
		break;
	case PROP_ZOOM:
		dia_canvas_view_set_zoom (view, g_value_get_double (value));
		break;
	case PROP_TOOL:
		dia_canvas_view_set_tool (view, g_value_get_object (value));
		break;
	case PROP_DEFAULT_TOOL:
		dia_canvas_view_set_default_tool (view, g_value_get_object (value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
dia_canvas_view_get_property (GObject *object, guint property_id,
			      GValue *value, GParamSpec *pspec)
{
	DiaCanvasView *view = (DiaCanvasView*) object;

	switch (property_id) {
	case PROP_AA:
		g_value_set_boolean (value, GNOME_CANVAS (view)->aa);
		break;
	case PROP_CANVAS:
		g_value_set_object (value, view->canvas);
		break;
	case PROP_ACTIVE_VIEW:
		g_value_set_object (value, active_view);
		break;
	case PROP_ZOOM:
		g_value_set_double (value, dia_canvas_view_get_zoom (view));
		break;
	case PROP_TOOL:
		g_value_set_object (value, dia_canvas_view_get_tool (view));
		break;
	case PROP_DEFAULT_TOOL:
		g_value_set_object (value, dia_canvas_view_get_default_tool (view));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
dia_canvas_view_destroy (GtkObject *object)
{
	DiaCanvasView *view = (DiaCanvasView*) object;
	
	//g_message (__FUNCTION__": begin (%x).", (guint) object);
	
	if (view->tool) {
		g_object_unref (G_OBJECT (view->tool));
		view->tool = NULL;
	}
	if (view->default_tool) {
		g_object_unref (G_OBJECT (view->default_tool));
		view->default_tool = NULL;
	}
	if (view->handle_layer) {
		g_object_unref (G_OBJECT (view->handle_layer));
		view->handle_layer = NULL;
	}

	if (view->text_view) {
		g_object_unref (G_OBJECT (view->text_view));
		view->text_view = NULL;
		view->edited_item = NULL;
		view->edited_shape = NULL;
	}

	if (view->canvas) {
		dia_canvas_view_unset_canvas (view);
	}

	//g_message (__FUNCTION__": calling parent.");
	GTK_OBJECT_CLASS (parent_class)->destroy (object);

}

static void
dia_canvas_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
	DiaCanvasView *view = DIA_CANVAS_VIEW (widget);

	(* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);

	/* Now adjust the size of the layout, so that it contains the canvas
	 * in a nice way. */
	update_extents (view);
}

static gboolean
dia_canvas_view_button_press (GtkWidget *widget, GdkEventButton *event)
{
	DiaCanvasView *view = (DiaCanvasView*) widget;
	gboolean result = FALSE;

	/* Ensure we have the right active view: the FocusIn event may
	 * come after this event... */
	active_view = view;

	/* Do nothing if we're currently editing a canvas item and the button
	 * is pressed inside the text view... */
	//if (view->text_view
	//    && gtk_widget_is_focus (GTK_WIDGET (view->text_view)))
	//	return FALSE;

	gtk_widget_grab_focus (widget);

	if (!view->canvas)
		return FALSE;

	gnome_canvas_window_to_world (GNOME_CANVAS (view),
				      event->x, event->y,
				      &event->x, &event->y);

	if (view->tool && dia_tool_button_press (view->tool, view, event))
		return TRUE;

	dia_canvas_push_undo (view->canvas, NULL);

	if (view->default_tool && dia_tool_button_press (view->default_tool, view, event))
		return TRUE;

	return FALSE;
}

static gboolean
dia_canvas_view_button_release (GtkWidget *widget, GdkEventButton *event)
{
	DiaCanvasView *view = (DiaCanvasView*) widget;
	gboolean result = FALSE;

	if (!view->canvas)
		return FALSE;

	gnome_canvas_window_to_world (GNOME_CANVAS (view),
				      event->x, event->y,
				      &event->x, &event->y);

	if (view->tool && dia_tool_button_release (view->tool, view, event))
		return TRUE;

	if (view->default_tool && dia_tool_button_release (view->default_tool, view, event))
		return TRUE;

	return FALSE;
}

static gboolean
dia_canvas_view_motion_notify (GtkWidget *widget, GdkEventMotion *event)
{
	DiaCanvasView *view = (DiaCanvasView*) widget;

	if (!view->canvas)
		return FALSE;

	gnome_canvas_window_to_world (GNOME_CANVAS (view),
				      event->x, event->y,
				      &event->x, &event->y);

	if (view->tool && dia_tool_motion_notify (view->tool, view, event))
		return TRUE;

	if (view->default_tool && dia_tool_motion_notify (view->default_tool, view, event))
		return TRUE;

	return FALSE;
}

static gboolean
dia_canvas_view_key_press (GtkWidget *widget, GdkEventKey *event)
{
	DiaCanvasView *view = (DiaCanvasView*) widget;

	/* Do not handle keys if we are editing text in the text view. */
	if (view->text_view && gtk_widget_is_focus (GTK_WIDGET (view->text_view)))
		return FALSE;

	if (!view->canvas)
		return FALSE;

	if (view->tool && dia_tool_key_press (view->tool, view, event))
		return TRUE;

	if (view->default_tool && dia_tool_key_press (view->default_tool, view, event))
		return TRUE;

	return FALSE;
}

static gboolean
dia_canvas_view_key_release (GtkWidget *widget, GdkEventKey *event)
{
	DiaCanvasView *view = (DiaCanvasView*) widget;
	gboolean result = FALSE;

	/* Do not handle keys if we are editing text in the text view. */
	if (view->text_view && gtk_widget_is_focus (GTK_WIDGET (view->text_view)))
		return FALSE;

	if (!view->canvas)
		return FALSE;

	if (view->tool && dia_tool_key_release (view->tool, view, event))
		return TRUE;

	if (view->default_tool && dia_tool_key_release (view->default_tool, view, event))
		return TRUE;

	return FALSE;
}

static gboolean
dia_canvas_view_focus_in (GtkWidget *widget, GdkEventFocus *event)
{
	DiaCanvasView *view = (DiaCanvasView*) widget;
	gboolean result = FALSE;

	//g_message(G_STRLOC);

	active_view = view;

	/* Update all shapes: we want to give the focused item its focused
	 * state "back". */
	if (view->focus_item) {
		DiaCanvasItem *diaitem = view->focus_item->item;
		DiaCanvasIter iter;
		if (dia_canvas_item_get_shape_iter (diaitem, &iter)) do {
			dia_shape_request_update (dia_canvas_item_shape_value (diaitem, &iter));
		} while (dia_canvas_item_shape_next (diaitem, &iter));

		gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (view->focus_item));
		result = TRUE;
	}

	if (GTK_WIDGET_CLASS(parent_class)->focus_in_event)
		result = (* GTK_WIDGET_CLASS(parent_class)->focus_in_event) (widget, event);

	return result;
}

static gboolean
dia_canvas_view_focus_out (GtkWidget *widget, GdkEventFocus *event)
{
	DiaCanvasView *view = (DiaCanvasView*) widget;
	gboolean result = FALSE;

	//g_message(G_STRLOC);

	//active_view = NULL;

	/* Update all shapes of the focused item: we want to remove the
	 * focused appearance of the item. */
	if (view->focus_item) {
		DiaCanvasItem *diaitem = view->focus_item->item;
		DiaCanvasIter iter;

		if (dia_canvas_item_get_shape_iter (diaitem, &iter)) do {
			dia_shape_request_update (dia_canvas_item_shape_value (diaitem, &iter));
		} while (dia_canvas_item_shape_next (diaitem, &iter));

		gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (view->focus_item));
		result = TRUE;
	}

	if (GTK_WIDGET_CLASS(parent_class)->focus_out_event)
		result = (* GTK_WIDGET_CLASS(parent_class)->focus_out_event) (widget, event);

	return result;
}

static inline void
calc_grid_start (DiaCanvas *diac, GnomeCanvas *canvas, int x, int y,
		 gdouble *sx, gdouble *sy, gdouble *intx, gdouble *inty)
{
	register gdouble ofsx, ofsy;
	
	*intx = diac->interval_x * canvas->pixels_per_unit;
	*inty = diac->interval_y * canvas->pixels_per_unit;

	while (*intx < 4)
		*intx *= 2;
	while (*inty < 4)
		*inty *= 2;

	/* For the offset we should take into account the GnomeCanvas' upper
	 * left coordinate, since GtkLayout only uses positive coordinates.
	 */
	ofsx = (diac->offset_x - canvas->scroll_x1
		+ floor (canvas->scroll_x1 / diac->interval_x)
		* diac->interval_x) * canvas->pixels_per_unit;
	ofsy = (diac->offset_y - canvas->scroll_y1
		+ floor (canvas->scroll_y1 / diac->interval_y)
		* diac->interval_y) * canvas->pixels_per_unit;

	*sx = floor ((x - ofsx) / *intx) * *intx + ofsx;
	*sy = floor ((y - ofsy) / *inty) * *inty + ofsy;
}

static void
dia_canvas_view_draw_background (GnomeCanvas *canvas, GdkDrawable *drawable,
				 int dx, int dy, int width, int height)
{
	DiaCanvas *diac = ((DiaCanvasView*) canvas)->canvas;
	GdkColor c;
	gdouble intx, inty, sx, sy;
	register gdouble x, y;

	if (!diac) {
		parent_class->draw_background (canvas, drawable,
					       dx, dy, width, height);
		return;
	}

	calc_grid_start (diac, canvas, dx, dy, &sx, &sy, &intx, &inty);

	c.pixel = gnome_canvas_get_color_pixel (canvas, diac->grid_bg | 0xff);
	gdk_gc_set_foreground (canvas->pixmap_gc, &c);
	gdk_draw_rectangle (drawable, canvas->pixmap_gc, TRUE,
			    0, 0, width, height);

	c.pixel = gnome_canvas_get_color_pixel (canvas, diac->grid_color);
	gdk_gc_set_foreground (canvas->pixmap_gc, &c);

	for (y = sy - dy; y < height; y += inty) {
		for (x = sx - dx; x < width; x += intx) {
			register gint ix, iy;
			ix = (int) (x + 0.5);
			iy = (int) (y + 0.5);
			gdk_draw_point (drawable, canvas->pixmap_gc, ix, iy);
		}
	}
}

static void
dia_canvas_view_render_background (GnomeCanvas *canvas, GnomeCanvasBuf *buf)
{
	DiaCanvas *diac = ((DiaCanvasView*) canvas)->canvas;
	gdouble intx, inty, sx, sy;
	register gdouble x, y;

	if (!diac) {
		if (parent_class->render_background)
			parent_class->render_background (canvas, buf);
		else if (parent_class->draw_background)
			parent_class->draw_background
				(canvas, GTK_WIDGET (canvas)->window,
			         buf->rect.x0, buf->rect.y0,
			         buf->rect.x1 - buf->rect.x0,
			         buf->rect.y1 - buf->rect.y0);
		return;
	}

	calc_grid_start (diac, canvas, buf->rect.x0, buf->rect.y0,
			 &sx, &sy, &intx, &inty);

	buf->bg_color = diac->grid_bg | 0xff;
	gnome_canvas_buf_ensure_buf (buf);
	buf->is_bg = FALSE;

	for (y = sy; y < buf->rect.y1; y += inty) {
		for (x = sx; x < buf->rect.x1; x += intx) {
			register gint ix, iy;
			ix = (int) (x + 0.5);
			iy = (int) (y + 0.5);

			if ((ix >= buf->rect.x0) && (ix < buf->rect.x1)
			&& (iy >= buf->rect.y0) && (iy < buf->rect.y1)) {
				guchar *p = buf->buf +
					    (iy - buf->rect.y0) * buf->buf_rowstride
					    + (ix - buf->rect.x0) * 3;
				*p++ = DIA_COLOR_RED(diac->grid_color);
				*p++ = DIA_COLOR_GREEN(diac->grid_color);
				*p = DIA_COLOR_BLUE(diac->grid_color);
			}
		}
	}
}

static void
redraw_view_cb (DiaCanvas *canvas, DiaCanvasView *view)
{
	gnome_canvas_request_redraw (GNOME_CANVAS (view),
				     G_MININT, G_MININT, G_MAXINT, G_MAXINT);
}

/**
 * update_extents:
 * @view
 *
 * Update the canvas view's size so that it always keeps (0, 0) in the upper
 * left corner. If the canvas extents are dynamic, the size of the view is
 * always one width/height larger than the extents of the canvas.
 *
 * Static extents are set to the maximum of the extents of view size.
 */
static void
update_extents (DiaCanvasView *view)
{
	GnomeCanvas *canvas = (GnomeCanvas*) view;
	gdouble width, height;

	g_assert (DIA_IS_CANVAS_VIEW (view));

	width = GTK_WIDGET (view)->allocation.width / canvas->pixels_per_unit;
	height = GTK_WIDGET (view)->allocation.height / canvas->pixels_per_unit;

	if (view->canvas) {
		DiaRectangle *extents = &view->canvas->extents;
		if (view->canvas->static_extents) {
			width = MAX(extents->right, width);
			height = MAX(extents->bottom, height);
		} else {
			width += extents->right;
			height += extents->bottom;
		}
	}

	gnome_canvas_set_scroll_region (canvas, 0.0, 0.0, width, height);
}

/**
 * dia_canvas_view_set_canvas:
 * @view: 
 * @canvas: 
 *
 * Set the canvas to be visualized by @view. You can only set a canvas if
 * the view doesn't already have a canvas.
 **/
void
dia_canvas_view_set_canvas (DiaCanvasView *view, DiaCanvas *canvas)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (view->canvas == NULL);
	g_return_if_fail (DIA_IS_CANVAS (canvas));

	g_object_ref (canvas);
	view->canvas = canvas;

	g_signal_connect_swapped (G_OBJECT (canvas), "notify::extents",
				  G_CALLBACK (update_extents), view);
	
	g_signal_connect (G_OBJECT (canvas), "redraw_view",
			  G_CALLBACK (redraw_view_cb), view);
	
	/* Create DiaCanvasViewItems for all items already in the canvas */
	dia_canvas_view_item_add_items
				(GNOME_CANVAS_GROUP (GNOME_CANVAS (view)->root),
				 canvas->root);
	view->root_item = DIA_CANVAS_VIEW_ITEM (g_list_last (GNOME_CANVAS_GROUP (GNOME_CANVAS (view)->root)->item_list)->data);

	/* Automatically set root_item to NULL on destruction. */
	g_object_add_weak_pointer (G_OBJECT (view->root_item),
				   (gpointer) &view->root_item);

	/* Make sure the HandleLayer is on top: */
	if (view->handle_layer)
		gnome_canvas_item_raise_to_top (GNOME_CANVAS_ITEM (view->handle_layer));

	g_assert (view->root_item != NULL);
	g_assert (DIA_IS_CANVAS_VIEW_ITEM (view->root_item));

	/* Make sure the extents of the canvas are set up properly. */
	update_extents (view);

	/*
	 * Don't know why it won't show a canvas imediately after a 
	 * set_canvas() is done while the canvas is still visible...
	 */
	gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (view),
					  GNOME_CANVAS (view)->pixels_per_unit);

	g_object_notify (G_OBJECT (view), "canvas");
}

/**
 * dia_canvas_view_get_canvas:
 * @view: 
 *
 * Get the #DiaCanvas that is displayed in the @view.
 *
 * Return value: The canvas.
 **/
DiaCanvas*
dia_canvas_view_get_canvas (DiaCanvasView *view)
{
	g_return_val_if_fail (DIA_IS_CANVAS_VIEW (view), NULL);

	return view->canvas;
}

static void
empty_canvas_view (DiaCanvasView *view)
{
	/* Remove canvas items from GnomeCanvas::root::item_list too! */
	if (view->root_item) {
		gtk_object_destroy (GTK_OBJECT (view->root_item));
		view->root_item = NULL;
	}
	g_list_free (view->selected_items);
	view->selected_items = NULL;
	view->focus_item = NULL;
	view->last_item = NULL;

	g_object_unref (view->canvas);
	view->canvas = NULL;

	gnome_canvas_request_redraw (GNOME_CANVAS (view),
				     G_MININT, G_MININT, G_MAXINT, G_MAXINT);
}

/**
 * dia_canvas_view_unset_canvas:
 * @view: 
 *
 * Disconnect the view from it's #DiaCanvas. 
 **/
void
dia_canvas_view_unset_canvas (DiaCanvasView *view)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (DIA_IS_CANVAS (view->canvas));

	//g_message (__FUNCTION__);

	//if (view->root_item)
	//	g_object_remove_weak_pointer (G_OBJECT (view->root_item),
	//				      view->root_item);
	g_signal_handlers_disconnect_matched (view->canvas,
					      G_SIGNAL_MATCH_DATA,
					      0, 0, NULL, NULL, view);

	//g_object_weak_unref (G_OBJECT (view->canvas),
	//		     (GWeakNotify) empty_canvas_view,
	//		     view);
	empty_canvas_view (view);
	g_object_notify (G_OBJECT (view), "canvas");
}

/**
 * dia_canvas_view_new:
 * @canvas: A #DiaCanvas that contains data that is to be visualized (may
 * 		be NULL).
 * @aa: Use Anti-alias (LibArt) mode rather that X.
 *
 * Create a new DiaCanvasView object. In most cases you want to set @aa to TRUE,
 * since it has the most complete renderer and the best quality.
 *
 * Return value: A newly create DiaCanvasView.
 **/
GtkWidget*
dia_canvas_view_new (DiaCanvas *canvas, gboolean aa)
{
	GtkWidget *view;
  
	gtk_widget_push_colormap (gdk_rgb_get_cmap ());
	if (aa) {
		view = g_object_new (DIA_TYPE_CANVAS_VIEW,
				     "aa", TRUE,
				     "canvas", canvas,
				     NULL);
	} else {
		view = g_object_new (DIA_TYPE_CANVAS_VIEW,
				     "canvas", canvas,
				     NULL);
	}
	gtk_widget_pop_colormap ();
	
	g_assert (view != NULL);
	
	return view;
}

/**
 * dia_canvas_view_aa_new:
 *
 * Create a new anti-aliased canvas. Note that no #DiaCanvas object is yet
 * connected to the canvas view.
 *
 * Return value: A newly created #DiaCanvasView.
 **/
GtkWidget*
dia_canvas_view_aa_new (void)
{
	GtkWidget *view;

	gtk_widget_push_colormap (gdk_rgb_get_cmap ());
	view = g_object_new (DIA_TYPE_CANVAS_VIEW, "aa", TRUE, NULL);
	gtk_widget_pop_colormap ();

	return view;
}

/**
 * dia_canvas_view_get_zoom:
 * @view: 
 *
 * Get the zoom factor for the view.
 *
 * Return value: 
 **/
gdouble
dia_canvas_view_get_zoom (DiaCanvasView *view)
{
	g_return_val_if_fail (DIA_IS_CANVAS_VIEW (view), 1.0);

	return GNOME_CANVAS (view)->pixels_per_unit;
}

/**
 * dia_canvas_view_set_zoom:
 * @view: 
 * @zoom: 
 *
 * Set the zoom factor for the canvas. 1.0 means 100%.
 **/
void
dia_canvas_view_set_zoom (DiaCanvasView *view, gdouble zoom)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (zoom > 0.0);

	if (GNOME_CANVAS (view)->pixels_per_unit == zoom)
		return;

	gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (view), zoom);

	update_extents (view);

	dia_canvas_view_request_update (view);

	g_object_notify (G_OBJECT (view), "zoom");
}

/**
 * dia_canvas_view_get_tool:
 * @view: 
 *
 * Return value: The tool or %NULL if no tool is set.
 **/
DiaTool*
dia_canvas_view_get_tool (DiaCanvasView *view)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));

	return view->tool;
}

/**
 * dia_canvas_view_set_tool:
 * @view: 
 * @tool: The #DiaTool 
 *
 * Set a tool to use by this canvas view. Tools are used to override the
 * default behavior of the view. @tool may be %NULL, so the tool is removed
 * from the view.
 **/
void
dia_canvas_view_set_tool (DiaCanvasView *view, DiaTool *tool)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (DIA_IS_TOOL (tool) || tool == NULL);

	if (view->tool) {
		g_object_unref (view->tool);
	}
	view->tool = tool;
	if (view->tool) {
		g_object_ref (view->tool);
	}
	g_object_notify (G_OBJECT (view), "tool");
}

/**
 * dia_canvas_view_get_default_tool:
 * @view: 
 *
 * 
 *
 * Return value: the default tool
 **/
DiaTool*
dia_canvas_view_get_default_tool (DiaCanvasView *view)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));

	return view->default_tool;
}

/**
 * dia_canvas_view_set_default_tool:
 * @view: 
 * @default_tool: 
 *
 * Set the default tool for the view. The default tool is used to do the
 * default behavior for the view, such as moving handles and editing the
 * diagram items.
 *
 * This tool is used if no tool is set via dia_canvas_view_set_tool().
 **/
void
dia_canvas_view_set_default_tool (DiaCanvasView *view, DiaTool *default_tool)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (DIA_IS_TOOL (default_tool));

	if (view->default_tool)
		g_object_unref (view->default_tool);
	view->default_tool = default_tool;
	if (view->default_tool)
		g_object_ref (view->default_tool);
	g_object_notify (G_OBJECT (view), "tool");
}

/* Select @item. only interactive items are selected.
 * TRUE is returned, so read_select can also be used by
 * dia_canvas_view_foreach().
 */
static gint
real_select (DiaCanvasViewItem *item, gpointer data)
{
	DiaCanvasView *view = data;

	if (!DIA_CANVAS_ITEM_INTERACTIVE(item->item))
		return TRUE;

	/* Find the uppermost canvas view item that should be selected. */
	while (DIA_CANVAS_ITEM_COMPOSITE (item->item))
		item = DIA_CANVAS_VIEW_ITEM (GNOME_CANVAS_ITEM (item)->parent);

	if (!g_list_find (view->selected_items, item)) {
		view->selected_items = g_list_append (view->selected_items,
						      item);

		gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (item));

		g_signal_emit(view, canvas_view_signals[SELECT_ITEM], 0, item);
	}
	return TRUE;
}

/**
 * dia_canvas_view_select:
 * @view: 
 * @item: 
 *
 * Select @item. If the item is a composite item (item->item has DIA_COMPOSITE
 * set), the parent is selected.
 **/
void
dia_canvas_view_select (DiaCanvasView *view, DiaCanvasViewItem *item)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (DIA_IS_CANVAS_VIEW_ITEM (item));

	real_select (item, view);
}

static gint
real_select_rectangle (DiaCanvasViewItem *item, gpointer data)
{
	if (g_list_find (data, item->item))
		real_select (item, DIA_CANVAS_VIEW (GNOME_CANVAS_ITEM (item)->canvas));

	return TRUE;
}

/**
 * dia_canvas_view_select_rectangle:
 * @view: 
 * @rect: 
 *
 * Select all object whose bounding box is within @rect.
 **/
void
dia_canvas_view_select_rectangle (DiaCanvasView *view, DiaRectangle *rect)
{
	GList *objs;

	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (rect != NULL);
	
	objs = dia_canvas_find_objects_in_rectangle (view->canvas, rect);
	if (objs) {
		dia_canvas_view_item_foreach (view->root_item,
					      real_select_rectangle, objs);
		g_list_free (objs);
	}
}

/**
 * dia_canvas_view_select_all:
 * @view: 
 *
 * Select all items on the canvas.
 **/
void
dia_canvas_view_select_all (DiaCanvasView *view)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));

	dia_canvas_view_item_foreach (view->root_item,
				      real_select, view);
}

static gint
real_unselect (DiaCanvasViewItem *item, gpointer view)
{
	/* Find the owning item in case of a composite object. */
	while (DIA_CANVAS_ITEM_COMPOSITE (item->item))
		  item = DIA_CANVAS_VIEW_ITEM (GNOME_CANVAS_ITEM (item)->parent);


	if (DIA_CANVAS_VIEW_ITEM_SELECT (item)) {
		DIA_CANVAS_VIEW (view)->selected_items = g_list_remove (DIA_CANVAS_VIEW (view)->selected_items, item);

		if (DIA_CANVAS_VIEW_ITEM_FOCUS (item))
			dia_canvas_view_focus (view, NULL);

		//gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (item));
		dia_canvas_item_request_update (item->item);

		g_signal_emit (view, canvas_view_signals[UNSELECT_ITEM],
				0, item);
	}
	return TRUE;
}

/**
 * dia_canvas_view_unselect:
 * @view: #DiaCanvasView
 * @root: item to unselect.
 *
 * Unselect @root and all children of @root. If one of the selected items
 * has the focus, it is de-focused.
 **/
void
dia_canvas_view_unselect (DiaCanvasView *view, DiaCanvasViewItem *root)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (DIA_IS_CANVAS_VIEW_ITEM (root));

	dia_canvas_view_item_foreach (root, real_unselect, view);
}

/**
 * dia_canvas_view_unselect_all:
 * @view: 
 *
 * Do a dia_canvas_view_unselect() for all items in the canvas view.
 **/
void
dia_canvas_view_unselect_all (DiaCanvasView *view)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));

	dia_canvas_view_unselect (view, view->root_item);
}

/**
 * dia_canvas_view_focus:
 * @view: 
 * @item: Item to focus, may be NULL (no focused object).
 *
 * Set the focus to @item. If @item is not an interactive item, the focus
 * is set to NULL.
 **/
void
dia_canvas_view_focus (DiaCanvasView *view, DiaCanvasViewItem *item)
{
	gboolean retval = TRUE;
	DiaEvent ev;

	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail ((item == NULL) || DIA_IS_CANVAS_VIEW_ITEM (item));

	if (view->focus_item == item)
		return;

	/* Unfocus focused object */
	if (view->focus_item) {
		ev.type = DIA_EVENT_FOCUS_OUT;
		// TODO: What to do with focus in/out events?
		//dia_canvas_view_item_emit_event (view->focus_item, &ev);
		//gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (view->focus_item));
		dia_canvas_item_request_update (view->focus_item->item);
	}

	/* Set default return value to TRUE, so the focus event will be
	 * emited, even if the item == NULL or non-interactive. */
	retval = TRUE;

	if (item && DIA_CANVAS_ITEM_INTERACTIVE (item->item)) {
		view->focus_item = item;
		dia_canvas_view_select (view, item);
		ev.type = DIA_EVENT_FOCUS_IN;
		//dia_canvas_view_item_emit_event (item, &ev);
	} else {
		view->focus_item = NULL;
	}

	//if (retval)
	g_signal_emit (view, canvas_view_signals[FOCUS_ITEM], 0, view->focus_item);
}


/* We determine if the parent is in the selected items list by traversing
 * the view-items. */
static gboolean
parent_in_selected_items_list (GList *selected_items, GnomeCanvasItem *item)
{
	if (g_list_find (selected_items, item->parent))
		return TRUE;
	else if (item->parent)
		return parent_in_selected_items_list (selected_items,
						      item->parent);
	return FALSE;
}


/**
 * dia_canvas_view_move:
 * @view: 
 * @dx: movement
 * @dy: 
 * @originator: the object that recieved the event resulting in the movement.
 *
 * Move all selected items. 
 **/
void
dia_canvas_view_move (DiaCanvasView *view, gdouble dx, gdouble dy,
		      DiaCanvasViewItem *originator)
{
	GList *l;
	gdouble w2i[6];

	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail ((originator == NULL) || DIA_IS_CANVAS_VIEW_ITEM (originator));

	for (l = view->selected_items; l != NULL; l = l->next) {
		DiaCanvasItem *item = DIA_CANVAS_VIEW_ITEM (l->data)->item;

		if (!DIA_CANVAS_ITEM_GET_CLASS (item)->move
		    || (l->data == (gpointer) originator)
		    || parent_in_selected_items_list (view->selected_items,
						      l->data))
			continue;

		dia_canvas_item_affine_w2i (DIA_CANVAS_ITEM (item->parent), w2i);

		w2i[4] = dx * w2i[0] + dy * w2i[2];
		w2i[5] = dx * w2i[1] + dy * w2i[3];

		dia_canvas_item_move (item, w2i[4], w2i[5]);
	}
}


static gint
real_request_update (DiaCanvasViewItem *item, gpointer data)
{
	GTK_OBJECT_SET_FLAGS (item, DIA_CANVAS_VIEW_ITEM_UPDATE_SHAPES);

	gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (item));

	return TRUE;
}

/**
 * dia_canvas_view_request_update:
 * @view: 
 *
 * Request an update for all items on the canvas. All shapes will be set
 * to update state too.
 **/
void
dia_canvas_view_request_update (DiaCanvasView *view)
{
	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));

	if (view->root_item)
		dia_canvas_view_item_foreach (view->root_item,
					      real_request_update,
					      NULL);
}

static gint
real_find_view_item (DiaCanvasViewItem *item, gpointer data)
{
	DiaCanvasItem **citem = data;
	
	if (item->item == *citem) {
		DiaCanvasViewItem **vitem = data;
		*vitem = item;
		return FALSE;
	}
	return TRUE;
}

/**
 * dia_canvas_view_find_view_item:
 * @view: 
 * @item: Item to find.
 *
 * Find the #DiaCanvasViewItem that visualizes @item.
 *
 * Return value: The view item or NULL.
 **/
DiaCanvasViewItem*
dia_canvas_view_find_view_item (DiaCanvasView *view, DiaCanvasItem *item)
{
	gpointer data = item;

	g_return_val_if_fail (DIA_IS_CANVAS_VIEW (view), NULL);
	g_return_val_if_fail (DIA_IS_CANVAS_ITEM (item), NULL);

	dia_canvas_view_item_foreach (view->root_item, real_find_view_item,
				      &data);

	if (DIA_IS_CANVAS_VIEW_ITEM (data))
		return data;

	return NULL;
}

/**
 * dia_canvas_view_gdk_event_to_dia_event:
 * @view: 
 * @item: 
 * @gdk_event: The GdkEvent as it was received by the view.
 * @dia_event: 
 *
 * DEPRICATED
 *
 * Convert a GdkEvent to a #DiaEvent. #DiaCanvasItem's can recieve mouse and
 * keyboard events.
 *
 * A transformation is done from the window coorsinates to item coordinates.
 **/
void
dia_canvas_view_gdk_event_to_dia_event (DiaCanvasView *view,
					DiaCanvasViewItem *item,
					GdkEvent *gdk_event,
					DiaEvent *dia_event)
{
	switch (gdk_event->type) {
	case GDK_BUTTON_PRESS:
		dia_event->type = DIA_EVENT_BUTTON_PRESS;
		break;
	case GDK_2BUTTON_PRESS:
		dia_event->type = DIA_EVENT_2BUTTON_PRESS;
		break;
	case GDK_3BUTTON_PRESS:
		dia_event->type = DIA_EVENT_3BUTTON_PRESS;
		break;
	case GDK_BUTTON_RELEASE:
		dia_event->type = DIA_EVENT_BUTTON_RELEASE;
		break;
	case GDK_MOTION_NOTIFY:
		dia_event->type = DIA_EVENT_MOTION;
		break;
	case GDK_KEY_PRESS:
		dia_event->type = DIA_EVENT_KEY_PRESS;
		break;
	case GDK_KEY_RELEASE:
		dia_event->type = DIA_EVENT_KEY_RELEASE;
		break;
	default:
		g_assert_not_reached ();
	}

	switch (dia_event->type) {
	case DIA_EVENT_BUTTON_PRESS:
	case DIA_EVENT_2BUTTON_PRESS:
	case DIA_EVENT_3BUTTON_PRESS:
	case DIA_EVENT_BUTTON_RELEASE:
		gnome_canvas_window_to_world (GNOME_CANVAS (view),
					      gdk_event->button.x, gdk_event->button.y,
					      &dia_event->button.x, &dia_event->button.y);
		gnome_canvas_item_w2i (GNOME_CANVAS_ITEM (item),
				       &dia_event->button.x, &dia_event->button.y);
		dia_canvas_snap_to_grid (view->canvas, &dia_event->button.x,
					 &dia_event->button.y);
		dia_event->button.modifier = (DiaEventMask) gdk_event->button.state;
		dia_event->button.button = gdk_event->button.button;
		break;
	case DIA_EVENT_MOTION:
		gnome_canvas_window_to_world (GNOME_CANVAS (view),
					      gdk_event->motion.x, gdk_event->motion.y,
					      &dia_event->motion.x, &dia_event->motion.y);
		gnome_canvas_item_w2i (GNOME_CANVAS_ITEM (item),
				       &dia_event->motion.x, &dia_event->motion.y);
		dia_canvas_snap_to_grid (view->canvas, &dia_event->motion.x,
					 &dia_event->motion.y);
		dia_event->motion.modifier = (DiaEventMask) gdk_event->motion.state;
		if (item == view->last_item) {
			dia_event->motion.dx = dia_event->motion.x - view->old_x;
			dia_event->motion.dy = dia_event->motion.y - view->old_y;
		} else {
			dia_event->motion.dx = 0.0;
			dia_event->motion.dy = 0.0;
			view->last_item = item;
		}
		view->old_x = dia_event->motion.x;
		view->old_y = dia_event->motion.y;
		break;
	case DIA_EVENT_KEY_PRESS:
	case DIA_EVENT_KEY_RELEASE:
		dia_event->key.keyval = gdk_event->key.keyval;
		dia_event->key.length = gdk_event->key.length;
		dia_event->key.string = gdk_event->key.string;
		dia_event->key.modifier = (DiaEventMask) gdk_event->key.state;
		break;
	default:
		break;
	}
}

/**
 * Update the text properties (font, alignment, etc.).
 */
static void
dia_canvas_view_update_text_view (DiaCanvasView *view)
{
	GtkWidget *text_view;
	DiaCanvasViewItem *item;
	DiaShapeText *text_shape;
	gint width = -1, height = -1;
	gdouble affine[6];
	ArtPoint p = { 0.0, 0.0 };

	g_return_if_fail (DIA_IS_CANVAS_VIEW_ITEM (view->edited_item));
	g_return_if_fail (view->edited_shape != NULL);

	text_view = GTK_WIDGET (view->text_view);
	item = view->edited_item;
	text_shape = view->edited_shape;

	/* set position, width and height */
	gnome_canvas_item_i2c_affine (GNOME_CANVAS_ITEM (item), affine);

	art_affine_multiply (affine, affine, text_shape->affine);

	art_affine_point (&p, &p, affine);

	gtk_layout_move (GTK_LAYOUT (view), text_view, (gint) p.x, (gint) p.y);

#ifdef ENABLE_DEBUG
	g_message (G_STRLOC": creating text view at (%f, %f)", p.x, p.y);
#endif /* ENABLE_DEBUG */

	if (text_shape->max_width < G_MAXINT) {
		if (text_shape->text_width > 0.0) {
			gint text_width = (gint) (text_shape->text_width + 0.5);
			width = MIN (text_width, text_shape->max_width);
		} else
			width = text_shape->max_width;
		width = MAX (MIN (width,
			 	  GTK_WIDGET (view)->allocation.width),
			     MIN_TEXT_VIEW_WIDTH);
	}
	if (text_shape->max_height < G_MAXINT) {
		height = MAX (MIN (text_shape->max_height,
				   GTK_WIDGET (view)->allocation.height),
			      MIN_TEXT_VIEW_HEIGHT);
	}

	gtk_widget_set_size_request (text_view, width, height);
}

static gboolean
idle_text_view_focus_out_cb(gpointer data)
{
	dia_canvas_view_editing_done (data);
	return FALSE;
}

static gint
text_view_focus_out_cb (GtkWidget *widget, GdkEventFocus *event, DiaCanvasView *view)
{
#ifdef ENABLE_DEBUG
	//g_message (G_STRLOC": Received a Focus-Out event...");
#endif

	g_assert (GTK_IS_TEXT_VIEW (widget));
	g_assert (DIA_CANVAS_VIEW (view));

	//dia_canvas_view_editing_done (view);
	g_idle_add (idle_text_view_focus_out_cb, view);

	return FALSE;
}

static void
text_buffer_changed_cb (GtkTextBuffer *buffer, DiaCanvasView *view)
{
	GtkTextIter start, end;
	gchar *str;
	
	g_assert (GTK_IS_TEXT_BUFFER (buffer));
	g_assert (DIA_CANVAS_VIEW (view));

	if (!view->edited_item || !view->edited_shape)
		return;

	gtk_text_buffer_get_bounds (buffer, &start, &end);
	str = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);

	dia_canvas_editable_text_changed (DIA_CANVAS_EDITABLE (view->edited_item->item),
					  view->edited_shape, str);

	dia_canvas_view_update_text_view (view);

	g_free (str);
}

/**
 * dia_canvas_view_start_editing:
 * @view:
 * @item:
 * @text_shape:
 *
 * Start interactive editing of the text in @text_shape. This function
 * starts the editing widget (a #GtkTextView) and puts it over the text
 * being edited. #DiaCanvasItem's that are edited should implement the
 * #DiaCanvasEditable interface.
 *
 * Call dia_canvas_view_editing_done() to end the interactive editing.
 *
 * While editing the #DiaShape refered to by @text_shape may not be destroyed.
 **/
void
dia_canvas_view_start_editing (DiaCanvasView *view, DiaCanvasViewItem *item,
			       DiaShapeText *text_shape)
{
	GtkWidget *text_view;
	GtkTextBuffer *text_buffer;

	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (DIA_IS_CANVAS_VIEW_ITEM (item));
	g_return_if_fail (text_shape != NULL);
	g_return_if_fail (((DiaShape*) text_shape)->type == DIA_SHAPE_TEXT);
	g_return_if_fail (view->edited_item == NULL);
	g_return_if_fail (view->edited_shape == NULL);

	/* If the item has the `grab' on events, make sure to ungrab it first
	 */
	if (GNOME_CANVAS (view)->grabbed_item
	    && DIA_IS_CANVAS_VIEW_ITEM (GNOME_CANVAS (view)->grabbed_item))
		dia_canvas_item_ungrab (DIA_CANVAS_VIEW_ITEM (GNOME_CANVAS (view)->grabbed_item)->item);

	if (view->text_view) {
		text_view = GTK_WIDGET (view->text_view);
	} else {
		text_view = gtk_text_view_new ();
		view->text_view = g_object_ref (text_view);
		gtk_layout_put (GTK_LAYOUT (view), text_view, 0, 0);
		gtk_container_set_resize_mode (GTK_CONTAINER (text_view),
					       GTK_RESIZE_IMMEDIATE);
		g_signal_connect (text_view, "focus-out-event",
				  G_CALLBACK (text_view_focus_out_cb), view);
	}

	/* Create a new, empty text buffer. This ensures that old settings
	 * do not spoil the text display. */
	text_buffer = gtk_text_buffer_new (NULL);
	gtk_text_view_set_buffer (view->text_view, text_buffer);
	g_signal_connect (text_buffer, "changed",
			  G_CALLBACK (text_buffer_changed_cb), view);

	/* Set the text */
	if (text_shape->text)
		gtk_text_buffer_set_text (text_buffer, text_shape->text, -1);

	/* set font */
	if (text_shape->font_desc)
		gtk_widget_modify_font (text_view, text_shape->font_desc);

	/* We still have a reference to our text buffer */
	g_object_unref (text_buffer);

	gtk_text_view_set_cursor_visible (view->text_view, TRUE);

	/* justify + alignment, wrap_mode, line_spacing */
	if (text_shape->justify) {
		gtk_text_view_set_justification (view->text_view,
						 GTK_JUSTIFY_FILL);
	} else {
		GtkJustification j = 0;
		switch (text_shape->alignment) {
		case PANGO_ALIGN_LEFT:
			j = GTK_JUSTIFY_LEFT;
			break;
		case PANGO_ALIGN_CENTER:
			j = GTK_JUSTIFY_CENTER;
			break;
		case PANGO_ALIGN_RIGHT:
			j = GTK_JUSTIFY_RIGHT;
			break;
		default:
			g_assert_not_reached ();
		}
		gtk_text_view_set_justification (view->text_view, j);
	}

	switch (text_shape->wrap_mode) {
	case DIA_WRAP_CHAR:
		gtk_text_view_set_wrap_mode (view->text_view, GTK_WRAP_CHAR);
		break;
	case DIA_WRAP_WORD:
		gtk_text_view_set_wrap_mode (view->text_view, GTK_WRAP_WORD);
		break;
	case DIA_WRAP_NONE:
	default:
		gtk_text_view_set_wrap_mode (view->text_view, GTK_WRAP_NONE);
	}

	gtk_text_view_set_pixels_inside_wrap (view->text_view,
					(int) (text_shape->line_spacing + 0.5));
	gtk_text_view_set_pixels_below_lines (view->text_view,
					(int) (text_shape->line_spacing + 0.5));

	/* Set those after we set the text, this will prevent the 
	 * DiaCanvasEditable::text_changed signal from being emited. */
	view->edited_item = item;
	view->edited_shape = text_shape;

	gtk_widget_show (text_view);

	dia_canvas_view_update_text_view (view);

	gtk_widget_grab_focus (text_view);
}

/**
 * dia_canvas_view_editing_done:
 * @view:
 *
 * This function should be called if a piece of text is being edited.
 * The editing will end after this function is called.
 **/
void
dia_canvas_view_editing_done (DiaCanvasView *view)
{
	GtkTextBuffer *text_buffer;
	GtkTextIter start, end;
	gchar *str;

	g_return_if_fail (DIA_IS_CANVAS_VIEW (view));
	g_return_if_fail (GTK_IS_TEXT_VIEW (view->text_view));
	g_return_if_fail (DIA_IS_CANVAS_VIEW_ITEM (view->edited_item));
	g_return_if_fail (DIA_IS_CANVAS_ITEM (view->edited_item->item));
	g_return_if_fail (view->edited_shape != NULL);
	g_return_if_fail (((DiaShape*) view->edited_shape)->type == DIA_SHAPE_TEXT);

	text_buffer = gtk_text_view_get_buffer (view->text_view);

	gtk_text_buffer_get_bounds (text_buffer, &start, &end);
	str = gtk_text_buffer_get_text (text_buffer, &start, &end, TRUE);

	dia_canvas_editable_editing_done (DIA_CANVAS_EDITABLE (view->edited_item->item),
					  view->edited_shape, str);

	g_free (str);

	view->edited_item = NULL;
	view->edited_shape = NULL;

	/* Seems to crash: */
	//gtk_widget_hide (GTK_WIDGET (view->text_view));
	/* Maybe this will work: */
	gtk_container_remove (GTK_CONTAINER (view), GTK_WIDGET (view->text_view));
	g_object_unref (view->text_view);
	view->text_view = NULL;
}

/**
 * dia_canvas_view_get_active_view:
 *
 * This function returns the view that was last edited (by mouse click or
 * key event).
 *
 * Return value: The last used view.
 **/
DiaCanvasView*
dia_canvas_view_get_active_view (void)
{
	if (G_IS_OBJECT (active_view))
		return active_view;

	return NULL;
}
