/* dia-canvas-text.c
 * Copyright (C) 2002-2003  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-text.h"
#include "dia-canvas-editable.h"
#include "dia-handle.h"
#include "dia-shape.h"
#include <libart_lgpl/art_affine.h>
#include <pango/pango-layout.h>
#include "diamarshal.h"
#include "dia-canvas-i18n.h"
/* Key syms */
#include <gdk/gdkkeysyms.h>

/* Text pasting support: */
#include <gtk/gtkclipboard.h>

enum {
	PROP_FONT = 1,
	PROP_TEXT,
	PROP_CURSOR,
	PROP_HEIGHT,
	PROP_WIDTH,
	PROP_EDITABLE,
	PROP_MARKUP,
	PROP_MULTILINE,
	PROP_WRAP_WORD,
	PROP_COLOR,
	PROP_ALIGNMENT,
	PROP_LAYOUT
};

static void dia_canvas_text_class_init (DiaCanvasTextClass *klass);
static void dia_canvas_text_editable_init (DiaCanvasEditableIface *editable);
static void dia_canvas_text_init (DiaCanvasText *item);
static void dia_canvas_text_dispose (GObject *object);
static void dia_canvas_text_set_property (GObject *object,
					     guint property_id,
					     const GValue *value,
					     GParamSpec *pspec);
static void dia_canvas_text_get_property (GObject *object,
					     guint property_id,
					     GValue *value,
					     GParamSpec *pspec);
static void dia_canvas_text_update_shape (DiaCanvasText *text);
static void dia_canvas_text_update (DiaCanvasItem *item, gdouble affine[6]);
static gboolean dia_canvas_text_get_shape_iter	(DiaCanvasItem *item,
						 DiaCanvasIter *iter);
static gboolean dia_canvas_text_shape_next	(DiaCanvasItem *item,
						 DiaCanvasIter *iter);
static DiaShape* dia_canvas_text_shape_value	(DiaCanvasItem *item,
						 DiaCanvasIter *iter);
static gdouble dia_canvas_text_point (DiaCanvasItem *item,
				         gdouble x, gdouble y);

/* Interface functions: */
static void dia_canvas_text_editable_start_editing (DiaCanvasEditable *editable,
						    DiaShapeText *text_shape);
static void dia_canvas_text_editable_editing_done  (DiaCanvasEditable *editable,
						    DiaShapeText *text_shape,
						    const gchar *new_text);
static void dia_canvas_text_editable_text_changed  (DiaCanvasEditable *editable,
						    DiaShapeText *text_shape,
						    const gchar *new_text);

static DiaCanvasItemClass *parent_class = NULL;

GType
dia_canvas_text_get_type (void)
{
	static GType object_type = 0;

	if (!object_type) {
		static const GTypeInfo object_info = {
			sizeof (DiaCanvasTextClass),
			(GBaseInitFunc) NULL,
			(GBaseFinalizeFunc) NULL,
			(GClassInitFunc) dia_canvas_text_class_init,
			(GClassFinalizeFunc) NULL,
			(gconstpointer) NULL, /* class_data */
			sizeof (DiaCanvasText),
			(guint16) 0, /* n_preallocs */
			(GInstanceInitFunc) dia_canvas_text_init,
		};
		static const GInterfaceInfo editable_info = {
			(GInterfaceInitFunc) dia_canvas_text_editable_init,
			NULL,
			NULL,
		};

		object_type = g_type_register_static (DIA_TYPE_CANVAS_ITEM,
						      "DiaCanvasText",
						      &object_info, 0);
		g_type_add_interface_static (object_type,
					     DIA_TYPE_CANVAS_EDITABLE,
					     &editable_info);
	}

	return object_type;
}


