text: /* -*- 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 "evolution-config.h"
  
  #include <math.h>
  #include <string.h>
  #include "gnome-canvas-text.h"
  
  #include "gnome-canvas-util.h"
  #include "gnome-canvas-i18n.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;
  
  	if (text->layout != NULL) {
  		g_object_unref (text->layout);
  		text->layout = NULL;
  	}
  
  	if (text->font_desc != NULL) {
  		pango_font_description_free (text->font_desc);
  		text->font_desc = NULL;
  	}
  
  	if (text->attr_list != NULL) {
  		pango_attr_list_unref (text->attr_list);
  		text->attr_list = NULL;
  	}
  
  	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;
  }
  