/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * $Id$
 * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
 * All rights reserved.
 *
 * This file is part of the Gnome Library.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * published by the Free Software Foundation; either the
 *
 * 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 Lesser General Public License
 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
 */
/*
  @NOTATION@
 */
/* Text item type for GnomeCanvas widget
 *
 * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas
 * widget.  Tk is copyrighted by the Regents of the University of California,
 * Sun Microsystems, and other parties.
 *
 *
 * Author: Federico Mena <federico@nuclecu.unam.mx>
 * Port to Pango co-done by Gergõ Érdi <cactus@cactus.rulez.org>
 */

#include <config.h>

#include <math.h>
#include <string.h>
#include "gnome-canvas-text.h"

#include "gnome-canvas-util.h"

/* Object argument IDs */
enum {
	PROP_0,

	/* Text contents */
	PROP_TEXT,
	PROP_MARKUP,

	/* Position */
	PROP_X,
	PROP_Y,

	/* Font */
	PROP_FONT,
	PROP_FONT_DESC,
	PROP_FAMILY, PROP_FAMILY_SET,

	/* Style */
	PROP_ATTRIBUTES,
	PROP_STYLE,         PROP_STYLE_SET,
	PROP_VARIANT,       PROP_VARIANT_SET,
	PROP_WEIGHT,        PROP_WEIGHT_SET,
	PROP_STRETCH,	    PROP_STRETCH_SET,
	PROP_SIZE,          PROP_SIZE_SET,
	PROP_SIZE_POINTS,
	PROP_STRIKETHROUGH, PROP_STRIKETHROUGH_SET,
	PROP_UNDERLINE,     PROP_UNDERLINE_SET,
	PROP_RISE,          PROP_RISE_SET,
	PROP_SCALE,         PROP_SCALE_SET,

	/* Clipping */
	PROP_JUSTIFICATION,
	PROP_CLIP_WIDTH,
	PROP_CLIP_HEIGHT,
	PROP_CLIP,
	PROP_X_OFFSET,
	PROP_Y_OFFSET,

	/* Coloring */
	PROP_FILL_COLOR,
	PROP_FILL_COLOR_GDK,
	PROP_FILL_COLOR_RGBA,

	/* Rendered size accessors */
	PROP_TEXT_WIDTH,
	PROP_TEXT_HEIGHT
};

static void gnome_canvas_text_dispose (GnomeCanvasItem *object);
static void gnome_canvas_text_set_property (GObject            *object,
					    guint               param_id,
					    const GValue       *value,
					    GParamSpec         *pspec);
static void gnome_canvas_text_get_property (GObject            *object,
					    guint               param_id,
					    GValue             *value,
					    GParamSpec         *pspec);

static void gnome_canvas_text_update (GnomeCanvasItem *item,
				      const cairo_matrix_t *matrix,
				      gint flags);
static void gnome_canvas_text_draw (GnomeCanvasItem *item, cairo_t *cr,
				    gint x, gint y, gint width, gint height);
static GnomeCanvasItem *gnome_canvas_text_point (GnomeCanvasItem *item,
                                                 gdouble x,
                                                 gdouble y,
                                                 gint cx,
                                                 gint cy);
static void gnome_canvas_text_bounds (GnomeCanvasItem *item,
				      gdouble *x1, gdouble *y1, gdouble *x2, gdouble *y2);

static void gnome_canvas_text_set_markup (GnomeCanvasText *textitem,
					  const gchar     *markup);

static void gnome_canvas_text_set_font_desc    (GnomeCanvasText *textitem,
						PangoFontDescription *font_desc);

static void gnome_canvas_text_apply_font_desc  (GnomeCanvasText *textitem);
static void gnome_canvas_text_apply_attributes (GnomeCanvasText *textitem);

static void add_attr (PangoAttrList  *attr_list,
		      PangoAttribute *attr);

G_DEFINE_TYPE (
	GnomeCanvasText,
	gnome_canvas_text,
	GNOME_TYPE_CANVAS_ITEM)