static void
dia_canvas_text_class_init (DiaCanvasTextClass *klass)
{
	GObjectClass *object_class;
	DiaCanvasItemClass *item_class;
	
	object_class = (GObjectClass*) klass;
	item_class = DIA_CANVAS_ITEM_CLASS (klass);
	
	parent_class = g_type_class_peek_parent (klass);

	object_class->dispose = dia_canvas_text_dispose;
	object_class->get_property = dia_canvas_text_get_property;
	object_class->set_property = dia_canvas_text_set_property;
	
	item_class->update = dia_canvas_text_update;
	item_class->point = dia_canvas_text_point;
	item_class->get_shape_iter = dia_canvas_text_get_shape_iter;
	item_class->shape_next = dia_canvas_text_shape_next;
	item_class->shape_value = dia_canvas_text_shape_value;

	g_object_class_install_property (object_class,
					 PROP_FONT,
					 g_param_spec_boxed ("font",
						_("Font description"),
						_(""),
						PANGO_TYPE_FONT_DESCRIPTION,
						G_PARAM_WRITABLE));
	g_object_class_install_property (object_class,
					 PROP_TEXT,
					 g_param_spec_string ("text",
						_("Text"),
						_("Text to be displayed"),
						NULL,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_CURSOR,
					 g_param_spec_int ("cursor",
						_("Cursor"),
						_("Position of the cursor in the text"),
						-1, G_MAXINT, 1,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_WIDTH,
					 g_param_spec_double ("width",
						_("Width"),
						_("Width of the text"),
						0.0, G_MAXDOUBLE, 1.0,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_HEIGHT,
					 g_param_spec_double ("height",
						_("Height"),
						_("Height of the text"),
						0.0, G_MAXDOUBLE, 1.0,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_EDITABLE,
					 g_param_spec_boolean ("editable",
						_("Editable"),
						_("Text is editable"),
						TRUE,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_MARKUP,
					 g_param_spec_boolean ("markup",
						_("Markup"),
						_("Text has markup symbols"),
						FALSE,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_WRAP_WORD,
					 g_param_spec_boolean ("wrap_word",
						_("Text wrap word"),
						_(""),
						TRUE,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_MULTILINE,
					 g_param_spec_boolean ("multiline",
						_("Multiline"),
						_("Multiline text"),
						TRUE,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_COLOR,
					 g_param_spec_ulong ("color",
						_("Color"),
						_("Color of the text in RGBA"),
						0, G_MAXULONG, 0,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_ALIGNMENT,
					 g_param_spec_enum ("alignment",
						_("Text alignmment"),
						_(""),
						PANGO_TYPE_ALIGNMENT,
						PANGO_ALIGN_LEFT,
						G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_LAYOUT,
					 g_param_spec_object ("layout",
						_("Layout"),
						_("Layout of the text"),
						PANGO_TYPE_LAYOUT,
						G_PARAM_READABLE));
}

static void
dia_canvas_text_editable_init (DiaCanvasEditableIface *editable)
{
	editable->start_editing = dia_canvas_text_editable_start_editing;
	editable->editing_done = dia_canvas_text_editable_editing_done;
	editable->text_changed = dia_canvas_text_editable_text_changed;
}

static void
dia_canvas_text_init (DiaCanvasText *item)
{
	item->text = g_string_new (NULL);
	item->width = item->height = 0.0;
	item->wrap_word = TRUE;
	item->multiline = TRUE;
	item->editable = TRUE;
	item->text_shape = dia_shape_new (DIA_SHAPE_TEXT);

	DIA_CANVAS_ITEM_FLAGS (item) |= DIA_COMPOSITE;
}

static void
dia_canvas_text_set_property (GObject *object, guint property_id,
				 const GValue *value, GParamSpec *pspec)
{
	DiaCanvasText *text = (DiaCanvasText*) (object);
	
	switch (property_id) {
	case PROP_FONT:
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "font");
		if (g_value_get_boxed (value))
			dia_shape_text_set_font_description (text->text_shape,
						g_value_get_boxed (value));
		else
			dia_shape_text_set_font_description (text->text_shape,
							     NULL);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (text));
		break;
	case PROP_TEXT:
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "text");
		if (g_value_get_string (value))
			g_string_assign (text->text, g_value_get_string (value));
		else
			g_string_assign (text->text, "");
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (text));
		break;
	case PROP_CURSOR:
		g_message ("The property 'cursor' of DiaCanvasText is not supported anymore");
		break;
	case PROP_HEIGHT:
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "height");
		text->height = g_value_get_double (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (text));
		break;
	case PROP_WIDTH:
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "width");
		text->width = g_value_get_double (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (text));
		break;
	case PROP_EDITABLE:
		if (text->markup) {
			static gboolean is_issued = FALSE;
			if (!is_issued) {
				g_warning ("You should not use 'editable' in "
					   "conjunction with 'markup'!");
				is_issued = TRUE;
			}
		}
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "editable");
		text->editable = g_value_get_boolean (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (text));
		break;
	case PROP_MARKUP:
		if (text->editable) {
			static gboolean is_issued = FALSE;
			if (!is_issued) {
				g_warning ("You should not use 'editable' in "
					   "conjunction with 'markup'!");
				is_issued = TRUE;
			}
		}
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "markup");
		text->markup = g_value_get_boolean (value);
		// TODO: convert text to/from marked up text
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (text));
		break;
	case PROP_WRAP_WORD:
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "wrap-word");
		text->wrap_word = g_value_get_boolean (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (text));
		break;
	case PROP_MULTILINE:
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "multiline");
		text->multiline = g_value_get_boolean (value);
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (text));
		break;
	case PROP_COLOR:
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "color");
		dia_shape_set_color (text->text_shape,
				     g_value_get_ulong (value));
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (object));
		break;
	case PROP_ALIGNMENT:
		dia_canvas_item_preserve_property (DIA_CANVAS_ITEM (text), "alignment");
		dia_shape_text_set_alignment (text->text_shape,
					      g_value_get_enum (value));
		dia_canvas_item_request_update (DIA_CANVAS_ITEM (object));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

