aboutsummaryrefslogblamecommitdiffstats
path: root/libgnomecanvas/gnome-canvas-rich-text.c
blob: 8b5b2030a006b6e5c3bc4a10a708092f57e04e80 (plain) (tree)



































                                                                          


                                  



                                    
                    

                                
                     
                        
                              
                                                  
                    









                                       





                                










                                       
                    


































                                          


                                                                                    
                                                                                        
                                                                                    
                                                                                  
                                                                                  
                                                                         


                                                                     

                                                                
                                                                          
                                                               
                                                              
                                                                                 
                                                                 
                                                               
                                                                
                                                          
                                                                     

                                                                          
 


                                                                                 
                                                                      

                                                                                 



                                                                          

                                                             





                           
                                      
















                                                                           


                                                                      





                              
                                                 


                                  
                                               








                                                                 
                                                                   
 


                                                                           
 








                                                                          

                                                       





                                                         

                                                  





                                                                    

                                                  





                                                                    

                                                          





                                                                    

                                                           





                                                                    

                                                                         





                                                          

                                                                        





                                                          

                                                                                      





                                                          

                                                                                      





                                                          

                                                                                                  





                                                          

                                                                  






                                                       

                                                        






                                                          

                                                    






                                                                       

                                                           






                                                        

                                                                        






                                                       

                                                                        






                                                       

                                                                            






                                                      

                                                                        






                                                      

                                                                         






                                                      

                                                                     




                                                      
                                             
                              
                                                 
                                  
                                                                        

















                                                                  
                                                       

     
                                              


                                                         
                                                             








                                                      
                                                                     
                                            
 










                                                                            
                                                                    



                                      
                                                   


                                                               

                                                                                          


















                                                                      
                                                                                    






                                                                       
                                                                                    




                                                                          
                                                            


                                                                                       
                                                                           


                                                     
                                                                                





                                                                                       
                                                                           


                                                                                            

                                                                                











                                                                       
                                                                       
                                                       
                                                                                    







                                                                           
                                                                                    







                                                                       
                                                                                    






                                                                          
 


                                                                                
                                                                                    



                                                                          
 


                                                                                
                                                                                    



                                                                          
 


                                                                                
                                                                                    



                                                                   
 


                                                                         
                                                                                    



                                                                    
 


                                                                          
                                                                                    



                                                                          
 

                                                                                         
                                                                                    

                      
 




                                                                               
                                                                    





                                                                        
                                                                    

































































                                                                               
                                                      
 
                                                                  
 
                                                                  
 
                                                    


                                      
                                                        
 
                                                                  
 
                                                     
 
                                                                    


                                        
                                                                     


                                                                            
                                                            




                                                       
                                                        





                                                       
                                                                        



                           

                                           
                                                                      
                                              





                                                           
                                                              




                                                                         

                                           





                                                                      
                                                                          

                                           
                                                    



                                                               
                                                                               
                                   
                                                                           

                                        
                                                           
                                                
                                                
                                                       
                                                                             


                                            
                                                                   


                                                          
                                                                   

                                                          
 
                                 
                                                               







                                                                       
                                                                      
                                   
                                                                     


                                      

                                                              

                                     

                                                                 





                      
                                                        
                                       


                                                          



                                                                    

                                                              


                 
                                                               


                                          
                                            




                                         
                                                
 
                                           


                      
                                                  




                                                            



                                                                                  
 
                                                 


                              
                                                                     







                                                                             
                                                                              



                                                                            

                                           






                                                                      
                                                                     


                                  
                                                                      
                                   
                                                                            





                                          

                                                          



                                   
                                                                      






                                      

                                                                  


                                                            
                                                                         




                                        
                                                               





                      




                                                                                



                                                 
                                                                           



                                             
                           




                                                     
                                                   
                                                                  
                                                   
                                                                  
 
                                                                                       
                                                                    
                                                                       




                                      
                                                                       




                                                                    
                                                                    

     
                                                                



                         
                                                                    
 
                                                                





                                                            
                                                                     








                                                                                
                                      





                                                 
                                                                   

                                                        
                                                                      
                                               
 
           
                                                              

                                                          
                                                                  













                                                         

                              


                                                                        



                                                                          
                         
                                                              




                                                                          
                           
                                                      
                                                            
                                                            



                                                  
                                                            




                                                                       
                          
                                                      
                                                            




                                                             
                                                            




                                                                        
                       
                                                      
                                                            




                                                                        
                                                            




                                                            
                       
                                                      
                                                            




                                                                         
                                                            




                                                             
                        
                                                    



                                                             
                          
                                                    



                                                            
                       
                                                      
                                                            




                                                                     
                       
                                                      
                                                            




                                                                    
                          
                                                    



                                                              
                         
                                                    



                                                             
                       
                                                      
                                                            




                                                                      
                       
                                                      
                                                            






                                                                     

                               
                                                      
                                                                   



                                                               
                                                                   



                                                           
                       
                                                      
                                                                   



                                                           
                                                                   



                                                               
                               
                                                      
                                                                   



                                                                
                                                                   



                                                            
                       
                                                      
                                                                   



                                                                    
                       
                                                      
                                                                   



                                                                
                           
                                                   
                                                                   



                                                                
                               
                                                   
                                                                   












                                                                               



                                                                        


                                               
                                                               
 



                                              
                                                                





                                                            
                      
 
                                                                  







                                       
                                                                 

                                                                
                                                                  

                                
                           


                                                                           
 
                                                                                   





                                                                        
                                                                               
















                                                                      


                                                                   



                                                             
                                                                     










                                                                         
 
                                                                        



                             


                                                                           
 

                                                                      

                 


                                                                                
                                


                                                                              











                                                                         
                                                                        



                             

                                                                                     


                                                                 
                                                               

                                                                 
                                                               
                                                                 
                                                                       



                                                                      


                                                                                
                                


                                                                              





                                                                        

                                                 


                                                                  
 



                                                 
                                                                   

                                                                  
                                                                  
                           


                                                                           
 





                                                       
                                                                            







                                                                   
                                                           

                                                                        
                                                                                








                                                   
                                                             

                                                           
                                                                  

                                                                 

                                                                               





                                             
                                                              

                                                            
                                                                  

                                                                 

                                                                                





                                              
                                                         
 
                                            
 


















                                     
                                                               



                                     
 
                                             




                                          
                                                                       


                                                                         
                                                        

                                                     
                                                                 


                                           
                                     

         
                            


                          
                                                                     
 
                                                                  
                  
 
                                                    




                                    

                                                                                     






                                                                              


                                                          



                              
                                                               

                                                     
                                                                 

                                                     
                                                                  

                                                        
                                                                    

                                                        







                                                                    


                                                  
                                                                      

                                                               
                                                                       
                                                               
         









                                        
  



                                                                           
                                                                
 

                                             
 
                                                         



                                                                                






                                                    
                                                                 
 

                                             
 
                                                          


                                                                                  






                                                               
                                                                  
 

                                             
 
                                                           





                                                                                  
                           
 
                                                                  

                                          
                                                                





                              
                        
 
                                                                  

                         

                                               
 
                                                                           
                    
                                                            

                                                         
                                                            

                                                        
                                                                           





                            
                                                                     








                                                                       
                                                                




                                                      
                                                                     


                                                       
                                                                               
 
                                                               



                                                          

                                                                                       





                                                                
                                                                    

                                         
                                                             






                                                                      
                              
 
                                                                  
 
                                                                    




                      
                                                          
 
                                                                  




                                               
                                                             


                                                                         
                                          


                           
                                            
 
                                                                  
 



                                                                  

                         
      
 
                      
                                       



                                                                      
                                                     

                                                                
                                                                  




                                                                     
                                                             
                                                                

                                           
                                                                
                                                                          
 

                                                                              
 
                                                                            


                                       
                                   
 
                                                                                






                                                                         
                                          

                       




                                     

                                                 
    
                                                             

                                                        

                                                                         




                                          
                                                                




                                     
                                                 

                                        
                                                                                 

         
                                                                    


                                         
                                      



                                   


                                                            




                                   









                                                                         
                                                             
 
                                                                      
 
                                 

                                         


















                                                                              






                                               

                                                            















                                                                       
           
                                                                            




                                                                           
 







                                                                      
                                                                





                                                        
                                                             
 
                                                                                           
 


                                                                        


                                            
                                                    

                                                                             
                                                                                
                    
                                                                        
 
                                                                       
 



                                                                              
 
                                              

                                                                       

                                                        
 
                                                   
 
                                                                  
                                                                    













                                                                            
                                                                               
 
                                                  
 
                                  


                                                                     
                                  





                                                                 
                                                                 

                                  




                                                                                   




                                             
                                                                        
 
                     























































                                             
                                                                  
                                      
 
                                                         


                                
 
                                          





                                     



                                                            






                   





                                                         
 
                                                                  



                                              
                                                                      
                                                            
 
                                                                  
                               

                          
                                                           

                                                
                                              
 
                                                                            
                                
                                                 

                                                                 
                                                        
                                     
 
             
                                                                          
                                                                             
 
                                                                  


                               




                                                                          
                                            






















                                                         
                                        


                                    
                                                                          
                                                                    
 
                                                                  

                          


                                       


                        


                                                    
 
                                            




                                        

                                         





                  
                                                                        



                                              
                                             
                              
                                    
                       
                         
                                                 





                                                                 
                                                                          





                                                                             
                                                                           

                                                                     




                               


                                                       



                                           

                                                                                                     

         

                                                   
 


                                                                            
 




                                                                         



                                      
/* Editable GnomeCanvas text item based on GtkTextLayout, borrowed heavily
 * from GtkTextView.
 *
 * Copyright (c) 2000 Red Hat, Inc.
 * Copyright (c) 2001 Joe Shaw
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <math.h>
#include <stdio.h>
#include <string.h>

#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
#include <gtk/gtktextdisplay.h>

#include "gnome-canvas.h"
#include "gnome-canvas-util.h"
#include "gnome-canvas-rich-text.h"
#include "gnome-canvas-i18n.h"

/* backward-compatibility cruft */
#include "e-util/gtk-compat.h"

