aboutsummaryrefslogblamecommitdiffstats
path: root/libgnomecanvas/gnome-canvas-text.c
blob: 830a2256f83564a4b7d480c133138e789c2aca7f (plain) (tree)










































                                                                             




















                                     
 













                                                   










                             










                                     

                                                                       
                                                                








                                                                         

                                                                             


                                                                                 
                                                                             




                                                                       
                                                            
                                                                                          




                                                                          
                                                                                 












                                                                           

         

                                                                             
  
































                                                                                              


                                               






                                                                     
 




                                             

                                                       






                                                                              

                                                                 

















                                                                              




                                             

                                                                     

                                                                              
 



                                                 

                                                                                         






                                                                             

                                                                                                        

                                                                              
 






                                                                             
 



                                            

                                                 


                                                                          
 



                                              

                                                   


                                                                          
 



                                            

                                                 



                                                                         
 



                                              

                                                   


                                                                          
 



                                          
                                               

                                                                              



                                                                         
 



                                                   

                                                                                         


                                                 

                                                                           



                                          

                                                                                                                



                                                                         
 



                                                       

                                                                            

                                                                             
 



                                                

                                                                       







                                                                          

                                                                               


                                                  

                                                                            
                                       







































                                                                               

                                                              





                                                                              

                                                                 





                                                                             

                                                                                 



                                                                            

                                                   

                                                                   





                                                            

                                                                    



                                                             



                                                                           

                                                    
                                        

                                                                  
                                                  

                                                                 
 
                                                      

                                                                   
 
                                                    

                                                                  
 
                                                      

                                                                   
 
                                                

                                                                
 
                                                

                                                           
 
                                                                  

                                                                
 
                                                          

                                                              

                                                  

                                                               
                   
 
                                                        





                                                            







                                                      







                                               
 


                                                   
 


                                    
 






                                                       
                                                   














                                                          
 







                                                              
 
                                                      
                                                   


                            
 

                                                                         


           




                                  

                              
                       




                                                            


                                               


                                                               






                                                                                



























                                                                  








































                                                                
                        









                                                         
 

                                                                                                           

























                                                                          
                                       






                                                                                   
 


















































                                                                                                   
 

















                                                                                                    
 


                                                         
 

                                                              
 

                                                         

                      


                                                           
 





                                                                  
 






                                                                  
 





                                                               
 






                                                          
 





                                                          
 









                                                                
 



                                                          




                                                               
                                                 

                                        
                                                   

                                       
                                                  

                              

                                                                   
                              
                 
                                                                 
                                             



                                                                     
                                             



                                                                      
                                             















                                                         
                                        






































                                                                                   










                                                                                              
                                              




                                       
                                                          





                                                              
 







































                                                             
 






                                                                                
 





                                                                                 
 






                                                                                                        
 


                                                                                                     
 


                                                                                                       
 


                                                                                                     
 


                                                                                                       
 


                                                                                                   
 
                                      



                                                                                    










                              

                                                                                    











                                                                                
 





                                                                 
 





                                                                 
 





                                                            
 



                                                           












































                                                                                      

















                                                                                                 






                                                                   



























                                                                                
 





                                                                                         
 





















































                                                                                                 



                                                

                              
                               






                                                                          










                                                         

                              






                                                 



                                                        



















                                                                     
                                                                

















                                                            





                                                                                       
                                     
                        
                                                                     
                                          


                              
                            


                                        





                                                                               

                                                    
                                        

                                                                           
 























                                                                       
                                                                 
                                                      
                                    

                 
                                                     
 
                                      
 
                    



                                      




                                                

                              
                              













                                                                        
                          
                           
 
/* -*- 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.
 *
 * The Gnome 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.
 *
 * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
/*
  @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 <pango/pangoft2.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
};

struct _GnomeCanvasTextPrivate {
    guint render_dirty : 1;
    FT_Bitmap bitmap;
};

static void gnome_canvas_text_class_init (GnomeCanvasTextClass *class);
static void gnome_canvas_text_init (GnomeCanvasText *text);
static void gnome_canvas_text_destroy (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, gdouble *affine,
                      ArtSVP *clip_path, gint flags);
static void gnome_canvas_text_realize (GnomeCanvasItem *item);
static void gnome_canvas_text_unrealize (GnomeCanvasItem *item);
static void gnome_canvas_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
                    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);

static GnomeCanvasItemClass *parent_class;



/**
 * gnome_canvas_text_get_type:
 * @void:
 *
 * Registers the &GnomeCanvasText class if necessary, and returns the type ID
 * associated to it.
 *
 * Return value: The type ID of the &GnomeCanvasText class.
 **/