static void
dia_canvas_text_get_property (GObject *object, guint property_id,
				 GValue *value, GParamSpec *pspec)
{
	switch (property_id) {
	case PROP_TEXT:
		if (DIA_CANVAS_TEXT (object)->text)
			g_value_set_string (value,
					    DIA_CANVAS_TEXT (object)->text->str);
		else
			g_value_set_string (value, "");
		break;
	case PROP_CURSOR:
		g_message ("The property 'cursor' of DiaCanvasText is not supported anymore");
		break;
	case PROP_HEIGHT:
		g_value_set_double (value, DIA_CANVAS_TEXT (object)->height);
		break;
	case PROP_WIDTH:
		g_value_set_double (value, DIA_CANVAS_TEXT (object)->width);
		break;
	case PROP_EDITABLE:
		g_value_set_boolean (value, DIA_CANVAS_TEXT (object)->editable);
		break;
	case PROP_MARKUP:
		g_value_set_boolean (value, DIA_CANVAS_TEXT (object)->markup);
		break;
	case PROP_WRAP_WORD:
		g_value_set_boolean (value, DIA_CANVAS_TEXT (object)->wrap_word);
		break;
	case PROP_MULTILINE:
		g_value_set_boolean (value, DIA_CANVAS_TEXT (object)->multiline);
		break;
	case PROP_COLOR:
		g_value_set_ulong (value, DIA_CANVAS_TEXT (object)->text_shape->color);
		break;
	case PROP_ALIGNMENT:
		g_value_set_enum (value, ((DiaShapeText*) DIA_CANVAS_TEXT (object)->text_shape)->alignment);
		break;
	case PROP_LAYOUT:
		dia_canvas_text_update_shape (DIA_CANVAS_TEXT (object));
		g_value_set_object_take_ownership (value,
			dia_shape_text_to_pango_layout (DIA_CANVAS_TEXT (object)->text_shape, TRUE));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
		break;
	}
}