/* Class initialization function for the text item */
static void
gnome_canvas_text_class_init (GnomeCanvasTextClass *class)
{
	GObjectClass *gobject_class;
	GnomeCanvasItemClass *item_class;

	gobject_class = (GObjectClass *) class;
	item_class = (GnomeCanvasItemClass *) class;

	gobject_class->set_property = gnome_canvas_text_set_property;
	gobject_class->get_property = gnome_canvas_text_get_property;

	/* Text */
	g_object_class_install_property (
		gobject_class,
		PROP_TEXT,
		g_param_spec_string (
			"text",
			"Text",
			"Text to render",
			NULL,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_MARKUP,
		g_param_spec_string (
			"markup",
			"Markup",
			"Marked up text to render",
			NULL,
			G_PARAM_WRITABLE));

	/* Position */
	g_object_class_install_property (
		gobject_class,
		PROP_X,
		g_param_spec_double (
			"x",
			NULL,
			NULL,
			-G_MAXDOUBLE,
			G_MAXDOUBLE,
			0.0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_Y,
		g_param_spec_double (
			"y",
			NULL,
			NULL,
			-G_MAXDOUBLE,
			G_MAXDOUBLE,
			0.0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	/* Font */
	g_object_class_install_property (
		gobject_class,
		PROP_FONT,
		g_param_spec_string (
			"font",
			"Font",
			"Font description as a string",
			NULL,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_FONT_DESC,
		g_param_spec_boxed (
			"font_desc",
			"Font description",
			"Font description as a PangoFontDescription struct",
			PANGO_TYPE_FONT_DESCRIPTION,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_FAMILY,
		g_param_spec_string (
			"family",
			"Font family",
			"Name of the font family, e.g. "
			"Sans, Helvetica, Times, Monospace",
			NULL,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	/* Style */
	g_object_class_install_property (
		gobject_class,
		PROP_ATTRIBUTES,
		g_param_spec_boxed (
			"attributes",
			NULL,
			NULL,
			PANGO_TYPE_ATTR_LIST,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_STYLE,
		g_param_spec_enum (
			"style",
			"Font style",
			"Font style",
			PANGO_TYPE_STYLE,
			PANGO_STYLE_NORMAL,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_VARIANT,
		g_param_spec_enum (
			"variant",
			"Font variant",
			"Font variant",
			PANGO_TYPE_VARIANT,
			PANGO_VARIANT_NORMAL,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_WEIGHT,
		g_param_spec_int (
			"weight",
			"Font weight",
			"Font weight",
			0,
			G_MAXINT,
			PANGO_WEIGHT_NORMAL,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_STRETCH,
		g_param_spec_enum (
			"stretch",
			"Font stretch",
			"Font stretch",
			PANGO_TYPE_STRETCH,
			PANGO_STRETCH_NORMAL,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_SIZE,
		g_param_spec_int (
			"size",
			"Font size",
			"Font size (as a multiple of PANGO_SCALE, "
			"eg. 12*PANGO_SCALE for a 12pt font size)",
			0,
			G_MAXINT,
			0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_SIZE_POINTS,
		g_param_spec_double (
			"size_points",
			"Font points",
			"Font size in points (eg. 12 for a 12pt font size)",
			0.0,
			G_MAXDOUBLE,
			0.0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_RISE,
		g_param_spec_int (
			"rise",
			"Rise",
			"Offset of text above the baseline "
			"(below the baseline if rise is negative)",
			-G_MAXINT,
			G_MAXINT,
			0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_STRIKETHROUGH,
		g_param_spec_boolean (
			"strikethrough",
			"Strikethrough",
			"Whether to strike through the text",
			FALSE,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_UNDERLINE,
		g_param_spec_enum (
			"underline",
			"Underline",
			"Style of underline for this text",
			PANGO_TYPE_UNDERLINE,
			PANGO_UNDERLINE_NONE,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_SCALE,
		g_param_spec_double (
			"scale",
			"Scale",
			"Size of font, relative to default size",
			0.0,
			G_MAXDOUBLE,
			1.0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_JUSTIFICATION,
		g_param_spec_enum (
			"justification",
			NULL,
			NULL,
			GTK_TYPE_JUSTIFICATION,
			GTK_JUSTIFY_LEFT,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_CLIP_WIDTH,
		g_param_spec_double (
			"clip_width",
			NULL,
			NULL,
			-G_MAXDOUBLE,
			G_MAXDOUBLE,
			0.0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_CLIP_HEIGHT,
		g_param_spec_double (
			"clip_height",
			NULL,
			NULL,
			-G_MAXDOUBLE,
			G_MAXDOUBLE,
			0.0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_CLIP,
		g_param_spec_boolean (
			"clip",
			NULL,
			NULL,
			FALSE,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_X_OFFSET,
		g_param_spec_double (
			"x_offset",
			NULL,
			NULL,
			-G_MAXDOUBLE,
			G_MAXDOUBLE,
			0.0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_Y_OFFSET,
		g_param_spec_double (
			"y_offset",
			NULL,
			NULL,
			-G_MAXDOUBLE,
			G_MAXDOUBLE,
			0.0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_FILL_COLOR,
		g_param_spec_string (
			"fill_color",
			"Color",
			"Text color, as string",
			NULL,
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_FILL_COLOR_GDK,
		g_param_spec_boxed (
			"fill_color_gdk",
			"Color",
			"Text color, as a GdkColor",
			GDK_TYPE_COLOR,
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_FILL_COLOR_RGBA,
		g_param_spec_uint (
			"fill_color_rgba",
			"Color",
			"Text color, as an R/G/B/A combined integer",
			0, G_MAXUINT, 0,
			G_PARAM_READABLE |
			G_PARAM_WRITABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_TEXT_WIDTH,
		g_param_spec_double (
			"text_width",
			"Text width",
			"Width of the rendered text",
			0.0, G_MAXDOUBLE, 0.0,
			G_PARAM_READABLE));

	g_object_class_install_property (
		gobject_class,
		PROP_TEXT_HEIGHT,
		g_param_spec_double (
			"text_height",
			"Text height",
			"Height of the rendered text",
			0.0, G_MAXDOUBLE, 0.0,
			G_PARAM_READABLE));

	/* Style props are set (explicitly applied) or not */
#define ADD_SET_PROP(propname, propval, nick, blurb) \
	g_object_class_install_property ( \
		gobject_class, propval, \
		g_param_spec_boolean ( \
			propname, nick, blurb, FALSE, \
			G_PARAM_READABLE | G_PARAM_WRITABLE))

	ADD_SET_PROP (
		"family_set",
		PROP_FAMILY_SET,
		"Font family set",
		"Whether this tag affects the font family");

	ADD_SET_PROP (
		"style_set",
		PROP_STYLE_SET,
		"Font style set",
		"Whether this tag affects the font style");

	ADD_SET_PROP (
		"variant_set",
		PROP_VARIANT_SET,
		"Font variant set",
		"Whether this tag affects the font variant");

	ADD_SET_PROP (
		"weight_set",
		PROP_WEIGHT_SET,
		"Font weight set",
		"Whether this tag affects the font weight");

	ADD_SET_PROP (
		"stretch_set",
		PROP_STRETCH_SET,
		"Font stretch set",
		"Whether this tag affects the font stretch");

	ADD_SET_PROP (
		"size_set",
		PROP_SIZE_SET,
		"Font size set",
		"Whether this tag affects the font size");

	ADD_SET_PROP (
		"rise_set",
		PROP_RISE_SET,
		"Rise set",
		"Whether this tag affects the rise");

	ADD_SET_PROP (
		"strikethrough_set",
		PROP_STRIKETHROUGH_SET,
		"Strikethrough set",
		"Whether this tag affects strikethrough");

	ADD_SET_PROP (
		"underline_set",
		PROP_UNDERLINE_SET,
		"Underline set",
		"Whether this tag affects underlining");

	ADD_SET_PROP (
		"scale_set",
		PROP_SCALE_SET,
		"Scale set",
		"Whether this tag affects font scaling");
#undef ADD_SET_PROP

	item_class->dispose = gnome_canvas_text_dispose;
	item_class->update = gnome_canvas_text_update;
	item_class->draw = gnome_canvas_text_draw;
	item_class->point = gnome_canvas_text_point;
	item_class->bounds = gnome_canvas_text_bounds;
}

/* Object initialization function for the text item */
static void
gnome_canvas_text_init (GnomeCanvasText *text)
{
	text->x = 0.0;
	text->y = 0.0;
	text->justification = GTK_JUSTIFY_LEFT;
	text->clip_width = 0.0;
	text->clip_height = 0.0;
	text->xofs = 0.0;
	text->yofs = 0.0;
	text->layout = NULL;

	text->font_desc = NULL;

	text->underline = PANGO_UNDERLINE_NONE;
	text->strikethrough = FALSE;
	text->rise = 0;

	text->underline_set = FALSE;
	text->strike_set = FALSE;
	text->rise_set = FALSE;
}

/* Dispose handler for the text item */
static void
gnome_canvas_text_dispose (GnomeCanvasItem *object)
{
	GnomeCanvasText *text;

	g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));

	text = GNOME_CANVAS_TEXT (object);

	g_free (text->text);
	text->text = NULL;

	g_clear_object (&text->layout);
	g_clear_pointer (&text->font_desc, pango_font_description_free);
	g_clear_pointer (&text->attr_list, pango_attr_list_unref);

	GNOME_CANVAS_ITEM_CLASS (gnome_canvas_text_parent_class)->
		dispose (object);
}

static void
get_bounds (GnomeCanvasText *text,
            gdouble *px1,
            gdouble *py1,
            gdouble *px2,
            gdouble *py2)
{
	GnomeCanvasItem *item;
	gdouble wx, wy;

	item = GNOME_CANVAS_ITEM (text);

	/* Get canvas pixel coordinates for text position */

	wx = text->x;
	wy = text->y;
	gnome_canvas_item_i2w (item, &wx, &wy);
	gnome_canvas_w2c (
		item->canvas, wx + text->xofs, wy + text->yofs,
		&text->cx, &text->cy);

	/* Get canvas pixel coordinates for clip rectangle position */

	gnome_canvas_w2c (item->canvas, wx, wy, &text->clip_cx, &text->clip_cy);
	text->clip_cwidth = text->clip_width;
	text->clip_cheight = text->clip_height;

	/* Bounds */

	if (text->clip) {
		*px1 = text->clip_cx;
		*py1 = text->clip_cy;
		*px2 = text->clip_cx + text->clip_cwidth;
		*py2 = text->clip_cy + text->clip_cheight;
	} else {
		*px1 = text->cx;
		*py1 = text->cy;
		*px2 = text->cx + text->max_width;
		*py2 = text->cy + text->height;
	}
}

static PangoFontMask
get_property_font_set_mask (guint property_id)
{
	switch (property_id) {
		case PROP_FAMILY_SET:
			return PANGO_FONT_MASK_FAMILY;
		case PROP_STYLE_SET:
			return PANGO_FONT_MASK_STYLE;
		case PROP_VARIANT_SET:
			return PANGO_FONT_MASK_VARIANT;
		case PROP_WEIGHT_SET:
			return PANGO_FONT_MASK_WEIGHT;
		case PROP_STRETCH_SET:
			return PANGO_FONT_MASK_STRETCH;
		case PROP_SIZE_SET:
			return PANGO_FONT_MASK_SIZE;
	}

	return 0;
}

static void
ensure_font (GnomeCanvasText *text)
{
	if (!text->font_desc)
		text->font_desc = pango_font_description_new ();
}

/* Set_arg handler for the text item */
static void
gnome_canvas_text_set_property (GObject *object,
                                guint param_id,
                                const GValue *value,
                                GParamSpec *pspec)
{
	GnomeCanvasItem *item;
	GnomeCanvasText *text;
	GdkColor *pcolor;
	PangoAlignment align;

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

	item = GNOME_CANVAS_ITEM (object);
	text = GNOME_CANVAS_TEXT (object);

	if (!text->layout)
		text->layout = pango_layout_new (
			gtk_widget_get_pango_context (
			GTK_WIDGET (item->canvas)));

	switch (param_id) {
	case PROP_TEXT:
		g_free (text->text);

		text->text = g_value_dup_string (value);
		pango_layout_set_text (text->layout, text->text, -1);

		break;

	case PROP_MARKUP:
		gnome_canvas_text_set_markup (
			text, g_value_get_string (value));
		break;

	case PROP_X:
		text->x = g_value_get_double (value);
		break;

	case PROP_Y:
		text->y = g_value_get_double (value);
		break;

	case PROP_FONT: {
		const gchar *font_name;
		PangoFontDescription *font_desc;

		font_name = g_value_get_string (value);
		if (font_name)
			font_desc = pango_font_description_from_string (font_name);
		else
			font_desc = NULL;

		gnome_canvas_text_set_font_desc (text, font_desc);
		if (font_desc)
			pango_font_description_free (font_desc);

		break;
	}

	case PROP_FONT_DESC:
		gnome_canvas_text_set_font_desc (text, g_value_peek_pointer (value));
		break;

	case PROP_FAMILY:
	case PROP_STYLE:
	case PROP_VARIANT:
	case PROP_WEIGHT:
	case PROP_STRETCH:
	case PROP_SIZE:
	case PROP_SIZE_POINTS:
		ensure_font (text);

		switch (param_id) {
		case PROP_FAMILY:
			pango_font_description_set_family (
				text->font_desc,
				g_value_get_string (value));
			break;
		case PROP_STYLE:
			pango_font_description_set_style (
				text->font_desc,
				g_value_get_enum (value));
			break;
		case PROP_VARIANT:
			pango_font_description_set_variant (
				text->font_desc,
				g_value_get_enum (value));
			break;
		case PROP_WEIGHT:
			pango_font_description_set_weight (
				text->font_desc,
				g_value_get_int (value));
			break;
		case PROP_STRETCH:
			pango_font_description_set_stretch (
				text->font_desc,
				g_value_get_enum (value));
			break;
		case PROP_SIZE:
			/* FIXME: This is bogus! It should be pixels, not points/PANGO_SCALE! */
			pango_font_description_set_size (
				text->font_desc,
				g_value_get_int (value));
			break;
		case PROP_SIZE_POINTS:
			pango_font_description_set_size (
				text->font_desc,
				g_value_get_double (value) * PANGO_SCALE);
			break;
		}

		gnome_canvas_text_apply_font_desc (text);
		break;

	case PROP_FAMILY_SET:
	case PROP_STYLE_SET:
	case PROP_VARIANT_SET:
	case PROP_WEIGHT_SET:
	case PROP_STRETCH_SET:
	case PROP_SIZE_SET:
		if (!g_value_get_boolean (value) && text->font_desc)
			pango_font_description_unset_fields (
				text->font_desc,
				get_property_font_set_mask (param_id));
		break;

	case PROP_SCALE:
		text->scale = g_value_get_double (value);
		text->scale_set = TRUE;

		gnome_canvas_text_apply_font_desc (text);
		break;

	case PROP_SCALE_SET:
		text->scale_set = g_value_get_boolean (value);

		gnome_canvas_text_apply_font_desc (text);
		break;

	case PROP_UNDERLINE:
		text->underline = g_value_get_enum (value);
		text->underline_set = TRUE;

		gnome_canvas_text_apply_attributes (text);
		break;

	case PROP_UNDERLINE_SET:
		text->underline_set = g_value_get_boolean (value);

		gnome_canvas_text_apply_attributes (text);
		break;

	case PROP_STRIKETHROUGH:
		text->strikethrough = g_value_get_boolean (value);
		text->strike_set = TRUE;

		gnome_canvas_text_apply_attributes (text);
		break;

	case PROP_STRIKETHROUGH_SET:
		text->strike_set = g_value_get_boolean (value);

		gnome_canvas_text_apply_attributes (text);
		break;

	case PROP_RISE:
		text->rise = g_value_get_int (value);
		text->rise_set = TRUE;

		gnome_canvas_text_apply_attributes (text);
		break;

	case PROP_RISE_SET:
		text->rise_set = TRUE;

		gnome_canvas_text_apply_attributes (text);
		break;

	case PROP_ATTRIBUTES:
		if (text->attr_list)
			pango_attr_list_unref (text->attr_list);

		text->attr_list = g_value_peek_pointer (value);
		pango_attr_list_ref (text->attr_list);

		gnome_canvas_text_apply_attributes (text);
		break;

	case PROP_JUSTIFICATION:
		text->justification = g_value_get_enum (value);

		switch (text->justification) {
		case GTK_JUSTIFY_LEFT:
			align = PANGO_ALIGN_LEFT;
			break;
		case GTK_JUSTIFY_CENTER:
			align = PANGO_ALIGN_CENTER;
			break;
		case GTK_JUSTIFY_RIGHT:
			align = PANGO_ALIGN_RIGHT;
			break;
		default:
			/* GTK_JUSTIFY_FILL isn't supported yet. */
			align = PANGO_ALIGN_LEFT;
			break;
		}
		pango_layout_set_alignment (text->layout, align);
		break;

	case PROP_CLIP_WIDTH:
		text->clip_width = fabs (g_value_get_double (value));
		break;

	case PROP_CLIP_HEIGHT:
		text->clip_height = fabs (g_value_get_double (value));
		break;

	case PROP_CLIP:
		text->clip = g_value_get_boolean (value);
		break;

	case PROP_X_OFFSET:
		text->xofs = g_value_get_double (value);
		break;

	case PROP_Y_OFFSET:
		text->yofs = g_value_get_double (value);
		break;

	case PROP_FILL_COLOR: {
		const gchar *color_name;

		color_name = g_value_get_string (value);
		if (color_name) {
			GdkColor color;
			if (gdk_color_parse (color_name, &color)) {
				text->rgba = GNOME_CANVAS_COLOR (color.red, color.green, color.blue);
			} else {
				g_warning (
					"%s: Failed to parse color form string '%s'",
					G_STRFUNC, color_name);
			}
		}
		break;
	}

	case PROP_FILL_COLOR_GDK:
		pcolor = g_value_get_boxed (value);
		if (pcolor) {
			text->rgba = GNOME_CANVAS_COLOR (pcolor->red, pcolor->green, pcolor->blue);
		} else {
			text->rgba = 0;
		}
		break;

	case PROP_FILL_COLOR_RGBA:
		text->rgba = g_value_get_uint (value);
		break;

	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
		break;
	}

	/* Calculate text dimensions */

	if (text->layout)
		pango_layout_get_pixel_size (
			text->layout,
			&text->max_width,
			&text->height);
	else {
		text->max_width = 0;
		text->height = 0;
	}

	gnome_canvas_item_request_update (item);
}

/* Get_arg handler for the text item */
static void
gnome_canvas_text_get_property (GObject *object,
                                guint param_id,
                                GValue *value,
                                GParamSpec *pspec)
{
	GnomeCanvasText *text;

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

	text = GNOME_CANVAS_TEXT (object);

	switch (param_id) {
	case PROP_TEXT:
		g_value_set_string (value, text->text);
		break;

	case PROP_X:
		g_value_set_double (value, text->x);
		break;

	case PROP_Y:
		g_value_set_double (value, text->y);
		break;

	case PROP_FONT:
	case PROP_FONT_DESC:
	case PROP_FAMILY:
	case PROP_STYLE:
	case PROP_VARIANT:
	case PROP_WEIGHT:
	case PROP_STRETCH:
	case PROP_SIZE:
	case PROP_SIZE_POINTS:
		ensure_font (text);

		switch (param_id) {
		case PROP_FONT:
		{
			/* FIXME GValue imposes a totally gratuitous string
			 *       copy here, we could just hand off string
			 *       ownership. */
			gchar *str;

			str = pango_font_description_to_string (text->font_desc);
			g_value_set_string (value, str);
			g_free (str);

			break;
		}

		case PROP_FONT_DESC:
			g_value_set_boxed (value, text->font_desc);
			break;

		case PROP_FAMILY:
			g_value_set_string (
				value,
				pango_font_description_get_family (
				text->font_desc));
			break;

		case PROP_STYLE:
			g_value_set_enum (
				value,
				pango_font_description_get_style (
				text->font_desc));
			break;

		case PROP_VARIANT:
			g_value_set_enum (
				value,
				pango_font_description_get_variant (
				text->font_desc));
			break;

		case PROP_WEIGHT:
			g_value_set_int (
				value,
				pango_font_description_get_weight (
				text->font_desc));
			break;

		case PROP_STRETCH:
			g_value_set_enum (
				value,
				pango_font_description_get_stretch (
				text->font_desc));
			break;

		case PROP_SIZE:
			g_value_set_int (
				value,
				pango_font_description_get_size (
				text->font_desc));
			break;

		case PROP_SIZE_POINTS:
			g_value_set_double (
				value, ((gdouble)
				pango_font_description_get_size (
				text->font_desc)) / (gdouble) PANGO_SCALE);
			break;
		}
		break;

	case PROP_FAMILY_SET:
	case PROP_STYLE_SET:
	case PROP_VARIANT_SET:
	case PROP_WEIGHT_SET:
	case PROP_STRETCH_SET:
	case PROP_SIZE_SET:
	{
		PangoFontMask set_mask = text->font_desc ?
			pango_font_description_get_set_fields (text->font_desc) : 0;
		PangoFontMask test_mask = get_property_font_set_mask (param_id);
		g_value_set_boolean (value, (set_mask & test_mask) != 0);

		break;
	}

	case PROP_SCALE:
		g_value_set_double (value, text->scale);
		break;
	case PROP_SCALE_SET:
		g_value_set_boolean (value, text->scale_set);
		break;

	case PROP_UNDERLINE:
		g_value_set_enum (value, text->underline);
		break;
	case PROP_UNDERLINE_SET:
		g_value_set_boolean (value, text->underline_set);
		break;

	case PROP_STRIKETHROUGH:
		g_value_set_boolean (value, text->strikethrough);
		break;
	case PROP_STRIKETHROUGH_SET:
		g_value_set_boolean (value, text->strike_set);
		break;

	case PROP_RISE:
		g_value_set_int (value, text->rise);
		break;
	case PROP_RISE_SET:
		g_value_set_boolean (value, text->rise_set);
		break;

	case PROP_ATTRIBUTES:
		g_value_set_boxed (value, text->attr_list);
		break;

	case PROP_JUSTIFICATION:
		g_value_set_enum (value, text->justification);
		break;

	case PROP_CLIP_WIDTH:
		g_value_set_double (value, text->clip_width);
		break;

	case PROP_CLIP_HEIGHT:
		g_value_set_double (value, text->clip_height);
		break;

	case PROP_CLIP:
		g_value_set_boolean (value, text->clip);
		break;

	case PROP_X_OFFSET:
		g_value_set_double (value, text->xofs);
		break;

	case PROP_Y_OFFSET:
		g_value_set_double (value, text->yofs);
		break;

	case PROP_FILL_COLOR_RGBA:
		g_value_set_uint (value, text->rgba);
		break;

	case PROP_TEXT_WIDTH:
		g_value_set_double (value, text->max_width);
		break;

	case PROP_TEXT_HEIGHT:
		g_value_set_double (value, text->height);
		break;

	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
		break;
	}
}

/* */
static void
gnome_canvas_text_apply_font_desc (GnomeCanvasText *text)
{
	PangoFontDescription *font_desc;
	PangoContext *pango_context;
	GtkWidget *widget;

	widget = GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas);
	pango_context = gtk_widget_create_pango_context (widget);
	font_desc = pango_font_description_copy (pango_context_get_font_description (pango_context));
	g_object_unref (pango_context);

	if (text->font_desc)
		pango_font_description_merge (font_desc, text->font_desc, TRUE);

	pango_layout_set_font_description (text->layout, font_desc);
	pango_font_description_free (font_desc);
}

static void
add_attr (PangoAttrList *attr_list,
          PangoAttribute *attr)
{
	attr->start_index = 0;
	attr->end_index = G_MAXINT;

	pango_attr_list_insert (attr_list, attr);
}

/* */
static void
gnome_canvas_text_apply_attributes (GnomeCanvasText *text)
{
	PangoAttrList *attr_list;

	if (text->attr_list)
		attr_list = pango_attr_list_copy (text->attr_list);
	else
		attr_list = pango_attr_list_new ();

	if (text->underline_set)
		add_attr (attr_list, pango_attr_underline_new (text->underline));
	if (text->strike_set)
		add_attr (attr_list, pango_attr_strikethrough_new (text->strikethrough));
	if (text->rise_set)
		add_attr (attr_list, pango_attr_rise_new (text->rise));

	pango_layout_set_attributes (text->layout, attr_list);
	pango_attr_list_unref (attr_list);
}

static void
gnome_canvas_text_set_font_desc (GnomeCanvasText *text,
                                 PangoFontDescription *font_desc)
{
	if (text->font_desc)
		pango_font_description_free (text->font_desc);

	if (font_desc)
		text->font_desc = pango_font_description_copy (font_desc);
	else
		text->font_desc = NULL;

	gnome_canvas_text_apply_font_desc (text);
}

/* Setting the text from a Pango markup string */
static void
gnome_canvas_text_set_markup (GnomeCanvasText *textitem,
                              const gchar *markup)
{
	PangoAttrList *attr_list = NULL;
	gchar         *text = NULL;
	GError        *error = NULL;

	if (markup && !pango_parse_markup (markup, -1,
					   0,
					   &attr_list, &text, NULL,
					   &error))
	{
		g_warning (
			"Failed to set cell text from markup due to "
			"error parsing markup: %s", error->message);
		g_error_free (error);
		return;
	}

	g_free (textitem->text);
	if (textitem->attr_list)
		pango_attr_list_unref (textitem->attr_list);

	textitem->text = text;
	textitem->attr_list = attr_list;

	pango_layout_set_text (textitem->layout, text, -1);

	gnome_canvas_text_apply_attributes (textitem);
}

/* Update handler for the text item */
static void
gnome_canvas_text_update (GnomeCanvasItem *item,
                          const cairo_matrix_t *matrix,
                          gint flags)
{
	GnomeCanvasText *text;
	gdouble x1, y1, x2, y2;

	text = GNOME_CANVAS_TEXT (item);

	GNOME_CANVAS_ITEM_CLASS (gnome_canvas_text_parent_class)->
		update (item, matrix, flags);

	get_bounds (text, &x1, &y1, &x2, &y2);

	gnome_canvas_update_bbox (
		item,
		floor (x1), floor (y1),
		ceil (x2), ceil (y2));
}

/* Draw handler for the text item */
static void
gnome_canvas_text_draw (GnomeCanvasItem *item,
                        cairo_t *cr,
                        gint x,
                        gint y,
                        gint width,
                        gint height)
{
	GnomeCanvasText *text = GNOME_CANVAS_TEXT (item);

	if (!text->text)
		return;

	cairo_save (cr);

	if (text->clip) {
		cairo_rectangle (
			cr,
			text->clip_cx - x,
			text->clip_cy - y,
			text->clip_cwidth,
			text->clip_cheight);
		cairo_clip (cr);
	}

	cairo_set_source_rgba (
		cr,
		((text->rgba >> 24) & 0xff) / 255.0,
		((text->rgba >> 16) & 0xff) / 255.0,
		((text->rgba >> 8) & 0xff) / 255.0,
		( text->rgba & 0xff) / 255.0);

	cairo_move_to (cr, text->cx - x, text->cy - y);
	pango_cairo_show_layout (cr, text->layout);

	cairo_restore (cr);
}

/* Point handler for the text item */
static GnomeCanvasItem *
gnome_canvas_text_point (GnomeCanvasItem *item,
                         gdouble x,
                         gdouble y,
                         gint cx,
                         gint cy)
{
	GnomeCanvasText *text;
	PangoLayoutIter *iter;
	gint x1, y1, x2, y2;

	text = GNOME_CANVAS_TEXT (item);

	/* The idea is to build bounding rectangles for each of the lines of
	 * text (clipped by the clipping rectangle, if it is activated) and see
	 * whether the point is inside any of these.  If it is, we are done.
	 * Otherwise, calculate the distance to the nearest rectangle.
	 */

	iter = pango_layout_get_iter (text->layout);
	do {
		PangoRectangle log_rect;

		pango_layout_iter_get_line_extents (iter, NULL, &log_rect);

		x1 = text->cx + PANGO_PIXELS (log_rect.x);
		y1 = text->cy + PANGO_PIXELS (log_rect.y);
		x2 = x1 + PANGO_PIXELS (log_rect.width);
		y2 = y1 + PANGO_PIXELS (log_rect.height);

		if (text->clip) {
			if (x1 < text->clip_cx)
				x1 = text->clip_cx;

			if (y1 < text->clip_cy)
				y1 = text->clip_cy;

			if (x2 > (text->clip_cx + text->clip_width))
				x2 = text->clip_cx + text->clip_width;

			if (y2 > (text->clip_cy + text->clip_height))
				y2 = text->clip_cy + text->clip_height;

			if ((x1 >= x2) || (y1 >= y2))
				continue;
		}

		/* Calculate distance from point to rectangle */

		if (cx >= x1 && cx < x2 && cy >= y1 && cy < y2) {
			pango_layout_iter_free (iter);
			return item;
		}

	} while (pango_layout_iter_next_line (iter));

	pango_layout_iter_free (iter);

	return NULL;
}

/* Bounds handler for the text item */
static void
gnome_canvas_text_bounds (GnomeCanvasItem *item,
                          gdouble *x1,
                          gdouble *y1,
                          gdouble *x2,
                          gdouble *y2)
{
	GnomeCanvasText *text;
	gdouble width, height;

	text = GNOME_CANVAS_TEXT (item);

	*x1 = text->x;
	*y1 = text->y;

	if (text->clip) {
		width = text->clip_width;
		height = text->clip_height;
	} else {
		width = text->max_width;
		height = text->height;
	}

	*x2 = *x1 + width;
	*y2 = *y1 + height;
}