struct _GnomeCanvasRichTextPrivate {
    GtkTextLayout *layout;
    GtkTextBuffer *buffer;

    gchar *text;

    /* Position at anchor */
    gdouble x, y;
    /* Dimensions */
    gdouble width, height;
    /* Top-left canvas coordinates for text */
    gint cx, cy;

    gboolean cursor_visible;
    gboolean cursor_blink;
    gboolean editable;
    gboolean visible;
    gboolean grow_height;
    GtkWrapMode wrap_mode;
    GtkJustification justification;
    GtkTextDirection direction;
    GtkAnchorType anchor;
    gint pixels_above_lines;
    gint pixels_below_lines;
    gint pixels_inside_wrap;
    gint left_margin;
    gint right_margin;
    gint indent;

    guint preblink_timeout;
    guint blink_timeout;

    guint selection_drag_handler;

    gint drag_start_x;
    gint drag_start_y;

    gboolean just_selected_element;

    gint clicks;
    guint click_timeout;
};

enum {
    PROP_0,
    PROP_TEXT,
    PROP_X,
    PROP_Y,
    PROP_WIDTH,
    PROP_HEIGHT,
    PROP_EDITABLE,
    PROP_VISIBLE,
    PROP_CURSOR_VISIBLE,
    PROP_CURSOR_BLINK,
    PROP_GROW_HEIGHT,
    PROP_WRAP_MODE,
    PROP_JUSTIFICATION,
    PROP_DIRECTION,
    PROP_ANCHOR,
    PROP_PIXELS_ABOVE_LINES,
    PROP_PIXELS_BELOW_LINES,
    PROP_PIXELS_INSIDE_WRAP,
    PROP_LEFT_MARGIN,
    PROP_RIGHT_MARGIN,
    PROP_INDENT
};

enum {
    TAG_CHANGED,
    LAST_SIGNAL
};

static GnomeCanvasItemClass *parent_class;
static guint signals[LAST_SIGNAL] = { 0 };

static void gnome_canvas_rich_text_class_init (GnomeCanvasRichTextClass *klass);
static void gnome_canvas_rich_text_init (GnomeCanvasRichText *text);
static void gnome_canvas_rich_text_set_property (GObject *object, guint property_id,
                        const GValue *value, GParamSpec *pspec);
static void gnome_canvas_rich_text_get_property (GObject *object, guint property_id,
                        GValue *value, GParamSpec *pspec);
static void gnome_canvas_rich_text_update (GnomeCanvasItem *item, gdouble *affine,
                      ArtSVP *clip_path, gint flags);
static void gnome_canvas_rich_text_realize (GnomeCanvasItem *item);
static void gnome_canvas_rich_text_unrealize (GnomeCanvasItem *item);
static gdouble gnome_canvas_rich_text_point (GnomeCanvasItem *item,
                       gdouble x, gdouble y,
                       gint cx, gint cy,
                       GnomeCanvasItem **actual_item);
static void gnome_canvas_rich_text_draw (GnomeCanvasItem *item,
                    GdkDrawable *drawable,
                    gint x, gint y, gint width, gint height);
static void gnome_canvas_rich_text_render (GnomeCanvasItem *item,
                      GnomeCanvasBuf *buf);
static gint gnome_canvas_rich_text_event (GnomeCanvasItem *item,
                     GdkEvent *event);
static void gnome_canvas_rich_text_get_bounds (GnomeCanvasItem *text,
                          gdouble *px1, gdouble *py1,
                          gdouble *px2, gdouble *py2);

static void gnome_canvas_rich_text_ensure_layout (GnomeCanvasRichText *text);
static void gnome_canvas_rich_text_destroy_layout (GnomeCanvasRichText *text);
static void gnome_canvas_rich_text_start_cursor_blink (GnomeCanvasRichText *text,
                              gboolean delay);
static void gnome_canvas_rich_text_stop_cursor_blink (GnomeCanvasRichText *text);
static void gnome_canvas_rich_text_move_cursor (GnomeCanvasRichText *text,
                           GtkMovementStep step,
                           gint count,
                           gboolean extend_selection);

static GtkTextBuffer *get_buffer (GnomeCanvasRichText *text);
static gint blink_cb (gpointer data);

#define PREBLINK_TIME 300
#define CURSOR_ON_TIME 800
#define CURSOR_OFF_TIME 400

GType
gnome_canvas_rich_text_get_type (void)
{
    static GType rich_text_type;

    if (!rich_text_type) {
        const GTypeInfo object_info = {
            sizeof (GnomeCanvasRichTextClass),
            (GBaseInitFunc) NULL,
            (GBaseFinalizeFunc) NULL,
            (GClassInitFunc) gnome_canvas_rich_text_class_init,
            (GClassFinalizeFunc) NULL,
            NULL,           /* class_data */
            sizeof (GnomeCanvasRichText),
            0,          /* n_preallocs */
            (GInstanceInitFunc) gnome_canvas_rich_text_init,
            NULL            /* value_table */
        };

        rich_text_type = g_type_register_static (
            GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasRichText",
            &object_info, 0);
    }

    return rich_text_type;
}