static void
dia_canvas_text_dispose (GObject *object)
{
	DiaCanvasText *text = (DiaCanvasText *) object;
	if (text->text) {
		g_string_free (text->text, TRUE);
		text->text = NULL;
	}
	if (text->text_shape) {
		dia_shape_free (text->text_shape);
		text->text_shape = NULL;
	}
	G_OBJECT_CLASS (parent_class)->dispose (object);
}

static void
dia_canvas_text_update_shape (DiaCanvasText *text)
{
	dia_shape_text_set_static_text (text->text_shape,
					text->text->str);
	dia_shape_text_set_markup (text->text_shape, text->markup);
	dia_shape_text_set_max_width (text->text_shape, text->width);
	dia_shape_text_set_max_height (text->text_shape, text->height);
	dia_shape_text_set_wrap_mode (text->text_shape, text->wrap_word ? PANGO_WRAP_WORD : PANGO_WRAP_CHAR);

	if (text->multiline)
		// TODO: this causes the cursor placement to malfunction
		// for centered text...
		dia_shape_text_set_text_width (text->text_shape, text->width);
	else
		dia_shape_text_set_text_width (text->text_shape, 0);

	/* if (text->editable)
		dia_shape_text_set_cursor (text->text_shape,
					   text->cursor);
	else
		dia_shape_text_set_cursor (text->text_shape, -1);
	*/
}

static void
dia_canvas_text_update (DiaCanvasItem *item, gdouble affine[6])
{
	DiaCanvasText *text = (DiaCanvasText*) item;

	//g_message(__FUNCTION__);
        if (DIA_CANVAS_ITEM_CLASS (parent_class)->update)
		DIA_CANVAS_ITEM_CLASS (parent_class)->update (item, affine);
	
	//g_message (__FUNCTION__": need_update = %d", item->flags & DIA_NEED_UPDATE);

	if (text->text->str) {
		dia_canvas_text_update_shape (text);
		item->bounds.top = 0.0;
		item->bounds.left = 0.0;
		item->bounds.bottom = text->height;
		item->bounds.right = text->width;
	} else {
		item->bounds.top = 0.0;
		item->bounds.left = 0.0;
		item->bounds.bottom = 0.0;
		item->bounds.right = 0.0;
	}
	//g_message (__FUNCTION__": need_update = %d", item->flags & DIA_NEED_UPDATE);
}

static gboolean
dia_canvas_text_get_shape_iter (DiaCanvasItem *item, DiaCanvasIter *iter)
{
	iter->data[0] = DIA_CANVAS_TEXT (item)->text_shape;
	return TRUE;
}

static gboolean
dia_canvas_text_shape_next (DiaCanvasItem *item, DiaCanvasIter *iter)
{
	return FALSE;
}

static DiaShape*
dia_canvas_text_shape_value (DiaCanvasItem *item, DiaCanvasIter *iter)
{
	return iter->data[0];
}

static gdouble
dia_canvas_text_point (DiaCanvasItem *item, gdouble x, gdouble y)
{
	DiaPoint p;

	p.x = x;
	p.y = y;

	return dia_distance_rectangle_point (&item->bounds, &p);
}

static void
dia_canvas_text_editable_start_editing (DiaCanvasEditable *editable,
					DiaShapeText *text_shape)
{
}

static void
dia_canvas_text_editable_editing_done (DiaCanvasEditable *editable,
				       DiaShapeText *text_shape,
				       const gchar *new_text)
{
	g_object_set (G_OBJECT (editable), "text", new_text, NULL);
}

static void
dia_canvas_text_editable_text_changed (DiaCanvasEditable *editable,
				       DiaShapeText *text_shape,
				       const gchar *new_text)
{
	g_object_set (G_OBJECT (editable), "text", new_text, NULL);
	dia_canvas_text_update_shape (DIA_CANVAS_TEXT (editable));
}