GType
gnome_canvas_text_get_type (void)
{
    static GType text_type;

    if (!text_type) {
        const GTypeInfo object_info = {
            sizeof (GnomeCanvasTextClass),
            (GBaseInitFunc) NULL,
            (GBaseFinalizeFunc) NULL,
            (GClassInitFunc) gnome_canvas_text_class_init,
            (GClassFinalizeFunc) NULL,
            NULL,           /* class_data */
            sizeof (GnomeCanvasText),
            0,          /* n_preallocs */
            (GInstanceInitFunc) gnome_canvas_text_init,
            NULL            /* value_table */
        };

        text_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasText",
                            &object_info, 0);
    }

    return text_type;
}

/* 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;

    parent_class = g_type_class_peek_parent (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_READABLE | 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_READABLE | 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->destroy = gnome_canvas_text_destroy;
    item_class->update = gnome_canvas_text_update;
    item_class->realize = gnome_canvas_text_realize;
    item_class->unrealize = gnome_canvas_text_unrealize;
    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;

    text->priv = g_new (GnomeCanvasTextPrivate, 1);
    text->priv->bitmap.buffer = NULL;
    text->priv->render_dirty = 1;
}

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

    g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));

    text = GNOME_CANVAS_TEXT (object);

    /* remember, destroy can be run multiple times! */

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

    if (text->layout)
        g_object_unref (G_OBJECT (text->layout));
    text->layout = NULL;

    if (text->font_desc) {
        pango_font_description_free (text->font_desc);
        text->font_desc = NULL;
    }

    if (text->attr_list)
        pango_attr_list_unref (text->attr_list);
    text->attr_list = NULL;

    if (text->priv && text->priv->bitmap.buffer) {
        g_free (text->priv->bitmap.buffer);
    }
    g_free (text->priv);
    text->priv = NULL;

    if (GNOME_CANVAS_ITEM_CLASS (parent_class)->destroy)
        GNOME_CANVAS_ITEM_CLASS (parent_class)->destroy (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 * item->canvas->pixels_per_unit;
    text->clip_cheight = text->clip_height * item->canvas->pixels_per_unit;

    /* 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;
    }
}

/* Convenience function to set the text's GC's foreground color */
static void
set_text_gc_foreground (GnomeCanvasText *text)
{
    GdkColor c;

    if (!text->gc)
        return;

    c.pixel = text->pixel;
    gdk_gc_set_foreground (text->gc, &c);
}

static PangoFontMask
get_property_font_set_mask (guint prop_id)
{
  switch (prop_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 color = { 0, 0, 0, 0, };
    GdkColor *pcolor;
    gboolean color_changed;
    gint have_pixel;
    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);

    color_changed = FALSE;
    have_pixel = FALSE;

    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);

        text->priv->render_dirty = 1;
        break;

    case PROP_MARKUP:
        gnome_canvas_text_set_markup (text,
                          g_value_get_string (value));
        text->priv->render_dirty = 1;
        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);
        text->priv->render_dirty = 1;
        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);
        text->priv->render_dirty = 1;
        break;

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

        gnome_canvas_text_apply_font_desc (text);
        text->priv->render_dirty = 1;
        break;

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

        gnome_canvas_text_apply_attributes (text);
        text->priv->render_dirty = 1;
        break;

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

        gnome_canvas_text_apply_attributes (text);
        text->priv->render_dirty = 1;
        break;

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

        gnome_canvas_text_apply_attributes (text);
        text->priv->render_dirty = 1;
        break;

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

        gnome_canvas_text_apply_attributes (text);
        text->priv->render_dirty = 1;
        break;

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

        gnome_canvas_text_apply_attributes (text);
        text->priv->render_dirty = 1;
        break;

    case PROP_RISE_SET:
        text->rise_set = TRUE;

        gnome_canvas_text_apply_attributes (text);
        text->priv->render_dirty = 1;
        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);
        text->priv->render_dirty = 1;
        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);
        text->priv->render_dirty = 1;
        break;

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

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

    case PROP_CLIP:
        text->clip = g_value_get_boolean (value);
        text->priv->render_dirty = 1;
        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) {
            gdk_color_parse (color_name, &color);

            text->rgba = ((color.red & 0xff00) << 16 |
                      (color.green & 0xff00) << 8 |
                      (color.blue & 0xff00) |
                      0xff);
            color_changed = TRUE;
        }
        text->priv->render_dirty = 1;
        break;
    }

    case PROP_FILL_COLOR_GDK:
        pcolor = g_value_get_boxed (value);
        if (pcolor) {
            GdkColormap *colormap;

            color = *pcolor;
            colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
            gdk_rgb_find_color (colormap, &color);
            have_pixel = TRUE;
        }

        text->rgba = ((color.red & 0xff00) << 16 |
                  (color.green & 0xff00) << 8|
                  (color.blue & 0xff00) |
                  0xff);
        color_changed = TRUE;
        break;

        case PROP_FILL_COLOR_RGBA:
        text->rgba = g_value_get_uint (value);
        color_changed = TRUE;
        text->priv->render_dirty = 1;
        break;

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

    if (color_changed) {
        if (have_pixel)
            text->pixel = color.pixel;
        else
            text->pixel = gnome_canvas_get_color_pixel (item->canvas, text->rgba);

        set_text_gc_foreground (text);
    }

    /* 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:
        g_value_take_string (value,
                     g_strdup_printf ("#%02x%02x%02x",
                     text->rgba >> 24,
                     (text->rgba >> 16) & 0xff,
                     (text->rgba >> 8) & 0xff));
        break;

    case PROP_FILL_COLOR_GDK: {
        GnomeCanvas *canvas = GNOME_CANVAS_ITEM (text)->canvas;
        GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
        GdkColor color;

        gdk_colormap_query_color (colormap, text->pixel, &color);
        g_value_set_boxed (value, &color);
        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 / text->item.canvas->pixels_per_unit);
        break;

    case PROP_TEXT_HEIGHT:
        g_value_set_double (value, text->height / text->item.canvas->pixels_per_unit);
        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;
    GtkWidget *widget;
    GtkStyle *style;

    widget = GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas);
    style = gtk_widget_get_style (widget);
    font_desc = pango_font_description_copy (style->font_desc);

    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);
    text->priv->render_dirty = 1;
}

/* 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,
                          gdouble *affine,
                          ArtSVP *clip_path,
                          gint flags)
{
    GnomeCanvasText *text;
    gdouble x1, y1, x2, y2;

    text = GNOME_CANVAS_TEXT (item);

    if (parent_class->update)
        (* parent_class->update) (item, affine, clip_path, flags);

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

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

/* Realize handler for the text item */
static void
gnome_canvas_text_realize (GnomeCanvasItem *item)
{
    GtkLayout *layout;
    GdkWindow *bin_window;
    GnomeCanvasText *text;

    text = GNOME_CANVAS_TEXT (item);

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

    layout = GTK_LAYOUT (item->canvas);
    bin_window = gtk_layout_get_bin_window (layout);

    text->gc = gdk_gc_new (bin_window);
}

/* Unrealize handler for the text item */
static void
gnome_canvas_text_unrealize (GnomeCanvasItem *item)
{
    GnomeCanvasText *text;

    text = GNOME_CANVAS_TEXT (item);

    g_object_unref (text->gc);
    text->gc = NULL;

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

/* Draw handler for the text item */
static void
gnome_canvas_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
            gint x, gint y, gint width, gint height)
{
    GnomeCanvasText *text;
    GdkRectangle rect;

    text = GNOME_CANVAS_TEXT (item);

    if (!text->text)
        return;

    if (text->clip) {
        rect.x = text->clip_cx - x;
        rect.y = text->clip_cy - y;
        rect.width = text->clip_cwidth;
        rect.height = text->clip_cheight;

        gdk_gc_set_clip_rectangle (text->gc, &rect);
    }

    gdk_draw_layout (drawable, text->gc, text->cx - x, text->cy - y, text->layout);

    if (text->clip)
        gdk_gc_set_clip_rectangle (text->gc, NULL);
}

/* 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 / item->canvas->pixels_per_unit;
        height = text->height / item->canvas->pixels_per_unit;
    }

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