static void
gnome_canvas_rich_text_finalize (GObject *object)
{
    GnomeCanvasRichText *text;

    text = GNOME_CANVAS_RICH_TEXT (object);

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

    if (G_OBJECT_CLASS (parent_class)->finalize)
        G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gnome_canvas_rich_text_class_init (GnomeCanvasRichTextClass *klass)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
    GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (klass);

    parent_class = g_type_class_peek_parent (klass);

    gobject_class->set_property = gnome_canvas_rich_text_set_property;
    gobject_class->get_property = gnome_canvas_rich_text_get_property;

    g_object_class_install_property (
        gobject_class,
        PROP_TEXT,
        g_param_spec_string ("text",
                     "Text",
                     "Text to display",
                     NULL,
                     G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_X,
        g_param_spec_double ("x",
                     "X",
                     "X position",
                     -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
                     G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_Y,
        g_param_spec_double ("y",
                     "Y",
                     "Y position",
                     -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
                     G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_WIDTH,
        g_param_spec_double ("width",
                     "Width",
                     "Width for text box",
                     -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
                     G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_HEIGHT,
        g_param_spec_double ("height",
                     "Height",
                     "Height for text box",
                     -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
                     G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_EDITABLE,
        g_param_spec_boolean ("editable",
                      "Editable",
                      "Is this rich text item editable?",
                      TRUE,
                      G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_VISIBLE,
        g_param_spec_boolean ("visible",
                      "Visible",
                      "Is this rich text item visible?",
                      TRUE,
                      G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_CURSOR_VISIBLE,
        g_param_spec_boolean ("cursor_visible",
                      "Cursor Visible",
                      "Is the cursor visible in this rich text item?",
                      TRUE,
                      G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_CURSOR_BLINK,
        g_param_spec_boolean ("cursor_blink",
                      "Cursor Blink",
                      "Does the cursor blink in this rich text item?",
                      TRUE,
                      G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_GROW_HEIGHT,
        g_param_spec_boolean ("grow_height",
                      "Grow Height",
                      "Should the text box height grow if the text does not fit?",
                      FALSE,
                      G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_WRAP_MODE,
        g_param_spec_enum ("wrap_mode",
                   "Wrap Mode",
                   "Wrap mode for multiline text",
                   GTK_TYPE_WRAP_MODE,
                   GTK_WRAP_WORD,
                   G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_JUSTIFICATION,
        g_param_spec_enum ("justification",
                   "Justification",
                   "Justification mode",
                   GTK_TYPE_JUSTIFICATION,
                   GTK_JUSTIFY_LEFT,
                   G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_DIRECTION,
        g_param_spec_enum ("direction",
                   "Direction",
                   "Text direction",
                   GTK_TYPE_DIRECTION_TYPE,
                   gtk_widget_get_default_direction (),
                   G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_ANCHOR,
        g_param_spec_enum ("anchor",
                   "Anchor",
                   "Anchor point for text",
                   GTK_TYPE_ANCHOR_TYPE,
                   GTK_ANCHOR_NW,
                   G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_PIXELS_ABOVE_LINES,
        g_param_spec_int ("pixels_above_lines",
                  "Pixels Above Lines",
                  "Number of pixels to put above lines",
                  G_MININT, G_MAXINT,
                  0,
                  G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_PIXELS_BELOW_LINES,
        g_param_spec_int ("pixels_below_lines",
                  "Pixels Below Lines",
                  "Number of pixels to put below lines",
                  G_MININT, G_MAXINT,
                  0,
                  G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_PIXELS_INSIDE_WRAP,
        g_param_spec_int ("pixels_inside_wrap",
                  "Pixels Inside Wrap",
                  "Number of pixels to put inside the wrap",
                  G_MININT, G_MAXINT,
                  0,
                  G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_LEFT_MARGIN,
        g_param_spec_int ("left_margin",
                  "Left Margin",
                  "Number of pixels in the left margin",
                  G_MININT, G_MAXINT,
                  0,
                  G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_RIGHT_MARGIN,
        g_param_spec_int ("right_margin",
                  "Right Margin",
                  "Number of pixels in the right margin",
                  G_MININT, G_MAXINT,
                  0,
                  G_PARAM_READWRITE));
    g_object_class_install_property (
        gobject_class,
        PROP_INDENT,
        g_param_spec_int ("indent",
                  "Indentation",
                  "Number of pixels for indentation",
                  G_MININT, G_MAXINT,
                  0,
                  G_PARAM_READWRITE));

    /* Signals */
    signals[TAG_CHANGED] = g_signal_new (
        "tag_changed",
        G_TYPE_FROM_CLASS (object_class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (GnomeCanvasRichTextClass, tag_changed),
        NULL, NULL,
        g_cclosure_marshal_VOID__OBJECT,
        G_TYPE_NONE, 1,
        G_TYPE_OBJECT);

    gobject_class->finalize = gnome_canvas_rich_text_finalize;

    item_class->update = gnome_canvas_rich_text_update;
    item_class->realize = gnome_canvas_rich_text_realize;
    item_class->unrealize = gnome_canvas_rich_text_unrealize;
    item_class->draw = gnome_canvas_rich_text_draw;
    item_class->point = gnome_canvas_rich_text_point;
    item_class->render = gnome_canvas_rich_text_render;
    item_class->event = gnome_canvas_rich_text_event;
    item_class->bounds = gnome_canvas_rich_text_get_bounds;
} /* gnome_canvas_rich_text_class_init */

static void
gnome_canvas_rich_text_init (GnomeCanvasRichText *text)
{
#if 0
    GtkObject *object = GTK_OBJECT (text);

    object->flags |= GNOME_CANVAS_ITEM_ALWAYS_REDRAW;
#endif
    text->_priv = g_new0 (GnomeCanvasRichTextPrivate, 1);

    /* Try to set some sane defaults */
    text->_priv->cursor_visible = TRUE;
    text->_priv->cursor_blink = TRUE;
    text->_priv->editable = TRUE;
    text->_priv->visible = TRUE;
    text->_priv->grow_height = FALSE;
    text->_priv->wrap_mode = GTK_WRAP_WORD;
    text->_priv->justification = GTK_JUSTIFY_LEFT;
    text->_priv->direction = gtk_widget_get_default_direction ();
    text->_priv->anchor = GTK_ANCHOR_NW;

    text->_priv->blink_timeout = 0;
    text->_priv->preblink_timeout = 0;

    text->_priv->clicks = 0;
    text->_priv->click_timeout = 0;
} /* gnome_canvas_rich_text_init */

static void
gnome_canvas_rich_text_set_property (GObject *object, guint property_id,
                     const GValue *value, GParamSpec *pspec)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (object);

    switch (property_id) {
    case PROP_TEXT:
        if (text->_priv->text)
            g_free (text->_priv->text);

        text->_priv->text = g_value_dup_string (value);

        gtk_text_buffer_set_text (
            get_buffer (text), text->_priv->text, strlen (text->_priv->text));

        break;
    case PROP_X:
        text->_priv->x = g_value_get_double (value);
        break;
    case PROP_Y:
        text->_priv->y = g_value_get_double (value);
        break;
    case PROP_WIDTH:
        text->_priv->width = g_value_get_double (value);
        break;
    case PROP_HEIGHT:
        text->_priv->height = g_value_get_double (value);
        break;
    case PROP_EDITABLE:
        text->_priv->editable = g_value_get_boolean (value);
        if (text->_priv->layout) {
            text->_priv->layout->default_style->editable =
                text->_priv->editable;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_VISIBLE:
        text->_priv->visible = g_value_get_boolean (value);
        if (text->_priv->layout) {
            text->_priv->layout->default_style->invisible =
                !text->_priv->visible;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_CURSOR_VISIBLE:
        text->_priv->cursor_visible = g_value_get_boolean (value);
        if (text->_priv->layout) {
            gtk_text_layout_set_cursor_visible (
                text->_priv->layout, text->_priv->cursor_visible);

            if (text->_priv->cursor_visible && text->_priv->cursor_blink) {
                gnome_canvas_rich_text_start_cursor_blink (
                    text, FALSE);
            }
            else
                gnome_canvas_rich_text_stop_cursor_blink (text);
        }
        break;
    case PROP_CURSOR_BLINK:
        text->_priv->cursor_blink = g_value_get_boolean (value);
        if (text->_priv->layout && text->_priv->cursor_visible) {
            if (text->_priv->cursor_blink && !text->_priv->blink_timeout) {
                gnome_canvas_rich_text_start_cursor_blink (
                    text, FALSE);
            }
            else if (!text->_priv->cursor_blink && text->_priv->blink_timeout) {
                gnome_canvas_rich_text_stop_cursor_blink (text);
                gtk_text_layout_set_cursor_visible (
                    text->_priv->layout, TRUE);
            }
        }
        break;
    case PROP_GROW_HEIGHT:
        text->_priv->grow_height = g_value_get_boolean (value);
        /* FIXME: Recalc here */
        break;
    case PROP_WRAP_MODE:
        text->_priv->wrap_mode = g_value_get_enum (value);

        if (text->_priv->layout) {
            text->_priv->layout->default_style->wrap_mode =
                text->_priv->wrap_mode;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_JUSTIFICATION:
        text->_priv->justification = g_value_get_enum (value);

        if (text->_priv->layout) {
            text->_priv->layout->default_style->justification =
                text->_priv->justification;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_DIRECTION:
        text->_priv->direction = g_value_get_enum (value);

        if (text->_priv->layout) {
            text->_priv->layout->default_style->direction =
                text->_priv->direction;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_ANCHOR:
        text->_priv->anchor = g_value_get_enum (value);
        break;
    case PROP_PIXELS_ABOVE_LINES:
        text->_priv->pixels_above_lines = g_value_get_int (value);

        if (text->_priv->layout) {
            text->_priv->layout->default_style->pixels_above_lines =
                text->_priv->pixels_above_lines;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_PIXELS_BELOW_LINES:
        text->_priv->pixels_below_lines = g_value_get_int (value);

        if (text->_priv->layout) {
            text->_priv->layout->default_style->pixels_below_lines =
                text->_priv->pixels_below_lines;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_PIXELS_INSIDE_WRAP:
        text->_priv->pixels_inside_wrap = g_value_get_int (value);

        if (text->_priv->layout) {
            text->_priv->layout->default_style->pixels_inside_wrap =
                text->_priv->pixels_inside_wrap;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_LEFT_MARGIN:
        text->_priv->left_margin = g_value_get_int (value);

        if (text->_priv->layout) {
            text->_priv->layout->default_style->left_margin =
                text->_priv->left_margin;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_RIGHT_MARGIN:
        text->_priv->right_margin = g_value_get_int (value);

        if (text->_priv->layout) {
            text->_priv->layout->default_style->right_margin =
                text->_priv->right_margin;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;
    case PROP_INDENT:
        text->_priv->pixels_above_lines = g_value_get_int (value);

        if (text->_priv->layout) {
            text->_priv->layout->default_style->indent = text->_priv->indent;
            gtk_text_layout_default_style_changed (text->_priv->layout);
        }
        break;

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

    gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text));
}

static void
gnome_canvas_rich_text_get_property (GObject *object, guint property_id,
                     GValue *value, GParamSpec *pspec)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (object);

    switch (property_id) {
    case PROP_TEXT:
        g_value_set_string (value, text->_priv->text);
        break;
    case PROP_X:
        g_value_set_double (value, text->_priv->x);
        break;
    case PROP_Y:
        g_value_set_double (value, text->_priv->y);
        break;
    case PROP_HEIGHT:
        g_value_set_double (value, text->_priv->height);
        break;
    case PROP_WIDTH:
        g_value_set_double (value, text->_priv->width);
        break;
    case PROP_EDITABLE:
        g_value_set_boolean (value, text->_priv->editable);
        break;
    case PROP_CURSOR_VISIBLE:
        g_value_set_boolean (value, text->_priv->cursor_visible);
        break;
    case PROP_CURSOR_BLINK:
        g_value_set_boolean (value, text->_priv->cursor_blink);
        break;
    case PROP_GROW_HEIGHT:
        g_value_set_boolean (value, text->_priv->grow_height);
        break;
    case PROP_WRAP_MODE:
        g_value_set_enum (value, text->_priv->wrap_mode);
        break;
    case PROP_JUSTIFICATION:
        g_value_set_enum (value, text->_priv->justification);
        break;
    case PROP_DIRECTION:
        g_value_set_enum (value, text->_priv->direction);
        break;
    case PROP_ANCHOR:
        g_value_set_enum (value, text->_priv->anchor);
        break;
    case PROP_PIXELS_ABOVE_LINES:
        g_value_set_enum (value, text->_priv->pixels_above_lines);
        break;
    case PROP_PIXELS_BELOW_LINES:
        g_value_set_int (value, text->_priv->pixels_below_lines);
        break;
    case PROP_PIXELS_INSIDE_WRAP:
        g_value_set_int (value, text->_priv->pixels_inside_wrap);
        break;
    case PROP_LEFT_MARGIN:
        g_value_set_int (value, text->_priv->left_margin);
        break;
    case PROP_RIGHT_MARGIN:
        g_value_set_int (value, text->_priv->right_margin);
        break;
    case PROP_INDENT:
        g_value_set_int (value, text->_priv->indent);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
        break;
    }
}

static void
gnome_canvas_rich_text_realize (GnomeCanvasItem *item)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);

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

    gnome_canvas_rich_text_ensure_layout (text);
} /* gnome_canvas_rich_text_realize */

static void
gnome_canvas_rich_text_unrealize (GnomeCanvasItem *item)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);

    gnome_canvas_rich_text_destroy_layout (text);

    (* GNOME_CANVAS_ITEM_CLASS (parent_class)->unrealize)(item);
} /* gnome_canvas_rich_text_unrealize */

static void
gnome_canvas_rich_text_move_iter_by_lines (GnomeCanvasRichText *text,
                      GtkTextIter *newplace, gint count)
{
    while (count < 0) {
        gtk_text_layout_move_iter_to_previous_line (
            text->_priv->layout, newplace);
        count++;
    }

    while (count > 0) {
        gtk_text_layout_move_iter_to_next_line (
            text->_priv->layout, newplace);
        count--;
    }
} /* gnome_canvas_rich_text_move_iter_by_lines */

static gint
gnome_canvas_rich_text_get_cursor_x_position (GnomeCanvasRichText *text)
{
    GtkTextIter insert;
    GdkRectangle rect;

    gtk_text_buffer_get_iter_at_mark (
        get_buffer (text), &insert,
        gtk_text_buffer_get_mark(get_buffer(text), "insert"));
    gtk_text_layout_get_cursor_locations (
        text->_priv->layout, &insert, &rect, NULL);

    return rect.x;
} /* gnome_canvas_rich_text_get_cursor_x_position */

static void
gnome_canvas_rich_text_move_cursor (GnomeCanvasRichText *text,
                   GtkMovementStep step,
                   gint count, gboolean extend_selection)
{
    GtkTextIter insert, newplace;

    gtk_text_buffer_get_iter_at_mark (
        get_buffer (text), &insert,
        gtk_text_buffer_get_mark(get_buffer(text), "insert"));

    newplace = insert;

    switch (step) {
    case GTK_MOVEMENT_LOGICAL_POSITIONS:
        gtk_text_iter_forward_cursor_positions (&newplace, count);
        break;
    case GTK_MOVEMENT_VISUAL_POSITIONS:
        gtk_text_layout_move_iter_visually (
            text->_priv->layout, &newplace, count);
        break;
    case GTK_MOVEMENT_WORDS:
        if (count < 0)
            gtk_text_iter_backward_word_starts (&newplace, -count);
        else if (count > 0)
            gtk_text_iter_forward_word_ends (&newplace, count);
        break;
    case GTK_MOVEMENT_DISPLAY_LINES:
        gnome_canvas_rich_text_move_iter_by_lines (
            text, &newplace, count);
        gtk_text_layout_move_iter_to_x (
            text->_priv->layout, &newplace,
            gnome_canvas_rich_text_get_cursor_x_position (text));
        break;
    case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
        if (count > 1) {
            gnome_canvas_rich_text_move_iter_by_lines (
                text, &newplace, --count);
        }
        else if (count < -1) {
            gnome_canvas_rich_text_move_iter_by_lines (
                text, &newplace, ++count);
        }

        if (count != 0) {
            gtk_text_layout_move_iter_to_line_end (
                text->_priv->layout, &newplace, count);
        }
        break;
    case GTK_MOVEMENT_PARAGRAPHS:
        /* FIXME: Busted in gtktextview.c too */
        break;
    case GTK_MOVEMENT_PARAGRAPH_ENDS:
        if (count > 0)
            gtk_text_iter_forward_to_line_end (&newplace);
        else if (count < 0)
            gtk_text_iter_set_line_offset (&newplace, 0);
        break;
    case GTK_MOVEMENT_BUFFER_ENDS:
        if (count > 0) {
            gtk_text_buffer_get_end_iter (
                get_buffer (text), &newplace);
        }
        else if (count < 0) {
            gtk_text_buffer_get_iter_at_offset (
                get_buffer (text), &newplace, 0);
        }
        break;
    default:
        break;
    }

    if (!gtk_text_iter_equal (&insert, &newplace)) {
        if (extend_selection) {
            gtk_text_buffer_move_mark (
                get_buffer (text),
                gtk_text_buffer_get_mark (
                    get_buffer(text), "insert"),
                &newplace);
        }
        else {
            gtk_text_buffer_place_cursor (
                get_buffer (text), &newplace);
        }
    }

    gnome_canvas_rich_text_start_cursor_blink (text, TRUE);
} /* gnome_canvas_rich_text_move_cursor */

static gboolean
whitespace (gunichar ch, gpointer user_data)
{
    return (ch == ' ' || ch == '\t');
} /* whitespace */

static gboolean
not_whitespace (gunichar ch, gpointer user_data)
{
    return !whitespace (ch, user_data);
} /* not_whitespace */

static gboolean
find_whitespace_region (const GtkTextIter *center,
               GtkTextIter *start, GtkTextIter *end)
{
    *start = *center;
    *end = *center;

    if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL, NULL))
        gtk_text_iter_forward_char (start);
    if (whitespace (gtk_text_iter_get_char (end), NULL))
        gtk_text_iter_forward_find_char (end, not_whitespace, NULL, NULL);

    return !gtk_text_iter_equal (start, end);
} /* find_whitespace_region */

static void
gnome_canvas_rich_text_delete_from_cursor (GnomeCanvasRichText *text,
                      GtkDeleteType type,
                      gint count)
{
    GtkTextIter insert, start, end;

    /* Special case: If the user wants to delete a character and there is
       a selection, then delete the selection and return */
    if (type == GTK_DELETE_CHARS) {
        if (gtk_text_buffer_delete_selection (get_buffer (text), TRUE,
                             text->_priv->editable))
            return;
    }

    gtk_text_buffer_get_iter_at_mark (
        get_buffer (text), &insert,
        gtk_text_buffer_get_mark(get_buffer(text), "insert"));

    start = insert;
    end = insert;

    switch (type) {
    case GTK_DELETE_CHARS:
        gtk_text_iter_forward_cursor_positions (&end, count);
        break;
    case GTK_DELETE_WORD_ENDS:
        if (count > 0)
            gtk_text_iter_forward_word_ends (&end, count);
        else if (count < 0)
            gtk_text_iter_backward_word_starts (&start, -count);
        break;
    case GTK_DELETE_WORDS:
        break;
    case GTK_DELETE_DISPLAY_LINE_ENDS:
        break;
    case GTK_DELETE_PARAGRAPH_ENDS:
        if (gtk_text_iter_ends_line (&end)) {
            gtk_text_iter_forward_line (&end);
            --count;
        }

        while (count > 0) {
            if (!gtk_text_iter_forward_to_line_end (&end))
                break;

            --count;
        }
        break;
    case GTK_DELETE_PARAGRAPHS:
        if (count > 0) {
            gtk_text_iter_set_line_offset (&start, 0);
            gtk_text_iter_forward_to_line_end (&end);

            /* Do the lines beyond the first. */
            while (count > 1) {
                gtk_text_iter_forward_to_line_end (&end);
                --count;
            }
        }
        break;
    case GTK_DELETE_WHITESPACE:
        find_whitespace_region (&insert, &start, &end);
        break;

    default:
        break;
    }

    if (!gtk_text_iter_equal (&start, &end)) {
        gtk_text_buffer_begin_user_action (get_buffer (text));
        gtk_text_buffer_delete_interactive (
            get_buffer (text), &start, &end, text->_priv->editable);
        gtk_text_buffer_end_user_action (get_buffer (text));
    }
} /* gnome_canvas_rich_text_delete_from_cursor */

static gint
selection_motion_event_handler (GnomeCanvasRichText *text, GdkEvent *event,
                   gpointer data)
{
    GtkTextIter newplace;
    GtkTextMark *mark;
    gdouble newx, newy;

    /* We only want to handle motion events... */
    if (event->type != GDK_MOTION_NOTIFY)
        return FALSE;

    newx = (event->motion.x - text->_priv->x) *
        GNOME_CANVAS_ITEM (text)->canvas->pixels_per_unit;
    newy = (event->motion.y - text->_priv->y) *
        GNOME_CANVAS_ITEM (text)->canvas->pixels_per_unit;

    gtk_text_layout_get_iter_at_pixel (text->_priv->layout, &newplace, newx, newy);
    mark = gtk_text_buffer_get_mark(get_buffer(text), "insert");
    gtk_text_buffer_move_mark (get_buffer (text), mark, &newplace);

    return TRUE;
} /* selection_motion_event_handler */

static void
gnome_canvas_rich_text_start_selection_drag (GnomeCanvasRichText *text,
                        const GtkTextIter *iter,
                        GdkEventButton *button)
{
    GtkTextIter newplace;

    g_return_if_fail (text->_priv->selection_drag_handler == 0);

#if 0
    gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (text));
#endif

    newplace = *iter;

    gtk_text_buffer_place_cursor (get_buffer (text), &newplace);

    text->_priv->selection_drag_handler = g_signal_connect (
        text, "event",
        G_CALLBACK (selection_motion_event_handler),
        NULL);
} /* gnome_canvas_rich_text_start_selection_drag */

static gboolean
gnome_canvas_rich_text_end_selection_drag (GnomeCanvasRichText *text,
                      GdkEventButton *event)
{
    if (text->_priv->selection_drag_handler == 0)
        return FALSE;

    g_signal_handler_disconnect (text, text->_priv->selection_drag_handler);
    text->_priv->selection_drag_handler = 0;

#if 0
    gnome_canvas_item_grab (NULL);
#endif

    return TRUE;
} /* gnome_canvas_rich_text_end_selection_drag */

static void
gnome_canvas_rich_text_emit_tag_changed (GnomeCanvasRichText *text,
                    GtkTextTag *tag)
{
    g_signal_emit (G_OBJECT (text), signals[TAG_CHANGED], 0, tag);
} /* gnome_canvas_rich_text_emit_tag_changed */

static gint
gnome_canvas_rich_text_key_press_event (GnomeCanvasItem *item,
                       GdkEventKey *event)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);
    gboolean extend_selection = FALSE;
    gboolean handled = FALSE;

#if 0
    printf("Key press event\n");
#endif

    if (!text->_priv->layout || !text->_priv->buffer)
        return FALSE;

    if (event->state & GDK_SHIFT_MASK)
        extend_selection = TRUE;

    switch (event->keyval) {
    case GDK_KEY_Return:
    case GDK_KEY_KP_Enter:
        gtk_text_buffer_delete_selection (
            get_buffer (text), TRUE, text->_priv->editable);
        gtk_text_buffer_insert_interactive_at_cursor (
            get_buffer(text), "\n", 1, text->_priv->editable);
        handled = TRUE;
        break;

    case GDK_KEY_Tab:
        gtk_text_buffer_insert_interactive_at_cursor (
            get_buffer(text), "\t", 1, text->_priv->editable);
        handled = TRUE;
        break;

    /* MOVEMENT */
    case GDK_KEY_Right:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_WORDS, 1,
                extend_selection);
            handled = TRUE;
        }
        else {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_VISUAL_POSITIONS, 1,
                extend_selection);
            handled = TRUE;
        }
        break;
    case GDK_KEY_Left:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_WORDS, -1,
                extend_selection);
            handled = TRUE;
        }
        else {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_VISUAL_POSITIONS, -1,
                extend_selection);
            handled = TRUE;
        }
        break;
    case GDK_KEY_f:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_LOGICAL_POSITIONS, 1,
                extend_selection);
            handled = TRUE;
        }
        else if (event->state & GDK_MOD1_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_WORDS, 1,
                extend_selection);
            handled = TRUE;
        }
        break;
    case GDK_KEY_b:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_LOGICAL_POSITIONS, -1,
                extend_selection);
            handled = TRUE;
        }
        else if (event->state & GDK_MOD1_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_WORDS, -1,
                extend_selection);
            handled = TRUE;
        }
        break;
    case GDK_KEY_Up:
        gnome_canvas_rich_text_move_cursor (
            text, GTK_MOVEMENT_DISPLAY_LINES, -1,
            extend_selection);
        handled = TRUE;
        break;
    case GDK_KEY_Down:
        gnome_canvas_rich_text_move_cursor (
            text, GTK_MOVEMENT_DISPLAY_LINES, 1,
            extend_selection);
        handled = TRUE;
        break;
    case GDK_KEY_p:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_DISPLAY_LINES, -1,
                extend_selection);
            handled = TRUE;
        }
        break;
    case GDK_KEY_n:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_DISPLAY_LINES, 1,
                extend_selection);
            handled = TRUE;
        }
        break;
    case GDK_KEY_Home:
        gnome_canvas_rich_text_move_cursor (
            text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
            extend_selection);
        handled = TRUE;
        break;
    case GDK_KEY_End:
        gnome_canvas_rich_text_move_cursor (
            text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
            extend_selection);
        handled = TRUE;
        break;
    case GDK_KEY_a:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
                extend_selection);
            handled = TRUE;
        }
        break;
    case GDK_KEY_e:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_move_cursor (
                text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
                extend_selection);
            handled = TRUE;
        }
        break;

    /* DELETING TEXT */
    case GDK_KEY_Delete:
    case GDK_KEY_KP_Delete:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_WORD_ENDS, 1);
            handled = TRUE;
        }
        else {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_CHARS, 1);
            handled = TRUE;
        }
        break;
    case GDK_KEY_d:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_CHARS, 1);
            handled = TRUE;
        }
        else if (event->state & GDK_MOD1_MASK) {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_WORD_ENDS, 1);
            handled = TRUE;
        }
        break;
    case GDK_KEY_BackSpace:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_WORD_ENDS, -1);
            handled = TRUE;
        }
        else {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_CHARS, -1);
        }
        handled = TRUE;
        break;
    case GDK_KEY_k:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_PARAGRAPH_ENDS, 1);
            handled = TRUE;
        }
        break;
    case GDK_KEY_u:
        if (event->state & GDK_CONTROL_MASK) {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_PARAGRAPHS, 1);
            handled = TRUE;
        }
        break;
    case GDK_KEY_space:
        if (event->state & GDK_MOD1_MASK) {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_WHITESPACE, 1);
            handled = TRUE;
        }
        break;
    case GDK_KEY_backslash:
        if (event->state & GDK_MOD1_MASK) {
            gnome_canvas_rich_text_delete_from_cursor (
                text, GTK_DELETE_WHITESPACE, 1);
            handled = TRUE;
        }
        break;
    default:
        break;
    }

    /* An empty string, click just pressing "Alt" by itself or whatever. */
    if (!event->length)
        return FALSE;

    if (!handled) {
        gtk_text_buffer_delete_selection (
            get_buffer (text), TRUE, text->_priv->editable);
        gtk_text_buffer_insert_interactive_at_cursor (
            get_buffer (text), event->string, event->length,
            text->_priv->editable);
    }

    gnome_canvas_rich_text_start_cursor_blink (text, TRUE);

    return TRUE;
} /* gnome_canvas_rich_text_key_press_event */

static gint
gnome_canvas_rich_text_key_release_event (GnomeCanvasItem *item,
                     GdkEventKey *event)
{
    return FALSE;
} /* gnome_canvas_rich_text_key_release_event */

static gboolean
_click (gpointer data)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (data);

    text->_priv->clicks = 0;
    text->_priv->click_timeout = 0;

    return FALSE;
} /* _click */

static gint
gnome_canvas_rich_text_button_press_event (GnomeCanvasItem *item,
                      GdkEventButton *event)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);
    GtkTextIter iter;
    GdkEventType event_type;
    gdouble newx, newy;

    newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
    newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;

    gtk_text_layout_get_iter_at_pixel (text->_priv->layout, &iter, newx, newy);

    /* The canvas doesn't give us double- or triple-click events, so
       we have to synthesize them ourselves. Yay. */
    event_type = event->type;
    if (event_type == GDK_BUTTON_PRESS) {
        text->_priv->clicks++;
        text->_priv->click_timeout = g_timeout_add (400, _click, text);

        if (text->_priv->clicks > 3)
            text->_priv->clicks = text->_priv->clicks % 3;

        if (text->_priv->clicks == 1)
            event_type = GDK_BUTTON_PRESS;
        else if (text->_priv->clicks == 2)
            event_type = GDK_2BUTTON_PRESS;
        else if (text->_priv->clicks == 3)
            event_type = GDK_3BUTTON_PRESS;
        else
            printf("ZERO CLICKS!\n");
    }

    if (event->button == 1 && event_type == GDK_BUTTON_PRESS) {
        GtkTextIter start, end;

        if (gtk_text_buffer_get_selection_bounds (
                get_buffer (text), &start, &end) &&
            gtk_text_iter_in_range (&iter, &start, &end)) {
            text->_priv->drag_start_x = event->x;
            text->_priv->drag_start_y = event->y;
        }
        else {
            gnome_canvas_rich_text_start_selection_drag (
                text, &iter, event);
        }

        return TRUE;
    }
    else if (event->button == 1 && event_type == GDK_2BUTTON_PRESS) {
        GtkTextIter start, end;

#if 0
        printf("double-click\n");
#endif

        gnome_canvas_rich_text_end_selection_drag (text, event);

        start = iter;
        end = start;

        if (gtk_text_iter_inside_word (&start)) {
            if (!gtk_text_iter_starts_word (&start))
                gtk_text_iter_backward_word_start (&start);

            if (!gtk_text_iter_ends_word (&end))
                gtk_text_iter_forward_word_end (&end);
        }

        gtk_text_buffer_move_mark (
            get_buffer (text),
            gtk_text_buffer_get_selection_bound (get_buffer (text)),
            &start);
        gtk_text_buffer_move_mark (
            get_buffer (text),
            gtk_text_buffer_get_insert (get_buffer (text)), &end);

        text->_priv->just_selected_element = TRUE;

        return TRUE;
    }
    else if (event->button == 1 && event_type == GDK_3BUTTON_PRESS) {
        GtkTextIter start, end;

#if 0
        printf("triple-click\n");
#endif

        gnome_canvas_rich_text_end_selection_drag (text, event);

        start = iter;
        end = start;

        if (gtk_text_layout_iter_starts_line (text->_priv->layout, &start)) {
            gtk_text_layout_move_iter_to_line_end (
                text->_priv->layout, &start, -1);
        }
        else {
            gtk_text_layout_move_iter_to_line_end (
                text->_priv->layout, &start, -1);

            if (!gtk_text_layout_iter_starts_line (
                    text->_priv->layout, &end)) {
                gtk_text_layout_move_iter_to_line_end (
                    text->_priv->layout, &end, 1);
            }
        }

        gtk_text_buffer_move_mark (
            get_buffer (text),
            gtk_text_buffer_get_selection_bound (get_buffer (text)),
            &start);
        gtk_text_buffer_move_mark (
            get_buffer (text),
            gtk_text_buffer_get_insert (get_buffer (text)), &end);

        text->_priv->just_selected_element = TRUE;

        return TRUE;
    }
    else if (event->button == 2 && event_type == GDK_BUTTON_PRESS) {
        gtk_text_buffer_paste_clipboard (
            get_buffer (text),
            gtk_clipboard_get (GDK_SELECTION_PRIMARY),
            &iter, text->_priv->editable);
    }

    return FALSE;
} /* gnome_canvas_rich_text_button_press_event */

static gint
gnome_canvas_rich_text_button_release_event (GnomeCanvasItem *item,
                        GdkEventButton *event)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);
    gdouble newx, newy;

    newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
    newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;

    if (event->button == 1) {
        if (text->_priv->drag_start_x >= 0) {
            text->_priv->drag_start_x = -1;
            text->_priv->drag_start_y = -1;
        }

        if (gnome_canvas_rich_text_end_selection_drag (text, event))
            return TRUE;
        else if (text->_priv->just_selected_element) {
            text->_priv->just_selected_element = FALSE;
            return FALSE;
        }
        else {
            GtkTextIter iter;

            gtk_text_layout_get_iter_at_pixel (
                text->_priv->layout, &iter, newx, newy);

            gtk_text_buffer_place_cursor (get_buffer (text), &iter);

            return FALSE;
        }
    }

    return FALSE;
} /* gnome_canvas_rich_text_button_release_event */

static gint
gnome_canvas_rich_text_focus_in_event (GnomeCanvasItem *item,
                      GdkEventFocus *event)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);

    if (text->_priv->cursor_visible && text->_priv->layout) {
        gtk_text_layout_set_cursor_visible (text->_priv->layout, TRUE);
        gnome_canvas_rich_text_start_cursor_blink (text, FALSE);
    }

    return FALSE;
} /* gnome_canvas_rich_text_focus_in_event */

static gint
gnome_canvas_rich_text_focus_out_event (GnomeCanvasItem *item,
                       GdkEventFocus *event)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);

    if (text->_priv->cursor_visible && text->_priv->layout) {
        gtk_text_layout_set_cursor_visible (text->_priv->layout, FALSE);
        gnome_canvas_rich_text_stop_cursor_blink (text);
    }

    return FALSE;
} /* gnome_canvas_rich_text_focus_out_event */

static gboolean
get_event_coordinates (GdkEvent *event, gint *x, gint *y)
{
    g_return_val_if_fail (event, FALSE);

    switch (event->type) {
    case GDK_MOTION_NOTIFY:
        *x = event->motion.x;
        *y = event->motion.y;
        return TRUE;
    case GDK_BUTTON_PRESS:
    case GDK_2BUTTON_PRESS:
    case GDK_3BUTTON_PRESS:
    case GDK_BUTTON_RELEASE:
        *x = event->button.x;
        *y = event->button.y;
        return TRUE;

    default:
        return FALSE;
    }
} /* get_event_coordinates */

static void
emit_event_on_tags (GnomeCanvasRichText *text, GdkEvent *event,
           GtkTextIter *iter)
{
    GSList *tags;
    GSList *i;

    tags = gtk_text_iter_get_tags (iter);

    i = tags;
    while (i) {
        GtkTextTag *tag = i->data;

        gtk_text_tag_event (tag, G_OBJECT (text), event, iter);

        /* The cursor has been moved to within this tag. Emit the
           tag_changed signal */
        if (event->type == GDK_BUTTON_RELEASE ||
            event->type == GDK_KEY_PRESS ||
            event->type == GDK_KEY_RELEASE) {
            gnome_canvas_rich_text_emit_tag_changed (
                text, tag);
        }

        i = g_slist_next (i);
    }

    g_slist_free (tags);
} /* emit_event_on_tags */

static gint
gnome_canvas_rich_text_event (GnomeCanvasItem *item, GdkEvent *event)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);
    gint x, y;

    if (get_event_coordinates (event, &x, &y)) {
        GtkTextIter iter;

        x -= text->_priv->x;
        y -= text->_priv->y;

        gtk_text_layout_get_iter_at_pixel (text->_priv->layout, &iter, x, y);
        emit_event_on_tags (text, event, &iter);
    }
    else if (event->type == GDK_KEY_PRESS ||
         event->type == GDK_KEY_RELEASE) {
        GtkTextMark *insert;
        GtkTextIter iter;

        insert = gtk_text_buffer_get_mark(get_buffer(text), "insert");
        gtk_text_buffer_get_iter_at_mark (
            get_buffer (text), &iter, insert);
        emit_event_on_tags (text, event, &iter);
    }

    switch (event->type) {
    case GDK_KEY_PRESS:
        return gnome_canvas_rich_text_key_press_event (
            item, (GdkEventKey *) event);
    case GDK_KEY_RELEASE:
        return gnome_canvas_rich_text_key_release_event (
            item, (GdkEventKey *) event);
    case GDK_BUTTON_PRESS:
        return gnome_canvas_rich_text_button_press_event (
            item, (GdkEventButton *) event);
    case GDK_BUTTON_RELEASE:
        return gnome_canvas_rich_text_button_release_event (
            item, (GdkEventButton *) event);
    case GDK_FOCUS_CHANGE:
    {
        GtkLayout *layout;
        GdkWindow *bin_window;

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

        if (((GdkEventFocus *) event)->window != bin_window)
            return FALSE;

        if (((GdkEventFocus *) event)->in)
            return gnome_canvas_rich_text_focus_in_event (
                item, (GdkEventFocus *) event);
        else
            return gnome_canvas_rich_text_focus_out_event (
                item, (GdkEventFocus *) event);
    }
    default:
        return FALSE;
    }
} /* gnome_canvas_rich_text_event */

/* Cut/Copy/Paste */

/**
 * gnome_canvas_rich_text_cut_clipboard:
 * @text: a #GnomeCanvasRichText.
 *
 * Copies the currently selected @text to clipboard, then deletes said text
 * if it's editable.
 **/
void
gnome_canvas_rich_text_cut_clipboard (GnomeCanvasRichText *text)
{
    g_return_if_fail (text);
    g_return_if_fail (get_buffer (text));

    gtk_text_buffer_cut_clipboard (get_buffer (text),
                      gtk_clipboard_get (GDK_SELECTION_PRIMARY),
                      text->_priv->editable);
} /* gnome_canvas_rich_text_cut_clipboard */

/**
 * gnome_canvas_rich_text_copy_clipboard:
 * @text: a #GnomeCanvasRichText.
 *
 * Copies the currently selected @text to clipboard.
 **/
void
gnome_canvas_rich_text_copy_clipboard (GnomeCanvasRichText *text)
{
    g_return_if_fail (text);
    g_return_if_fail (get_buffer (text));

    gtk_text_buffer_copy_clipboard (get_buffer (text),
                       gtk_clipboard_get (GDK_SELECTION_PRIMARY));
} /* gnome_canvas_rich_text_cut_clipboard */

/**
 * gnome_canvas_rich_text_paste_clipboard:
 * @text: a #GnomeCanvasRichText.
 *
 * Pastes the contents of the clipboard at the insertion point.
 **/
void
gnome_canvas_rich_text_paste_clipboard (GnomeCanvasRichText *text)
{
    g_return_if_fail (text);
    g_return_if_fail (get_buffer (text));

    gtk_text_buffer_paste_clipboard (get_buffer (text),
                    gtk_clipboard_get (GDK_SELECTION_PRIMARY),
                    NULL,
                    text->_priv->editable);
} /* gnome_canvas_rich_text_cut_clipboard */

static gint
preblink_cb (gpointer data)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (data);

    text->_priv->preblink_timeout = 0;
    gnome_canvas_rich_text_start_cursor_blink (text, FALSE);

    /* Remove ourselves */
    return FALSE;
} /* preblink_cb */

static gint
blink_cb (gpointer data)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (data);
    gboolean visible;

    g_assert (text->_priv->layout);
    g_assert (text->_priv->cursor_visible);

    visible = gtk_text_layout_get_cursor_visible (text->_priv->layout);
    if (visible)
        text->_priv->blink_timeout = g_timeout_add (
            CURSOR_OFF_TIME, blink_cb, text);
    else
        text->_priv->blink_timeout = g_timeout_add (
            CURSOR_ON_TIME, blink_cb, text);

    gtk_text_layout_set_cursor_visible (text->_priv->layout, !visible);

    /* Remove ourself */
    return FALSE;
} /* blink_cb */

static void
gnome_canvas_rich_text_start_cursor_blink (GnomeCanvasRichText *text,
                      gboolean with_delay)
{
    if (!text->_priv->layout)
        return;

    if (!text->_priv->cursor_visible || !text->_priv->cursor_blink)
        return;

    if (text->_priv->preblink_timeout != 0) {
        g_source_remove (text->_priv->preblink_timeout);
        text->_priv->preblink_timeout = 0;
    }

    if (with_delay) {
        if (text->_priv->blink_timeout != 0) {
            g_source_remove (text->_priv->blink_timeout);
            text->_priv->blink_timeout = 0;
        }

        gtk_text_layout_set_cursor_visible (text->_priv->layout, TRUE);

        text->_priv->preblink_timeout = g_timeout_add (
            PREBLINK_TIME, preblink_cb, text);
    }
    else {
        if (text->_priv->blink_timeout == 0) {
            gtk_text_layout_set_cursor_visible (text->_priv->layout, TRUE);
            text->_priv->blink_timeout = g_timeout_add (
                CURSOR_ON_TIME, blink_cb, text);
        }
    }
} /* gnome_canvas_rich_text_start_cursor_blink */

static void
gnome_canvas_rich_text_stop_cursor_blink (GnomeCanvasRichText *text)
{
    if (text->_priv->blink_timeout) {
        g_source_remove (text->_priv->blink_timeout);
        text->_priv->blink_timeout = 0;
    }
} /* gnome_canvas_rich_text_stop_cursor_blink */

/* We have to request updates this way because the update cycle is not
   re-entrant. This will fire off a request in an idle loop. */
static gboolean
request_update (gpointer data)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (data);

    gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text));

    return FALSE;
} /* request_update */

static void
invalidated_handler (GtkTextLayout *layout, gpointer data)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (data);

#if 0
    printf("Text is being invalidated.\n");
#endif

    gtk_text_layout_validate (text->_priv->layout, 2000);

    /* We are called from the update cycle; gotta put this in an idle
       loop. */
    g_idle_add (request_update, text);
} /* invalidated_handler */

static void
scale_fonts (GtkTextTag *tag, gpointer data)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (data);

    /* XXX GtkTextTag::values is sealed with apparently no way
     *     to access it.  This looks like a small optimization
     *     anyway. */
#if 0
    if (!tag->values)
        return;
#endif

    g_object_set (
        G_OBJECT(tag), "scale",
        text->_priv->layout->default_style->font_scale, NULL);
} /* scale_fonts */

static void
changed_handler (GtkTextLayout *layout, gint start_y,
        gint old_height, gint new_height, gpointer data)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (data);

#if 0
    printf("Layout %p is being changed.\n", text->_priv->layout);
#endif

    if (text->_priv->layout->default_style->font_scale !=
        GNOME_CANVAS_ITEM (text)->canvas->pixels_per_unit) {
        GtkTextTagTable *tag_table;

        text->_priv->layout->default_style->font_scale =
            GNOME_CANVAS_ITEM (text)->canvas->pixels_per_unit;

        tag_table = gtk_text_buffer_get_tag_table (get_buffer (text));
        gtk_text_tag_table_foreach (tag_table, scale_fonts, text);

        gtk_text_layout_default_style_changed (text->_priv->layout);
    }

    if (text->_priv->grow_height) {
        gint width, height;

        gtk_text_layout_get_size (text->_priv->layout, &width, &height);

        if (height > text->_priv->height)
            text->_priv->height = height;
    }

    /* We are called from the update cycle; gotta put this in an idle
       loop. */
    g_idle_add (request_update, text);
} /* changed_handler */

/**
 * gnome_canvas_rich_text_set_buffer:
 * @text: a #GnomeCanvasRichText.
 * @buffer: a #GtkTextBuffer.
 *
 * Sets the buffer field of the @text to @buffer.
 **/
void
gnome_canvas_rich_text_set_buffer (GnomeCanvasRichText *text,
                  GtkTextBuffer *buffer)
{
    g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
    g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));

    if (text->_priv->buffer == buffer)
        return;

    if (text->_priv->buffer != NULL) {
        g_object_unref (G_OBJECT (text->_priv->buffer));
    }

    text->_priv->buffer = buffer;

    if (buffer) {
        g_object_ref (G_OBJECT (buffer));

        if (text->_priv->layout)
            gtk_text_layout_set_buffer (text->_priv->layout, buffer);
    }

    gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text));
} /* gnome_canvas_rich_text_set_buffer */

static GtkTextBuffer *
get_buffer (GnomeCanvasRichText *text)
{
    if (!text->_priv->buffer) {
        GtkTextBuffer *b;

        b = gtk_text_buffer_new (NULL);
        gnome_canvas_rich_text_set_buffer (text, b);
        g_object_unref (G_OBJECT (b));
    }

    return text->_priv->buffer;
} /* get_buffer */

/**
 * gnome_canvas_rich_text_get_buffer:
 * @text: a #GnomeCanvasRichText.
 *
 * Returns a #GtkTextBuffer associated with the #GnomeCanvasRichText.
 * This function creates a new #GtkTextBuffer if the text buffer is NULL.
 *
 * Return value: the #GtkTextBuffer.
 **/
GtkTextBuffer *
gnome_canvas_rich_text_get_buffer (GnomeCanvasRichText *text)
{
    g_return_val_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text), NULL);

    return get_buffer (text);
} /* gnome_canvas_rich_text_get_buffer */

/**
 * gnome_canvas_rich_text_get_iter_location:
 * @text: a #GnomeCanvasRichText.
 * @iter: a #GtkTextIter.
 * @location: a #GdkRectangle containing the bounds of the character at @iter.
 *
 * Gets a rectangle which roughly contains the character at @iter.
 **/
void
gnome_canvas_rich_text_get_iter_location (GnomeCanvasRichText *text,
                      const GtkTextIter *iter,
                      GdkRectangle      *location)
{
  g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
  g_return_if_fail (gtk_text_iter_get_buffer (iter) == text->_priv->buffer);

  gtk_text_layout_get_iter_location (text->_priv->layout, iter, location);
}

/**
 * gnome_canvas_rich_text_get_iter_at_location:
 * @text: a #GnomeCanvasRichText.
 * @iter: a #GtkTextIter.
 * @x: x position, in buffer coordinates.
 * @y: y position, in buffer coordinates.
 *
 * Retrieves the iterator at the buffer coordinates x and y.
 **/
void
gnome_canvas_rich_text_get_iter_at_location (GnomeCanvasRichText *text,
                                    GtkTextIter *iter,
                                    gint         x,
                                    gint         y)
{
  g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text->_priv->layout != NULL);

  gtk_text_layout_get_iter_at_pixel (text->_priv->layout,
                                     iter,
                                     x,
                                     y);
}

static void
gnome_canvas_rich_text_set_attributes_from_style (GnomeCanvasRichText *text,
                         GtkTextAttributes *values,
                         GtkStyle *style)
{
    values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
    values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];

    if (values->font)
        pango_font_description_free (values->font);

    values->font = pango_font_description_copy (style->font_desc);

} /* gnome_canvas_rich_text_set_attributes_from_style */

static void
gnome_canvas_rich_text_ensure_layout (GnomeCanvasRichText *text)
{
    if (!text->_priv->layout) {
        GtkWidget *canvas;
        GtkTextAttributes *style;
        PangoContext *ltr_context, *rtl_context;

        text->_priv->layout = gtk_text_layout_new ();

        gtk_text_layout_set_screen_width (text->_priv->layout, text->_priv->width);

        if (get_buffer (text)) {
            gtk_text_layout_set_buffer (
                text->_priv->layout, get_buffer (text));
        }

        /* Setup the cursor stuff */
        gtk_text_layout_set_cursor_visible (
            text->_priv->layout, text->_priv->cursor_visible);
        if (text->_priv->cursor_visible && text->_priv->cursor_blink)
            gnome_canvas_rich_text_start_cursor_blink (text, FALSE);
        else
            gnome_canvas_rich_text_stop_cursor_blink (text);

        canvas = GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas);

        ltr_context = gtk_widget_create_pango_context (canvas);
        pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
        rtl_context = gtk_widget_create_pango_context (canvas);
        pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);

        gtk_text_layout_set_contexts (
            text->_priv->layout, ltr_context, rtl_context);

        g_object_unref (G_OBJECT (ltr_context));
        g_object_unref (G_OBJECT (rtl_context));

        style = gtk_text_attributes_new ();

        gnome_canvas_rich_text_set_attributes_from_style (
            text, style, gtk_widget_get_style (canvas));

        style->pixels_above_lines = text->_priv->pixels_above_lines;
        style->pixels_below_lines = text->_priv->pixels_below_lines;
        style->pixels_inside_wrap = text->_priv->pixels_inside_wrap;
        style->left_margin = text->_priv->left_margin;
        style->right_margin = text->_priv->right_margin;
        style->indent = text->_priv->indent;
        style->tabs = NULL;
        style->wrap_mode = text->_priv->wrap_mode;
        style->justification = text->_priv->justification;
        style->direction = text->_priv->direction;
        style->editable = text->_priv->editable;
        style->invisible = !text->_priv->visible;

        gtk_text_layout_set_default_style (text->_priv->layout, style);

        gtk_text_attributes_unref (style);

        g_signal_connect (
            G_OBJECT(text->_priv->layout), "invalidated",
            G_CALLBACK (invalidated_handler), text);

        g_signal_connect (
            G_OBJECT(text->_priv->layout), "changed",
            G_CALLBACK (changed_handler), text);
    }
} /* gnome_canvas_rich_text_ensure_layout */

static void
gnome_canvas_rich_text_destroy_layout (GnomeCanvasRichText *text)
{
    if (text->_priv->layout) {
        g_signal_handlers_disconnect_by_func (
            G_OBJECT (text->_priv->layout), invalidated_handler, text);
        g_signal_handlers_disconnect_by_func (
            G_OBJECT (text->_priv->layout), changed_handler, text);
        g_object_unref (G_OBJECT (text->_priv->layout));
        text->_priv->layout = NULL;
    }
} /* gnome_canvas_rich_text_destroy_layout */

static void
adjust_for_anchors (GnomeCanvasRichText *text, gdouble *ax, gdouble *ay)
{
    gdouble x, y;

    x = text->_priv->x;
    y = text->_priv->y;

    /* Anchor text */
    /* X coordinates */
    switch (text->_priv->anchor) {
    case GTK_ANCHOR_NW:
    case GTK_ANCHOR_W:
    case GTK_ANCHOR_SW:
        break;

    case GTK_ANCHOR_N:
    case GTK_ANCHOR_CENTER:
    case GTK_ANCHOR_S:
        x -= text->_priv->width / 2;
        break;

    case GTK_ANCHOR_NE:
    case GTK_ANCHOR_E:
    case GTK_ANCHOR_SE:
        x -= text->_priv->width;
        break;
    default:
        break;
    }

    /* Y coordinates */
    switch (text->_priv->anchor) {
    case GTK_ANCHOR_NW:
    case GTK_ANCHOR_N:
    case GTK_ANCHOR_NE:
        break;

    case GTK_ANCHOR_W:
    case GTK_ANCHOR_CENTER:
    case GTK_ANCHOR_E:
        y -= text->_priv->height / 2;
        break;

    case GTK_ANCHOR_SW:
    case GTK_ANCHOR_S:
    case GTK_ANCHOR_SE:
        y -= text->_priv->height;
        break;
    default:
        break;
    }

    if (ax)
        *ax = x;
    if (ay)
        *ay = y;
} /* adjust_for_anchors */

static void
get_bounds (GnomeCanvasRichText *text, gdouble *px1, gdouble *py1,
       gdouble *px2, gdouble *py2)
{
    GnomeCanvasItem *item = GNOME_CANVAS_ITEM (text);
    gdouble x, y;
    gdouble x1, x2, y1, y2;
    gint cx1, cx2, cy1, cy2;

    adjust_for_anchors (text, &x, &y);

    x1 = x;
    y1 = y;
    x2 = x + text->_priv->width;
    y2 = y + text->_priv->height;

    gnome_canvas_item_i2w (item, &x1, &y1);
    gnome_canvas_item_i2w (item, &x2, &y2);
    gnome_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
    gnome_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);

    *px1 = cx1;
    *py1 = cy1;
    *px2 = cx2;
    *py2 = cy2;
} /* get_bounds */

static void
gnome_canvas_rich_text_get_bounds (GnomeCanvasItem *item,
                                   gdouble *px1,
                                   gdouble *py1,
                                   gdouble *px2,
                                   gdouble *py2)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);
    get_bounds (text, px1, py1, px2, py2);
}

static void
gnome_canvas_rich_text_update (GnomeCanvasItem *item, gdouble *affine,
                  ArtSVP *clip_path, gint flags)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);
    gdouble x1, y1, x2, y2;
    GtkTextIter start;

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

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

    gtk_text_buffer_get_iter_at_offset (text->_priv->buffer, &start, 0);
    if (text->_priv->layout)
        gtk_text_layout_validate_yrange (
            text->_priv->layout, &start, 0, y2 - y1);

    gnome_canvas_update_bbox (item, x1, y1, x2, y2);
} /* gnome_canvas_rich_text_update */

static double
gnome_canvas_rich_text_point (GnomeCanvasItem *item, gdouble x, gdouble y,
                 gint cx, gint cy, GnomeCanvasItem **actual_item)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);
    gdouble ax, ay;
    gdouble x1, x2, y1, y2;
    gdouble dx, dy;

    *actual_item = item;

    /* This is a lame cop-out. Anywhere inside of the bounding box. */

    adjust_for_anchors (text, &ax, &ay);

    x1 = ax;
    y1 = ay;
    x2 = ax + text->_priv->width;
    y2 = ay + text->_priv->height;

    if ((x > x1) && (y > y1) && (x < x2) && (y < y2))
        return 0.0;

    if (x < x1)
        dx = x1 - x;
    else if (x > x2)
        dx = x - x2;
    else
        dx = 0.0;

    if (y < y1)
        dy = y1 - y;
    else if (y > y2)
        dy = y - y2;
    else
        dy = 0.0;

    return sqrt (dx * dx + dy * dy);
} /* gnome_canvas_rich_text_point */

static void
gnome_canvas_rich_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
                gint x, gint y, gint width, gint height)
{
    GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT (item);
    GtkStyle *style;
    GtkWidget *widget;
    gdouble i2w[6], w2c[6], i2c[6];
    gdouble ax, ay;
    gint x1, y1, x2, y2;
    ArtPoint i1, i2;
    ArtPoint c1, c2;

    gnome_canvas_item_i2w_affine (item, i2w);
    gnome_canvas_w2c_affine (item->canvas, w2c);
    art_affine_multiply (i2c, i2w, w2c);

    adjust_for_anchors (text, &ax, &ay);

    i1.x = ax;
    i1.y = ay;
    i2.x = ax + text->_priv->width;
    i2.y = ay + text->_priv->height;
    art_affine_point (&c1, &i1, i2c);
    art_affine_point (&c2, &i2, i2c);

    x1 = c1.x;
    y1 = c1.y;
    x2 = c2.x;
    y2 = c2.y;

    gtk_text_layout_set_screen_width (text->_priv->layout, x2 - x1);

    widget = GTK_WIDGET (item->canvas);
    style = gtk_widget_get_style (widget);

        /* FIXME: should last arg be NULL? */
    gtk_text_layout_draw (
        text->_priv->layout,
        widget,
        drawable,
        style->text_gc[GTK_STATE_NORMAL],
        x - x1, y - y1,
        0, 0, (x2 - x1) - (x - x1), (y2 - y1) - (y - y1),
        NULL);
} /* gnome_canvas_rich_text_draw */

static void
gnome_canvas_rich_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
{
    g_warning ("rich text item not implemented for anti-aliased canvas");
} /* gnome_canvas_rich_text_render */

#if 0
static GtkTextTag *
gnome_canvas_rich_text_add_tag (GnomeCanvasRichText *text, gchar *tag_name,
                   gint start_offset, gint end_offset,
                   const gchar *first_property_name, ...)
{
    GtkTextTag *tag;
    GtkTextIter start, end;
    va_list var_args;

    g_return_val_if_fail (text, NULL);
    g_return_val_if_fail (start_offset >= 0, NULL);
    g_return_val_if_fail (end_offset >= 0, NULL);

    if (tag_name) {
        GtkTextTagTable *tag_table;

        tag_table = gtk_text_buffer_get_tag_table (get_buffer (text));
        g_return_val_if_fail (gtk_text_tag_table_lookup (tag_table, tag_name) == NULL, NULL);
    }

    tag = gtk_text_buffer_create_tag (
        get_buffer (text), tag_name, NULL);

    va_start (var_args, first_property_name);
    g_object_set_valist (G_OBJECT (tag), first_property_name, var_args);
    va_end (var_args);

    gtk_text_buffer_get_iter_at_offset (
        get_buffer (text), &start, start_offset);
    gtk_text_buffer_get_iter_at_offset (
        get_buffer (text), &end, end_offset);
    gtk_text_buffer_apply_tag (get_buffer (text), tag, &start, &end);

    return tag;
} /* gnome_canvas_rich_text_add_tag */
#endif