cf7c5290e088f60d97b31c133cfe8305aba3792e
[ardour.git] / libs / gnomecanvas / libgnomecanvas / gnome-canvas-rich-text.c
1 /* Editable GnomeCanvas text item based on GtkTextLayout, borrowed heavily
2  * from GtkTextView.
3  *
4  * Copyright (c) 2000 Red Hat, Inc.
5  * Copyright (c) 2001 Joe Shaw
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <math.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtk.h>
29 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
30 #include <gtk/gtktextdisplay.h>
31
32 #include "gnome-canvas.h"
33 #include "gnome-canvas-util.h"
34 #include "gnome-canvas-rich-text.h"
35 #include "gnome-canvas-i18n.h"
36
37 struct _GnomeCanvasRichTextPrivate {
38         GtkTextLayout *layout;
39         GtkTextBuffer *buffer;
40
41         char *text;
42
43         /* Position at anchor */
44         double x, y;
45         /* Dimensions */
46         double width, height;
47         /* Top-left canvas coordinates for text */
48         int cx, cy;
49
50         gboolean cursor_visible;
51         gboolean cursor_blink;
52         gboolean editable;
53         gboolean visible;
54         gboolean grow_height;
55         GtkWrapMode wrap_mode;
56         GtkJustification justification;
57         GtkTextDirection direction;
58         GtkAnchorType anchor;
59         int pixels_above_lines;
60         int pixels_below_lines;
61         int pixels_inside_wrap;
62         int left_margin;
63         int right_margin;
64         int indent;
65
66         guint preblink_timeout;
67         guint blink_timeout;
68
69         guint selection_drag_handler;
70
71         gint drag_start_x;
72         gint drag_start_y;
73
74         gboolean just_selected_element;
75
76         int clicks;
77         guint click_timeout;
78 };
79
80 enum {
81         PROP_0,
82         PROP_TEXT,
83         PROP_X,
84         PROP_Y,
85         PROP_WIDTH,
86         PROP_HEIGHT,
87         PROP_EDITABLE,
88         PROP_VISIBLE,
89         PROP_CURSOR_VISIBLE,
90         PROP_CURSOR_BLINK,
91         PROP_GROW_HEIGHT,
92         PROP_WRAP_MODE,
93         PROP_JUSTIFICATION,
94         PROP_DIRECTION,
95         PROP_ANCHOR,
96         PROP_PIXELS_ABOVE_LINES,
97         PROP_PIXELS_BELOW_LINES,
98         PROP_PIXELS_INSIDE_WRAP,
99         PROP_LEFT_MARGIN,
100         PROP_RIGHT_MARGIN,
101         PROP_INDENT
102 };
103
104 enum {
105         TAG_CHANGED,
106         LAST_SIGNAL
107 };
108
109 static GnomeCanvasItemClass *parent_class;
110 static guint signals[LAST_SIGNAL] = { 0 };
111
112 static void gnome_canvas_rich_text_class_init(GnomeCanvasRichTextClass *klass);
113 static void gnome_canvas_rich_text_init(GnomeCanvasRichText *text);
114 static void gnome_canvas_rich_text_set_property(GObject *object, guint property_id,
115                                                 const GValue *value, GParamSpec *pspec);
116 static void gnome_canvas_rich_text_get_property(GObject *object, guint property_id,
117                                                 GValue *value, GParamSpec *pspec);
118 static void gnome_canvas_rich_text_update(GnomeCanvasItem *item, double *affine,
119                                           ArtSVP *clip_path, int flags);
120 static void gnome_canvas_rich_text_realize(GnomeCanvasItem *item);
121 static void gnome_canvas_rich_text_unrealize(GnomeCanvasItem *item);
122 static double gnome_canvas_rich_text_point(GnomeCanvasItem *item, 
123                                            double x, double y,
124                                            int cx, int cy, 
125                                            GnomeCanvasItem **actual_item);
126 static void gnome_canvas_rich_text_draw(GnomeCanvasItem *item, 
127                                         GdkDrawable *drawable,
128                                         int x, int y, int width, int height);
129 static void gnome_canvas_rich_text_render(GnomeCanvasItem *item,
130                                           GnomeCanvasBuf *buf);
131 static gint gnome_canvas_rich_text_event(GnomeCanvasItem *item, 
132                                          GdkEvent *event);
133 static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *text, double *px1, double *py1,
134            double *px2, double *py2);
135
136 static void gnome_canvas_rich_text_ensure_layout(GnomeCanvasRichText *text);
137 static void gnome_canvas_rich_text_destroy_layout(GnomeCanvasRichText *text);
138 static void gnome_canvas_rich_text_start_cursor_blink(GnomeCanvasRichText *text, gboolean delay);
139 static void gnome_canvas_rich_text_stop_cursor_blink(GnomeCanvasRichText *text);
140 static void gnome_canvas_rich_text_move_cursor(GnomeCanvasRichText *text,
141                                                GtkMovementStep step,
142                                                gint count,
143                                                gboolean extend_selection);
144
145
146
147 static GtkTextBuffer *get_buffer(GnomeCanvasRichText *text);
148 static gint blink_cb(gpointer data);
149
150 #define PREBLINK_TIME 300
151 #define CURSOR_ON_TIME 800
152 #define CURSOR_OFF_TIME 400
153
154 GType
155 gnome_canvas_rich_text_get_type(void)
156 {
157         static GType rich_text_type;
158
159         if (!rich_text_type) {
160                 const GTypeInfo object_info = {
161                         sizeof (GnomeCanvasRichTextClass),
162                         (GBaseInitFunc) NULL,
163                         (GBaseFinalizeFunc) NULL,
164                         (GClassInitFunc) gnome_canvas_rich_text_class_init,
165                         (GClassFinalizeFunc) NULL,
166                         NULL,                   /* class_data */
167                         sizeof (GnomeCanvasRichText),
168                         0,                      /* n_preallocs */
169                         (GInstanceInitFunc) gnome_canvas_rich_text_init,
170                         NULL                    /* value_table */
171                 };
172
173                 rich_text_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasRichText",
174                                                          &object_info, 0);
175         }
176
177         return rich_text_type;
178 }
179
180 static void
181 gnome_canvas_rich_text_finalize(GObject *object)
182 {
183         GnomeCanvasRichText *text;
184
185         text = GNOME_CANVAS_RICH_TEXT(object);
186
187         g_free (text->_priv);
188         text->_priv = NULL;
189
190         if (G_OBJECT_CLASS (parent_class)->finalize)
191                 G_OBJECT_CLASS (parent_class)->finalize (object);
192 }
193
194 static void
195 gnome_canvas_rich_text_class_init(GnomeCanvasRichTextClass *klass)
196 {
197         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
198         GtkObjectClass *object_class = GTK_OBJECT_CLASS(klass);
199         GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS(klass);
200         
201         parent_class = g_type_class_peek_parent (klass);
202
203         gobject_class->set_property = gnome_canvas_rich_text_set_property;
204         gobject_class->get_property = gnome_canvas_rich_text_get_property;
205
206         g_object_class_install_property (
207                 gobject_class,
208                 PROP_TEXT,
209                 g_param_spec_string ("text",
210                                      _("Text"),
211                                      _("Text to display"),
212                                      NULL,
213                                      G_PARAM_READWRITE));
214         g_object_class_install_property (
215                 gobject_class,
216                 PROP_X,
217                 g_param_spec_double ("x",
218                                      _("X"),
219                                      _("X position"),
220                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
221                                      G_PARAM_READWRITE));
222         g_object_class_install_property (
223                 gobject_class,
224                 PROP_Y,
225                 g_param_spec_double ("y",
226                                      _("Y"),
227                                      _("Y position"),
228                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
229                                      G_PARAM_READWRITE));
230         g_object_class_install_property (
231                 gobject_class,
232                 PROP_WIDTH,
233                 g_param_spec_double ("width",
234                                      _("Width"),
235                                      _("Width for text box"),
236                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
237                                      G_PARAM_READWRITE));
238         g_object_class_install_property (
239                 gobject_class,
240                 PROP_HEIGHT,
241                 g_param_spec_double ("height",
242                                      _("Height"),
243                                      _("Height for text box"),
244                                      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
245                                      G_PARAM_READWRITE));
246         g_object_class_install_property (
247                 gobject_class,
248                 PROP_EDITABLE,
249                 g_param_spec_boolean ("editable",
250                                       _("Editable"),
251                                       _("Is this rich text item editable?"),
252                                       TRUE,
253                                       G_PARAM_READWRITE));
254         g_object_class_install_property (
255                 gobject_class,
256                 PROP_VISIBLE,
257                 g_param_spec_boolean ("visible",
258                                       _("Visible"),
259                                       _("Is this rich text item visible?"),
260                                       TRUE,
261                                       G_PARAM_READWRITE));
262         g_object_class_install_property (
263                 gobject_class,
264                 PROP_CURSOR_VISIBLE,
265                 g_param_spec_boolean ("cursor_visible",
266                                       _("Cursor Visible"),
267                                       _("Is the cursor visible in this rich text item?"),
268                                       TRUE,
269                                       G_PARAM_READWRITE));
270         g_object_class_install_property (
271                 gobject_class,
272                 PROP_CURSOR_BLINK,
273                 g_param_spec_boolean ("cursor_blink",
274                                       _("Cursor Blink"),
275                                       _("Does the cursor blink in this rich text item?"),
276                                       TRUE,
277                                       G_PARAM_READWRITE));
278         g_object_class_install_property (
279                 gobject_class,
280                 PROP_GROW_HEIGHT,
281                 g_param_spec_boolean ("grow_height",
282                                       _("Grow Height"),
283                                       _("Should the text box height grow if the text does not fit?"),
284                                       FALSE,
285                                       G_PARAM_READWRITE));
286         g_object_class_install_property (
287                 gobject_class,
288                 PROP_WRAP_MODE,
289                 g_param_spec_enum ("wrap_mode",
290                                    _("Wrap Mode"),
291                                    _("Wrap mode for multiline text"),
292                                    GTK_TYPE_WRAP_MODE,
293                                    GTK_WRAP_WORD,
294                                    G_PARAM_READWRITE));
295         g_object_class_install_property (
296                 gobject_class,
297                 PROP_JUSTIFICATION,
298                 g_param_spec_enum ("justification",
299                                    _("Justification"),
300                                    _("Justification mode"),
301                                    GTK_TYPE_JUSTIFICATION,
302                                    GTK_JUSTIFY_LEFT,
303                                    G_PARAM_READWRITE));
304         g_object_class_install_property (
305                 gobject_class,
306                 PROP_DIRECTION,
307                 g_param_spec_enum ("direction",
308                                    _("Direction"),
309                                    _("Text direction"),
310                                    GTK_TYPE_DIRECTION_TYPE,
311                                    gtk_widget_get_default_direction (),
312                                    G_PARAM_READWRITE));
313         g_object_class_install_property (
314                 gobject_class,
315                 PROP_ANCHOR,
316                 g_param_spec_enum ("anchor",
317                                    _("Anchor"),
318                                    _("Anchor point for text"),
319                                    GTK_TYPE_ANCHOR_TYPE,
320                                    GTK_ANCHOR_NW,
321                                    G_PARAM_READWRITE));
322         g_object_class_install_property (
323                 gobject_class,
324                 PROP_PIXELS_ABOVE_LINES,
325                 g_param_spec_int ("pixels_above_lines",
326                                   _("Pixels Above Lines"),
327                                   _("Number of pixels to put above lines"),
328                                   G_MININT, G_MAXINT,
329                                   0,
330                                   G_PARAM_READWRITE));
331         g_object_class_install_property (
332                 gobject_class,
333                 PROP_PIXELS_BELOW_LINES,
334                 g_param_spec_int ("pixels_below_lines",
335                                   _("Pixels Below Lines"),
336                                   _("Number of pixels to put below lines"),
337                                   G_MININT, G_MAXINT,
338                                   0,
339                                   G_PARAM_READWRITE));
340         g_object_class_install_property (
341                 gobject_class,
342                 PROP_PIXELS_INSIDE_WRAP,
343                 g_param_spec_int ("pixels_inside_wrap",
344                                   _("Pixels Inside Wrap"),
345                                   _("Number of pixels to put inside the wrap"),
346                                   G_MININT, G_MAXINT,
347                                   0,
348                                   G_PARAM_READWRITE));
349         g_object_class_install_property (
350                 gobject_class,
351                 PROP_LEFT_MARGIN,
352                 g_param_spec_int ("left_margin",
353                                   _("Left Margin"),
354                                   _("Number of pixels in the left margin"),
355                                   G_MININT, G_MAXINT,
356                                   0,
357                                   G_PARAM_READWRITE));
358         g_object_class_install_property (
359                 gobject_class,
360                 PROP_RIGHT_MARGIN,
361                 g_param_spec_int ("right_margin",
362                                   _("Right Margin"),
363                                   _("Number of pixels in the right margin"),
364                                   G_MININT, G_MAXINT,
365                                   0,
366                                   G_PARAM_READWRITE));
367         g_object_class_install_property (
368                 gobject_class,
369                 PROP_INDENT,
370                 g_param_spec_int ("indent",
371                                   _("Indentation"),
372                                   _("Number of pixels for indentation"),
373                                   G_MININT, G_MAXINT,
374                                   0,
375                                   G_PARAM_READWRITE));
376
377         /* Signals */
378         signals[TAG_CHANGED] = g_signal_new(
379                 "tag_changed",
380                 G_TYPE_FROM_CLASS(object_class),
381                 G_SIGNAL_RUN_LAST,
382                 G_STRUCT_OFFSET(GnomeCanvasRichTextClass, tag_changed),
383                 NULL, NULL,
384                 g_cclosure_marshal_VOID__OBJECT,
385                 G_TYPE_NONE, 1,
386                 G_TYPE_OBJECT);
387
388         gobject_class->finalize = gnome_canvas_rich_text_finalize;
389
390         item_class->update = gnome_canvas_rich_text_update;
391         item_class->realize = gnome_canvas_rich_text_realize;
392         item_class->unrealize = gnome_canvas_rich_text_unrealize;
393         item_class->draw = gnome_canvas_rich_text_draw;
394         item_class->point = gnome_canvas_rich_text_point;
395         item_class->render = gnome_canvas_rich_text_render;
396         item_class->event = gnome_canvas_rich_text_event;
397         item_class->bounds = gnome_canvas_rich_text_get_bounds;
398 } /* gnome_canvas_rich_text_class_init */
399
400 static void
401 gnome_canvas_rich_text_init(GnomeCanvasRichText *text)
402 {
403 #if 0
404         GtkObject *object = GTK_OBJECT(text);
405
406         object->flags |= GNOME_CANVAS_ITEM_ALWAYS_REDRAW;
407 #endif
408         text->_priv = g_new0(GnomeCanvasRichTextPrivate, 1);
409
410         /* Try to set some sane defaults */
411         text->_priv->cursor_visible = TRUE;
412         text->_priv->cursor_blink = TRUE;
413         text->_priv->editable = TRUE;
414         text->_priv->visible = TRUE;
415         text->_priv->grow_height = FALSE;
416         text->_priv->wrap_mode = GTK_WRAP_WORD;
417         text->_priv->justification = GTK_JUSTIFY_LEFT;
418         text->_priv->direction = gtk_widget_get_default_direction();
419         text->_priv->anchor = GTK_ANCHOR_NW;
420         
421         text->_priv->blink_timeout = 0;
422         text->_priv->preblink_timeout = 0;
423
424         text->_priv->clicks = 0;
425         text->_priv->click_timeout = 0;
426 } /* gnome_canvas_rich_text_init */
427
428 static void
429 gnome_canvas_rich_text_set_property (GObject *object, guint property_id,
430                                      const GValue *value, GParamSpec *pspec)
431 {
432         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
433
434         switch (property_id) {
435         case PROP_TEXT:
436                 if (text->_priv->text)
437                         g_free(text->_priv->text);
438
439                 text->_priv->text = g_value_dup_string (value);
440
441                 gtk_text_buffer_set_text(
442                         get_buffer(text), text->_priv->text, strlen(text->_priv->text));
443
444                 break;
445         case PROP_X:
446                 text->_priv->x = g_value_get_double (value);
447                 break;
448         case PROP_Y:
449                 text->_priv->y = g_value_get_double (value);
450                 break;
451         case PROP_WIDTH:
452                 text->_priv->width = g_value_get_double (value);
453                 break;
454         case PROP_HEIGHT:
455                 text->_priv->height = g_value_get_double (value);
456                 break;
457         case PROP_EDITABLE:
458                 text->_priv->editable = g_value_get_boolean (value);
459                 if (text->_priv->layout) {
460                         text->_priv->layout->default_style->editable =
461                                 text->_priv->editable;
462                         gtk_text_layout_default_style_changed(text->_priv->layout);
463                 }
464                 break;
465         case PROP_VISIBLE:
466                 text->_priv->visible = g_value_get_boolean (value);
467                 if (text->_priv->layout) {
468                         text->_priv->layout->default_style->invisible =
469                                 !text->_priv->visible;
470                         gtk_text_layout_default_style_changed(text->_priv->layout);
471                 }
472                 break;
473         case PROP_CURSOR_VISIBLE:
474                 text->_priv->cursor_visible = g_value_get_boolean (value);
475                 if (text->_priv->layout) {
476                         gtk_text_layout_set_cursor_visible(
477                                 text->_priv->layout, text->_priv->cursor_visible);
478
479                         if (text->_priv->cursor_visible && text->_priv->cursor_blink) {
480                                 gnome_canvas_rich_text_start_cursor_blink(
481                                         text, FALSE);
482                         }
483                         else
484                                 gnome_canvas_rich_text_stop_cursor_blink(text);
485                 }
486                 break;
487         case PROP_CURSOR_BLINK:
488                 text->_priv->cursor_blink = g_value_get_boolean (value);
489                 if (text->_priv->layout && text->_priv->cursor_visible) {
490                         if (text->_priv->cursor_blink && !text->_priv->blink_timeout) {
491                                 gnome_canvas_rich_text_start_cursor_blink(
492                                         text, FALSE);
493                         }
494                         else if (!text->_priv->cursor_blink && text->_priv->blink_timeout) {
495                                 gnome_canvas_rich_text_stop_cursor_blink(text);
496                                 gtk_text_layout_set_cursor_visible(
497                                         text->_priv->layout, TRUE);
498                         }
499                 }
500                 break;
501         case PROP_GROW_HEIGHT:
502                 text->_priv->grow_height = g_value_get_boolean (value);
503                 /* FIXME: Recalc here */
504                 break;
505         case PROP_WRAP_MODE:
506                 text->_priv->wrap_mode = g_value_get_enum (value);
507
508                 if (text->_priv->layout) {
509                         text->_priv->layout->default_style->wrap_mode = 
510                                 text->_priv->wrap_mode;
511                         gtk_text_layout_default_style_changed(text->_priv->layout);
512                 }
513                 break;
514         case PROP_JUSTIFICATION:
515                 text->_priv->justification = g_value_get_enum (value);
516
517                 if (text->_priv->layout) {
518                         text->_priv->layout->default_style->justification =
519                                 text->_priv->justification;
520                         gtk_text_layout_default_style_changed(text->_priv->layout);
521                 }
522                 break;
523         case PROP_DIRECTION:
524                 text->_priv->direction = g_value_get_enum (value);
525
526                 if (text->_priv->layout) {
527                         text->_priv->layout->default_style->direction =
528                                 text->_priv->direction;
529                         gtk_text_layout_default_style_changed(text->_priv->layout);
530                 }
531                 break;
532         case PROP_ANCHOR:
533                 text->_priv->anchor = g_value_get_enum (value);
534                 break;
535         case PROP_PIXELS_ABOVE_LINES:
536                 text->_priv->pixels_above_lines = g_value_get_int (value);
537                 
538                 if (text->_priv->layout) {
539                         text->_priv->layout->default_style->pixels_above_lines =
540                                 text->_priv->pixels_above_lines;
541                         gtk_text_layout_default_style_changed(text->_priv->layout);
542                 }
543                 break;
544         case PROP_PIXELS_BELOW_LINES:
545                 text->_priv->pixels_below_lines = g_value_get_int (value);
546                 
547                 if (text->_priv->layout) {
548                         text->_priv->layout->default_style->pixels_below_lines =
549                                 text->_priv->pixels_below_lines;
550                         gtk_text_layout_default_style_changed(text->_priv->layout);
551                 }
552                 break;
553         case PROP_PIXELS_INSIDE_WRAP:
554                 text->_priv->pixels_inside_wrap = g_value_get_int (value);
555                 
556                 if (text->_priv->layout) {
557                         text->_priv->layout->default_style->pixels_inside_wrap =
558                                 text->_priv->pixels_inside_wrap;
559                         gtk_text_layout_default_style_changed(text->_priv->layout);
560                 }
561                 break;
562         case PROP_LEFT_MARGIN:
563                 text->_priv->left_margin = g_value_get_int (value);
564                 
565                 if (text->_priv->layout) {
566                         text->_priv->layout->default_style->left_margin =
567                                 text->_priv->left_margin;
568                         gtk_text_layout_default_style_changed(text->_priv->layout);
569                 }
570                 break;
571         case PROP_RIGHT_MARGIN:
572                 text->_priv->right_margin = g_value_get_int (value);
573                 
574                 if (text->_priv->layout) {
575                         text->_priv->layout->default_style->right_margin =
576                                 text->_priv->right_margin;
577                         gtk_text_layout_default_style_changed(text->_priv->layout);
578                 }
579                 break;
580         case PROP_INDENT:
581                 text->_priv->pixels_above_lines = g_value_get_int (value);
582                 
583                 if (text->_priv->layout) {
584                         text->_priv->layout->default_style->indent = text->_priv->indent;
585                         gtk_text_layout_default_style_changed(text->_priv->layout);
586                 }
587                 break;
588                        
589         default:
590                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
591                 break;
592         }
593
594         gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
595 }
596
597 static void
598 gnome_canvas_rich_text_get_property (GObject *object, guint property_id,
599                                      GValue *value, GParamSpec *pspec)
600 {
601         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
602
603         switch (property_id) {
604         case PROP_TEXT:
605                 g_value_set_string (value, text->_priv->text);
606                 break;
607         case PROP_X:
608                 g_value_set_double (value, text->_priv->x);
609                 break;
610         case PROP_Y:
611                 g_value_set_double (value, text->_priv->y);
612                 break;
613         case PROP_HEIGHT:
614                 g_value_set_double (value, text->_priv->height);
615                 break;
616         case PROP_WIDTH:
617                 g_value_set_double (value, text->_priv->width);
618                 break;
619         case PROP_EDITABLE:
620                 g_value_set_boolean (value, text->_priv->editable);
621                 break;
622         case PROP_CURSOR_VISIBLE:
623                 g_value_set_boolean (value, text->_priv->cursor_visible);
624                 break;
625         case PROP_CURSOR_BLINK:
626                 g_value_set_boolean (value, text->_priv->cursor_blink);
627                 break;
628         case PROP_GROW_HEIGHT:
629                 g_value_set_boolean (value, text->_priv->grow_height);
630                 break;
631         case PROP_WRAP_MODE:
632                 g_value_set_enum (value, text->_priv->wrap_mode);
633                 break;
634         case PROP_JUSTIFICATION:
635                 g_value_set_enum (value, text->_priv->justification);
636                 break;
637         case PROP_DIRECTION:
638                 g_value_set_enum (value, text->_priv->direction);
639                 break;
640         case PROP_ANCHOR:
641                 g_value_set_enum (value, text->_priv->anchor);
642                 break;
643         case PROP_PIXELS_ABOVE_LINES:
644                 g_value_set_enum (value, text->_priv->pixels_above_lines);
645                 break;
646         case PROP_PIXELS_BELOW_LINES:
647                 g_value_set_int (value, text->_priv->pixels_below_lines);
648                 break;
649         case PROP_PIXELS_INSIDE_WRAP:
650                 g_value_set_int (value, text->_priv->pixels_inside_wrap);
651                 break;
652         case PROP_LEFT_MARGIN:
653                 g_value_set_int (value, text->_priv->left_margin);
654                 break;
655         case PROP_RIGHT_MARGIN:
656                 g_value_set_int (value, text->_priv->right_margin);
657                 break;
658         case PROP_INDENT:
659                 g_value_set_int (value, text->_priv->indent);
660                 break;
661         default:
662                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
663                 break;
664         }
665 }
666
667 static void
668 gnome_canvas_rich_text_realize(GnomeCanvasItem *item)
669 {
670         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
671
672         (* GNOME_CANVAS_ITEM_CLASS(parent_class)->realize)(item);
673
674         gnome_canvas_rich_text_ensure_layout(text);
675 } /* gnome_canvas_rich_text_realize */
676
677 static void
678 gnome_canvas_rich_text_unrealize(GnomeCanvasItem *item)
679 {
680         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
681
682         gnome_canvas_rich_text_destroy_layout(text);
683
684         (* GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize)(item);
685 } /* gnome_canvas_rich_text_unrealize */
686
687 static void
688 gnome_canvas_rich_text_move_iter_by_lines(GnomeCanvasRichText *text,
689                                           GtkTextIter *newplace, gint count)
690 {
691         while (count < 0) {
692                 gtk_text_layout_move_iter_to_previous_line(
693                         text->_priv->layout, newplace);
694                 count++;
695         }
696
697         while (count > 0) {
698                 gtk_text_layout_move_iter_to_next_line(
699                         text->_priv->layout, newplace);
700                 count--;
701         }
702 } /* gnome_canvas_rich_text_move_iter_by_lines */
703
704 static gint
705 gnome_canvas_rich_text_get_cursor_x_position(GnomeCanvasRichText *text)
706 {
707         GtkTextIter insert;
708         GdkRectangle rect;
709
710         gtk_text_buffer_get_iter_at_mark(
711                 get_buffer(text), &insert,
712                 gtk_text_buffer_get_mark(get_buffer(text), "insert"));
713         gtk_text_layout_get_cursor_locations(
714                 text->_priv->layout, &insert, &rect, NULL);
715
716         return rect.x;
717 } /* gnome_canvas_rich_text_get_cursor_x_position */
718
719 static void
720 gnome_canvas_rich_text_move_cursor(GnomeCanvasRichText *text,
721                                    GtkMovementStep step,
722                                    gint count, gboolean extend_selection)
723 {
724         GtkTextIter insert, newplace;
725
726         gtk_text_buffer_get_iter_at_mark(
727                 get_buffer(text), &insert, 
728                 gtk_text_buffer_get_mark(get_buffer(text), "insert"));
729
730         newplace = insert;
731
732         switch (step) {
733         case GTK_MOVEMENT_LOGICAL_POSITIONS:
734                 gtk_text_iter_forward_cursor_positions(&newplace, count);
735                 break;
736         case GTK_MOVEMENT_VISUAL_POSITIONS:
737                 gtk_text_layout_move_iter_visually(
738                         text->_priv->layout, &newplace, count);
739                 break;
740         case GTK_MOVEMENT_WORDS:
741                 if (count < 0)
742                         gtk_text_iter_backward_word_starts(&newplace, -count);
743                 else if (count > 0)
744                         gtk_text_iter_forward_word_ends(&newplace, count);
745                 break;
746         case GTK_MOVEMENT_DISPLAY_LINES:
747                 gnome_canvas_rich_text_move_iter_by_lines(
748                         text, &newplace, count);
749                 gtk_text_layout_move_iter_to_x(
750                         text->_priv->layout, &newplace, 
751                         gnome_canvas_rich_text_get_cursor_x_position(text));
752                 break;
753         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
754                 if (count > 1) {
755                         gnome_canvas_rich_text_move_iter_by_lines(
756                                 text, &newplace, --count);
757                 }
758                 else if (count < -1) {
759                         gnome_canvas_rich_text_move_iter_by_lines(
760                                 text, &newplace, ++count);
761                 }
762                
763                 if (count != 0) {
764                         gtk_text_layout_move_iter_to_line_end(
765                                 text->_priv->layout, &newplace, count);
766                 }
767                 break;
768         case GTK_MOVEMENT_PARAGRAPHS:
769                 /* FIXME: Busted in gtktextview.c too */
770                 break;
771         case GTK_MOVEMENT_PARAGRAPH_ENDS:
772                 if (count > 0)
773                         gtk_text_iter_forward_to_line_end(&newplace);
774                 else if (count < 0)
775                         gtk_text_iter_set_line_offset(&newplace, 0);
776                 break;
777         case GTK_MOVEMENT_BUFFER_ENDS:
778                 if (count > 0) {
779                         gtk_text_buffer_get_end_iter(
780                                 get_buffer(text), &newplace);
781                 }
782                 else if (count < 0) {
783                         gtk_text_buffer_get_iter_at_offset(
784                                 get_buffer(text), &newplace, 0);
785                 }
786                 break;
787         default:
788                 break;
789         }
790
791         if (!gtk_text_iter_equal(&insert, &newplace)) {
792                 if (extend_selection) {
793                         gtk_text_buffer_move_mark(
794                                 get_buffer(text),
795                                 gtk_text_buffer_get_mark(
796                                         get_buffer(text), "insert"),
797                                 &newplace);
798                 }
799                 else {
800                         gtk_text_buffer_place_cursor(
801                                 get_buffer(text), &newplace);
802                 }
803         }
804
805         gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
806 } /* gnome_canvas_rich_text_move_cursor */
807
808 static gboolean
809 whitespace(gunichar ch, gpointer ignored)
810 {
811         return (ch == ' ' || ch == '\t');
812 } /* whitespace */
813
814 static gboolean
815 not_whitespace(gunichar ch, gpointer ignored)
816 {
817         return !whitespace(ch, ignored);
818 } /* not_whitespace */
819
820 static gboolean
821 find_whitespace_region(const GtkTextIter *center,
822                        GtkTextIter *start, GtkTextIter *end)
823 {
824         *start = *center;
825         *end = *center;
826
827         if (gtk_text_iter_backward_find_char(start, not_whitespace, NULL, NULL))
828                 gtk_text_iter_forward_char(start);
829         if (whitespace(gtk_text_iter_get_char(end), NULL))
830                 gtk_text_iter_forward_find_char(end, not_whitespace, NULL, NULL);
831
832         return !gtk_text_iter_equal(start, end);
833 } /* find_whitespace_region */
834
835 static void
836 gnome_canvas_rich_text_delete_from_cursor(GnomeCanvasRichText *text,
837                                           GtkDeleteType type,
838                                           gint count)
839 {
840         GtkTextIter insert, start, end;
841
842         /* Special case: If the user wants to delete a character and there is
843            a selection, then delete the selection and return */
844         if (type == GTK_DELETE_CHARS) {
845                 if (gtk_text_buffer_delete_selection(get_buffer(text), TRUE, 
846                                                      text->_priv->editable))
847                         return;
848         }
849
850         gtk_text_buffer_get_iter_at_mark(
851                 get_buffer(text), &insert,
852                 gtk_text_buffer_get_mark(get_buffer(text), "insert"));
853
854         start = insert;
855         end = insert;
856
857         switch (type) {
858         case GTK_DELETE_CHARS:
859                 gtk_text_iter_forward_cursor_positions(&end, count);
860                 break;
861         case GTK_DELETE_WORD_ENDS:
862                 if (count > 0)
863                         gtk_text_iter_forward_word_ends(&end, count);
864                 else if (count < 0)
865                         gtk_text_iter_backward_word_starts(&start, -count);
866                 break;
867         case GTK_DELETE_WORDS:
868                 break;
869         case GTK_DELETE_DISPLAY_LINE_ENDS:
870                 break;
871         case GTK_DELETE_PARAGRAPH_ENDS:
872                 if (gtk_text_iter_ends_line(&end)) {
873                         gtk_text_iter_forward_line(&end);
874                         --count;
875                 }
876
877                 while (count > 0) {
878                         if (!gtk_text_iter_forward_to_line_end(&end))
879                                 break;
880
881                         --count;
882                 }
883                 break;
884         case GTK_DELETE_PARAGRAPHS:
885                 if (count > 0) {
886                         gtk_text_iter_set_line_offset(&start, 0);
887                         gtk_text_iter_forward_to_line_end(&end);
888
889                         /* Do the lines beyond the first. */
890                         while (count > 1) {
891                                 gtk_text_iter_forward_to_line_end(&end);
892                                 --count;
893                         }
894                 }
895                 break;
896         case GTK_DELETE_WHITESPACE:
897                 find_whitespace_region(&insert, &start, &end);
898                 break;
899
900         default:
901                 break;
902         }
903
904         if (!gtk_text_iter_equal(&start, &end)) {
905                 gtk_text_buffer_begin_user_action(get_buffer(text));
906                 gtk_text_buffer_delete_interactive(
907                         get_buffer(text), &start, &end, text->_priv->editable);
908                 gtk_text_buffer_end_user_action(get_buffer(text));
909         }
910 } /* gnome_canvas_rich_text_delete_from_cursor */
911
912 static gint
913 selection_motion_event_handler(GnomeCanvasRichText *text, GdkEvent *event,
914                                gpointer ignored)
915 {
916         GtkTextIter newplace;
917         GtkTextMark *mark;
918         double newx, newy;
919
920         /* We only want to handle motion events... */
921         if (event->type != GDK_MOTION_NOTIFY)
922                 return FALSE;
923
924         newx = (event->motion.x - text->_priv->x) * 
925                 GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
926         newy = (event->motion.y - text->_priv->y) * 
927                 GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
928
929         gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &newplace, newx, newy);
930         mark = gtk_text_buffer_get_mark(get_buffer(text), "insert");
931         gtk_text_buffer_move_mark(get_buffer(text), mark, &newplace);
932
933         return TRUE;
934 } /* selection_motion_event_handler */
935
936 static void
937 gnome_canvas_rich_text_start_selection_drag(GnomeCanvasRichText *text,
938                                             const GtkTextIter *iter,
939                                             GdkEventButton * button)
940 {
941         GtkTextIter newplace;
942
943         g_return_if_fail(text->_priv->selection_drag_handler == 0);
944
945 #if 0
946         gnome_canvas_item_grab_focus(GNOME_CANVAS_ITEM(text));
947 #endif
948
949         newplace = *iter;
950
951         gtk_text_buffer_place_cursor(get_buffer(text), &newplace);
952
953         text->_priv->selection_drag_handler = g_signal_connect(
954                 text, "event",
955                 G_CALLBACK (selection_motion_event_handler),
956                 NULL);
957 } /* gnome_canvas_rich_text_start_selection_drag */
958
959 static gboolean
960 gnome_canvas_rich_text_end_selection_drag(GnomeCanvasRichText *text,
961                                           GdkEventButton * event)
962 {
963         if (text->_priv->selection_drag_handler == 0)
964                 return FALSE;
965
966         g_signal_handler_disconnect (text, text->_priv->selection_drag_handler);
967         text->_priv->selection_drag_handler = 0;
968
969 #if 0
970         gnome_canvas_item_grab(NULL);
971 #endif
972
973         return TRUE;
974 } /* gnome_canvas_rich_text_end_selection_drag */
975
976 static void
977 gnome_canvas_rich_text_emit_tag_changed(GnomeCanvasRichText *text,
978                                         GtkTextTag *tag)
979 {
980         g_signal_emit(G_OBJECT(text), signals[TAG_CHANGED], 0, tag);
981 } /* gnome_canvas_rich_text_emit_tag_changed */
982                                                 
983 static gint
984 gnome_canvas_rich_text_key_press_event(GnomeCanvasItem *item, 
985                                        GdkEventKey *event)
986 {
987         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
988         gboolean extend_selection = FALSE;
989         gboolean handled = FALSE;
990
991 #if 0
992         printf("Key press event\n");
993 #endif
994
995         if (!text->_priv->layout || !text->_priv->buffer)
996                 return FALSE;
997
998         if (event->state & GDK_SHIFT_MASK)
999                 extend_selection = TRUE;
1000
1001         switch (event->keyval) {
1002         case GDK_Return:
1003         case GDK_KP_Enter:
1004                 gtk_text_buffer_delete_selection(
1005                         get_buffer(text), TRUE, text->_priv->editable);
1006                 gtk_text_buffer_insert_interactive_at_cursor(
1007                         get_buffer(text), "\n", 1, text->_priv->editable);
1008                 handled = TRUE;
1009                 break;
1010
1011         case GDK_Tab:
1012                 gtk_text_buffer_insert_interactive_at_cursor(
1013                         get_buffer(text), "\t", 1, text->_priv->editable);
1014                 handled = TRUE;
1015                 break;
1016
1017         /* MOVEMENT */
1018         case GDK_Right:
1019                 if (event->state & GDK_CONTROL_MASK) {
1020                         gnome_canvas_rich_text_move_cursor(
1021                                 text, GTK_MOVEMENT_WORDS, 1, 
1022                                 extend_selection);
1023                         handled = TRUE;
1024                 }
1025                 else {
1026                         gnome_canvas_rich_text_move_cursor(
1027                                 text, GTK_MOVEMENT_VISUAL_POSITIONS, 1,
1028                                 extend_selection);
1029                         handled = TRUE;
1030                 }
1031                 break;
1032         case GDK_Left:
1033                 if (event->state & GDK_CONTROL_MASK) {
1034                         gnome_canvas_rich_text_move_cursor(
1035                                 text, GTK_MOVEMENT_WORDS, -1,
1036                                 extend_selection);
1037                         handled = TRUE;
1038                 }
1039                 else {
1040                         gnome_canvas_rich_text_move_cursor(
1041                                 text, GTK_MOVEMENT_VISUAL_POSITIONS, -1,
1042                                 extend_selection);
1043                         handled = TRUE;
1044                 }
1045                 break;
1046         case GDK_f:
1047                 if (event->state & GDK_CONTROL_MASK) {
1048                         gnome_canvas_rich_text_move_cursor(
1049                                 text, GTK_MOVEMENT_LOGICAL_POSITIONS, 1,
1050                                 extend_selection);
1051                         handled = TRUE;
1052                 }
1053                 else if (event->state & GDK_MOD1_MASK) {
1054                         gnome_canvas_rich_text_move_cursor(
1055                                 text, GTK_MOVEMENT_WORDS, 1,
1056                                 extend_selection);
1057                         handled = TRUE;
1058                 }
1059                 break;
1060         case GDK_b:
1061                 if (event->state & GDK_CONTROL_MASK) {
1062                         gnome_canvas_rich_text_move_cursor(
1063                                 text, GTK_MOVEMENT_LOGICAL_POSITIONS, -1,
1064                                 extend_selection);
1065                         handled = TRUE;
1066                 }
1067                 else if (event->state & GDK_MOD1_MASK) {
1068                         gnome_canvas_rich_text_move_cursor(
1069                                 text, GTK_MOVEMENT_WORDS, -1,
1070                                 extend_selection);
1071                         handled = TRUE;
1072                 }
1073                 break;
1074         case GDK_Up:
1075                 gnome_canvas_rich_text_move_cursor(
1076                         text, GTK_MOVEMENT_DISPLAY_LINES, -1,
1077                         extend_selection);
1078                 handled = TRUE;
1079                 break;
1080         case GDK_Down:
1081                 gnome_canvas_rich_text_move_cursor(
1082                         text, GTK_MOVEMENT_DISPLAY_LINES, 1,
1083                         extend_selection);
1084                 handled = TRUE;
1085                 break;
1086         case GDK_p:
1087                 if (event->state & GDK_CONTROL_MASK) {
1088                         gnome_canvas_rich_text_move_cursor(
1089                                 text, GTK_MOVEMENT_DISPLAY_LINES, -1,
1090                                 extend_selection);
1091                         handled = TRUE;
1092                 }
1093                 break;
1094         case GDK_n:
1095                 if (event->state & GDK_CONTROL_MASK) {
1096                         gnome_canvas_rich_text_move_cursor(
1097                                 text, GTK_MOVEMENT_DISPLAY_LINES, 1,
1098                                 extend_selection);
1099                         handled = TRUE;
1100                 }
1101                 break;
1102         case GDK_Home:
1103                 gnome_canvas_rich_text_move_cursor(
1104                         text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
1105                         extend_selection);
1106                 handled = TRUE;
1107                 break;
1108         case GDK_End:
1109                 gnome_canvas_rich_text_move_cursor(
1110                         text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
1111                         extend_selection);
1112                 handled = TRUE;
1113                 break;
1114         case GDK_a:
1115                 if (event->state & GDK_CONTROL_MASK) {
1116                         gnome_canvas_rich_text_move_cursor(
1117                                 text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
1118                                 extend_selection);
1119                         handled = TRUE;
1120                 }
1121                 break;
1122         case GDK_e:
1123                 if (event->state & GDK_CONTROL_MASK) {
1124                         gnome_canvas_rich_text_move_cursor(
1125                                 text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
1126                                 extend_selection);
1127                         handled = TRUE;
1128                 }
1129                 break;
1130
1131         /* DELETING TEXT */
1132         case GDK_Delete:
1133         case GDK_KP_Delete:
1134                 if (event->state & GDK_CONTROL_MASK) {
1135                         gnome_canvas_rich_text_delete_from_cursor(
1136                                 text, GTK_DELETE_WORD_ENDS, 1);
1137                         handled = TRUE;
1138                 }
1139                 else {
1140                         gnome_canvas_rich_text_delete_from_cursor(
1141                                 text, GTK_DELETE_CHARS, 1);
1142                         handled = TRUE;
1143                 }
1144                 break;
1145         case GDK_d:
1146                 if (event->state & GDK_CONTROL_MASK) {
1147                         gnome_canvas_rich_text_delete_from_cursor(
1148                                 text, GTK_DELETE_CHARS, 1);
1149                         handled = TRUE;
1150                 }
1151                 else if (event->state & GDK_MOD1_MASK) {
1152                         gnome_canvas_rich_text_delete_from_cursor(
1153                                 text, GTK_DELETE_WORD_ENDS, 1);
1154                         handled = TRUE;
1155                 }
1156                 break;
1157         case GDK_BackSpace:
1158                 if (event->state & GDK_CONTROL_MASK) {
1159                         gnome_canvas_rich_text_delete_from_cursor(
1160                                 text, GTK_DELETE_WORD_ENDS, -1);
1161                         handled = TRUE;
1162                 }
1163                 else {
1164                         gnome_canvas_rich_text_delete_from_cursor(
1165                                 text, GTK_DELETE_CHARS, -1);
1166                 }
1167                 handled = TRUE;
1168                 break;
1169         case GDK_k:
1170                 if (event->state & GDK_CONTROL_MASK) {
1171                         gnome_canvas_rich_text_delete_from_cursor(
1172                                 text, GTK_DELETE_PARAGRAPH_ENDS, 1);
1173                         handled = TRUE;
1174                 }
1175                 break;
1176         case GDK_u:
1177                 if (event->state & GDK_CONTROL_MASK) {
1178                         gnome_canvas_rich_text_delete_from_cursor(
1179                                 text, GTK_DELETE_PARAGRAPHS, 1);
1180                         handled = TRUE;
1181                 }
1182                 break;
1183         case GDK_space:
1184                 if (event->state & GDK_MOD1_MASK) {
1185                         gnome_canvas_rich_text_delete_from_cursor(
1186                                 text, GTK_DELETE_WHITESPACE, 1);
1187                         handled = TRUE;
1188                 }
1189                 break;
1190         case GDK_backslash:
1191                 if (event->state & GDK_MOD1_MASK) {
1192                         gnome_canvas_rich_text_delete_from_cursor(
1193                                 text, GTK_DELETE_WHITESPACE, 1);
1194                         handled = TRUE;
1195                 }
1196                 break;
1197         default:
1198                 break;
1199         }
1200
1201         /* An empty string, click just pressing "Alt" by itself or whatever. */
1202         if (!event->length)
1203                 return FALSE;
1204
1205         if (!handled) {
1206                 gtk_text_buffer_delete_selection(
1207                         get_buffer(text), TRUE, text->_priv->editable);
1208                 gtk_text_buffer_insert_interactive_at_cursor(
1209                         get_buffer(text), event->string, event->length,
1210                         text->_priv->editable);
1211         }
1212
1213         gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
1214         
1215         return TRUE;
1216 } /* gnome_canvas_rich_text_key_press_event */
1217
1218 static gint
1219 gnome_canvas_rich_text_key_release_event(GnomeCanvasItem * item, 
1220                                          GdkEventKey *event)
1221 {
1222         return FALSE;
1223 } /* gnome_canvas_rich_text_key_release_event */
1224
1225 static gboolean
1226 _click(gpointer data)
1227 {
1228         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1229
1230         text->_priv->clicks = 0;
1231         text->_priv->click_timeout = 0;
1232
1233         return FALSE;
1234 } /* _click */
1235
1236 static gint
1237 gnome_canvas_rich_text_button_press_event(GnomeCanvasItem *item,
1238                                           GdkEventButton *event)
1239 {
1240         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
1241         GtkTextIter iter;
1242         GdkEventType event_type;
1243         double newx, newy;
1244
1245         newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
1246         newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
1247         
1248         gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, newx, newy);
1249
1250         /* The canvas doesn't give us double- or triple-click events, so
1251            we have to synthesize them ourselves. Yay. */
1252         event_type = event->type;
1253         if (event_type == GDK_BUTTON_PRESS) {
1254                 text->_priv->clicks++;
1255                 text->_priv->click_timeout = g_timeout_add(400, _click, text);
1256
1257                 if (text->_priv->clicks > 3)
1258                         text->_priv->clicks = text->_priv->clicks % 3;
1259
1260                 if (text->_priv->clicks == 1)
1261                         event_type = GDK_BUTTON_PRESS;
1262                 else if (text->_priv->clicks == 2)
1263                         event_type = GDK_2BUTTON_PRESS;
1264                 else if (text->_priv->clicks == 3)
1265                         event_type = GDK_3BUTTON_PRESS;
1266                 else
1267                         printf("ZERO CLICKS!\n");
1268         }
1269
1270         if (event->button == 1 && event_type == GDK_BUTTON_PRESS) {
1271                 GtkTextIter start, end;
1272
1273                 if (gtk_text_buffer_get_selection_bounds(
1274                             get_buffer(text), &start, &end) &&
1275                     gtk_text_iter_in_range(&iter, &start, &end)) {
1276                         text->_priv->drag_start_x = event->x;
1277                         text->_priv->drag_start_y = event->y;
1278                 }
1279                 else {
1280                         gnome_canvas_rich_text_start_selection_drag(
1281                                 text, &iter, event);
1282                 }
1283
1284                 return TRUE;
1285         }
1286         else if (event->button == 1 && event_type == GDK_2BUTTON_PRESS) {
1287                 GtkTextIter start, end;
1288
1289 #if 0
1290                 printf("double-click\n");
1291 #endif
1292                 
1293                 gnome_canvas_rich_text_end_selection_drag(text, event);
1294
1295                 start = iter;
1296                 end = start;
1297
1298                 if (gtk_text_iter_inside_word(&start)) {
1299                         if (!gtk_text_iter_starts_word(&start))
1300                                 gtk_text_iter_backward_word_start(&start);
1301                         
1302                         if (!gtk_text_iter_ends_word(&end))
1303                                 gtk_text_iter_forward_word_end(&end);
1304                 }
1305
1306                 gtk_text_buffer_move_mark(
1307                         get_buffer(text),
1308                         gtk_text_buffer_get_selection_bound(get_buffer(text)),
1309                         &start);
1310                 gtk_text_buffer_move_mark(
1311                         get_buffer(text),
1312                         gtk_text_buffer_get_insert(get_buffer(text)), &end);
1313
1314                 text->_priv->just_selected_element = TRUE;
1315
1316                 return TRUE;
1317         }
1318         else if (event->button == 1 && event_type == GDK_3BUTTON_PRESS) {
1319                 GtkTextIter start, end;
1320
1321 #if 0
1322                 printf("triple-click\n");
1323 #endif
1324
1325                 gnome_canvas_rich_text_end_selection_drag(text, event);
1326
1327                 start = iter;
1328                 end = start;
1329
1330                 if (gtk_text_layout_iter_starts_line(text->_priv->layout, &start)) {
1331                         gtk_text_layout_move_iter_to_line_end(
1332                                 text->_priv->layout, &start, -1);
1333                 }
1334                 else {
1335                         gtk_text_layout_move_iter_to_line_end(
1336                                 text->_priv->layout, &start, -1);
1337
1338                         if (!gtk_text_layout_iter_starts_line(
1339                                     text->_priv->layout, &end)) {
1340                                 gtk_text_layout_move_iter_to_line_end(
1341                                         text->_priv->layout, &end, 1);
1342                         }
1343                 }
1344
1345                 gtk_text_buffer_move_mark(
1346                         get_buffer(text),
1347                         gtk_text_buffer_get_selection_bound(get_buffer(text)),
1348                         &start);
1349                 gtk_text_buffer_move_mark(
1350                         get_buffer(text),
1351                         gtk_text_buffer_get_insert(get_buffer(text)), &end);
1352
1353                 text->_priv->just_selected_element = TRUE;
1354
1355                 return TRUE;
1356         }
1357         else if (event->button == 2 && event_type == GDK_BUTTON_PRESS) {
1358                 gtk_text_buffer_paste_clipboard(
1359                         get_buffer(text),
1360                         gtk_clipboard_get (GDK_SELECTION_PRIMARY),
1361                         &iter, text->_priv->editable);
1362         }
1363                 
1364         return FALSE;
1365 } /* gnome_canvas_rich_text_button_press_event */
1366
1367 static gint
1368 gnome_canvas_rich_text_button_release_event(GnomeCanvasItem *item,
1369                                             GdkEventButton *event)
1370 {
1371         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
1372         double newx, newy;
1373
1374         newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
1375         newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
1376         
1377         if (event->button == 1) {
1378                 if (text->_priv->drag_start_x >= 0) {
1379                         text->_priv->drag_start_x = -1;
1380                         text->_priv->drag_start_y = -1;
1381                 }
1382
1383                 if (gnome_canvas_rich_text_end_selection_drag(text, event))
1384                         return TRUE;
1385                 else if (text->_priv->just_selected_element) {
1386                         text->_priv->just_selected_element = FALSE;
1387                         return FALSE;
1388                 }
1389                 else {
1390                         GtkTextIter iter;
1391
1392                         gtk_text_layout_get_iter_at_pixel(
1393                                 text->_priv->layout, &iter, newx, newy);
1394
1395                         gtk_text_buffer_place_cursor(get_buffer(text), &iter);
1396
1397                         return FALSE;
1398                 }
1399         }
1400
1401         return FALSE;
1402 } /* gnome_canvas_rich_text_button_release_event */
1403
1404 static gint
1405 gnome_canvas_rich_text_focus_in_event(GnomeCanvasItem *item,
1406                                       GdkEventFocus * event)
1407 {
1408         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
1409
1410         if (text->_priv->cursor_visible && text->_priv->layout) {
1411                 gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
1412                 gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
1413         }
1414
1415         return FALSE;
1416 } /* gnome_canvas_rich_text_focus_in_event */
1417
1418 static gint
1419 gnome_canvas_rich_text_focus_out_event(GnomeCanvasItem *item,
1420                                        GdkEventFocus * event)
1421 {
1422         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
1423
1424         if (text->_priv->cursor_visible && text->_priv->layout) {
1425                 gtk_text_layout_set_cursor_visible(text->_priv->layout, FALSE);
1426                 gnome_canvas_rich_text_stop_cursor_blink(text);
1427         }
1428
1429         return FALSE;
1430 } /* gnome_canvas_rich_text_focus_out_event */
1431
1432 static gboolean
1433 get_event_coordinates(GdkEvent *event, gint *x, gint *y)
1434 {
1435         g_return_val_if_fail(event, FALSE);
1436         
1437         switch (event->type) {
1438         case GDK_MOTION_NOTIFY:
1439                 *x = event->motion.x;
1440                 *y = event->motion.y;
1441                 return TRUE;
1442         case GDK_BUTTON_PRESS:
1443         case GDK_2BUTTON_PRESS:
1444         case GDK_3BUTTON_PRESS:
1445         case GDK_BUTTON_RELEASE:
1446                 *x = event->button.x;
1447                 *y = event->button.y;
1448                 return TRUE;
1449
1450         default:
1451                 return FALSE;
1452         }
1453 } /* get_event_coordinates */
1454
1455 static void
1456 emit_event_on_tags(GnomeCanvasRichText *text, GdkEvent *event, 
1457                    GtkTextIter *iter)
1458 {
1459         GSList *tags;
1460         GSList *i;
1461         
1462         tags = gtk_text_iter_get_tags(iter);
1463
1464         i = tags;
1465         while (i) {
1466                 GtkTextTag *tag = i->data;
1467
1468                 gtk_text_tag_event(tag, G_OBJECT(text), event, iter);
1469
1470                 /* The cursor has been moved to within this tag. Emit the
1471                    tag_changed signal */
1472                 if (event->type == GDK_BUTTON_RELEASE || 
1473                     event->type == GDK_KEY_PRESS ||
1474                     event->type == GDK_KEY_RELEASE) {
1475                         gnome_canvas_rich_text_emit_tag_changed(
1476                                 text, tag);
1477                 }
1478
1479                 i = g_slist_next(i);
1480         }
1481
1482         g_slist_free(tags);
1483 } /* emit_event_on_tags */
1484
1485 static gint
1486 gnome_canvas_rich_text_event(GnomeCanvasItem *item, GdkEvent *event)
1487 {
1488         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
1489         int x, y;
1490
1491         if (get_event_coordinates(event, &x, &y)) {
1492                 GtkTextIter iter;
1493
1494                 x -= text->_priv->x;
1495                 y -= text->_priv->y;
1496
1497                 gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, x, y);
1498                 emit_event_on_tags(text, event, &iter);
1499         }
1500         else if (event->type == GDK_KEY_PRESS ||
1501                  event->type == GDK_KEY_RELEASE) {
1502                 GtkTextMark *insert;
1503                 GtkTextIter iter;
1504
1505                 insert = gtk_text_buffer_get_mark(get_buffer(text), "insert");
1506                 gtk_text_buffer_get_iter_at_mark(
1507                         get_buffer(text), &iter, insert);
1508                 emit_event_on_tags(text, event, &iter);
1509         }
1510
1511         switch (event->type) {
1512         case GDK_KEY_PRESS:
1513                 return gnome_canvas_rich_text_key_press_event(
1514                         item, (GdkEventKey *) event);
1515         case GDK_KEY_RELEASE:
1516                 return gnome_canvas_rich_text_key_release_event(
1517                         item, (GdkEventKey *) event);
1518         case GDK_BUTTON_PRESS:
1519                 return gnome_canvas_rich_text_button_press_event(
1520                         item, (GdkEventButton *) event);
1521         case GDK_BUTTON_RELEASE:
1522                 return gnome_canvas_rich_text_button_release_event(
1523                         item, (GdkEventButton *) event);
1524         case GDK_FOCUS_CHANGE:
1525                 if (((GdkEventFocus *) event)->window !=
1526                     item->canvas->layout.bin_window)
1527                         return FALSE;
1528
1529                 if (((GdkEventFocus *) event)->in)
1530                         return gnome_canvas_rich_text_focus_in_event(
1531                                 item, (GdkEventFocus *) event);
1532                 else
1533                         return gnome_canvas_rich_text_focus_out_event(
1534                                 item, (GdkEventFocus *) event);
1535         default:
1536                 return FALSE;
1537         }
1538 } /* gnome_canvas_rich_text_event */
1539
1540 /* Cut/Copy/Paste */
1541
1542 /**
1543  * gnome_canvas_rich_text_cut_clipboard:
1544  * @text: a #GnomeCanvasRichText.
1545  * 
1546  * Copies the currently selected @text to clipboard, then deletes said text
1547  * if it's editable.
1548  **/
1549 void
1550 gnome_canvas_rich_text_cut_clipboard(GnomeCanvasRichText *text)
1551 {
1552         g_return_if_fail(text);
1553         g_return_if_fail(get_buffer(text));
1554
1555         gtk_text_buffer_cut_clipboard(get_buffer(text),
1556                                       gtk_clipboard_get (GDK_SELECTION_PRIMARY),
1557                                       text->_priv->editable);
1558 } /* gnome_canvas_rich_text_cut_clipboard */
1559
1560
1561 /**
1562  * gnome_canvas_rich_text_copy_clipboard:
1563  * @text: a #GnomeCanvasRichText.
1564  *
1565  * Copies the currently selected @text to clipboard.
1566  **/
1567 void
1568 gnome_canvas_rich_text_copy_clipboard(GnomeCanvasRichText *text)
1569 {
1570         g_return_if_fail(text);
1571         g_return_if_fail(get_buffer(text));
1572
1573         gtk_text_buffer_copy_clipboard(get_buffer(text),
1574                                        gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1575 } /* gnome_canvas_rich_text_cut_clipboard */
1576
1577
1578 /**
1579  * gnome_canvas_rich_text_paste_clipboard:
1580  * @text: a #GnomeCanvasRichText.
1581  *
1582  * Pastes the contents of the clipboard at the insertion point.
1583  **/
1584 void
1585 gnome_canvas_rich_text_paste_clipboard(GnomeCanvasRichText *text)
1586 {
1587         g_return_if_fail(text);
1588         g_return_if_fail(get_buffer(text));
1589
1590         gtk_text_buffer_paste_clipboard(get_buffer(text),
1591                                         gtk_clipboard_get (GDK_SELECTION_PRIMARY),
1592                                         NULL,
1593                                         text->_priv->editable);
1594 } /* gnome_canvas_rich_text_cut_clipboard */
1595
1596 static gint
1597 preblink_cb(gpointer data)
1598 {
1599         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1600
1601         text->_priv->preblink_timeout = 0;
1602         gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
1603
1604         /* Remove ourselves */
1605         return FALSE;
1606 } /* preblink_cb */
1607
1608 static gint
1609 blink_cb(gpointer data)
1610 {
1611         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1612         gboolean visible;
1613
1614         g_assert(text->_priv->layout);
1615         g_assert(text->_priv->cursor_visible);
1616
1617         visible = gtk_text_layout_get_cursor_visible(text->_priv->layout);
1618         if (visible)
1619                 text->_priv->blink_timeout = g_timeout_add(
1620                         CURSOR_OFF_TIME, blink_cb, text);
1621         else
1622                 text->_priv->blink_timeout = g_timeout_add(
1623                         CURSOR_ON_TIME, blink_cb, text);
1624
1625         gtk_text_layout_set_cursor_visible(text->_priv->layout, !visible);
1626
1627         /* Remove ourself */
1628         return FALSE;
1629 } /* blink_cb */
1630
1631 static void
1632 gnome_canvas_rich_text_start_cursor_blink(GnomeCanvasRichText *text,
1633                                           gboolean with_delay)
1634 {
1635         if (!text->_priv->layout)
1636                 return;
1637
1638         if (!text->_priv->cursor_visible || !text->_priv->cursor_blink)
1639                 return;
1640
1641         if (text->_priv->preblink_timeout != 0) {
1642                 g_source_remove(text->_priv->preblink_timeout);
1643                 text->_priv->preblink_timeout = 0;
1644         }
1645
1646         if (with_delay) {
1647                 if (text->_priv->blink_timeout != 0) {
1648                         g_source_remove(text->_priv->blink_timeout);
1649                         text->_priv->blink_timeout = 0;
1650                 }
1651
1652                 gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
1653
1654                 text->_priv->preblink_timeout = g_timeout_add(
1655                         PREBLINK_TIME, preblink_cb, text);
1656         }
1657         else {
1658                 if (text->_priv->blink_timeout == 0) {
1659                         gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
1660                         text->_priv->blink_timeout = g_timeout_add(
1661                                 CURSOR_ON_TIME, blink_cb, text);
1662                 }
1663         }
1664 } /* gnome_canvas_rich_text_start_cursor_blink */
1665
1666 static void
1667 gnome_canvas_rich_text_stop_cursor_blink(GnomeCanvasRichText *text)
1668 {
1669         if (text->_priv->blink_timeout) {
1670                 g_source_remove(text->_priv->blink_timeout);
1671                 text->_priv->blink_timeout = 0;
1672         }
1673 } /* gnome_canvas_rich_text_stop_cursor_blink */
1674
1675 /* We have to request updates this way because the update cycle is not
1676    re-entrant. This will fire off a request in an idle loop. */
1677 static gboolean
1678 request_update(gpointer data)
1679 {
1680         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1681
1682         gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
1683
1684         return FALSE;
1685 } /* request_update */
1686
1687 static void
1688 invalidated_handler(GtkTextLayout * layout, gpointer data)
1689 {
1690         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1691
1692 #if 0
1693         printf("Text is being invalidated.\n");
1694 #endif
1695
1696         gtk_text_layout_validate(text->_priv->layout, 2000);
1697
1698         /* We are called from the update cycle; gotta put this in an idle
1699            loop. */
1700         g_idle_add(request_update, text);
1701 } /* invalidated_handler */
1702
1703 static void
1704 scale_fonts(GtkTextTag *tag, gpointer data)
1705 {
1706         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1707
1708         if (!tag->values)
1709                 return;
1710
1711         g_object_set(
1712                 G_OBJECT(tag), "scale", 
1713                 text->_priv->layout->default_style->font_scale, NULL);
1714 } /* scale_fonts */
1715
1716 static void
1717 changed_handler(GtkTextLayout * layout, gint start_y, 
1718                 gint old_height, gint new_height, gpointer data)
1719 {
1720         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1721
1722 #if 0
1723         printf("Layout %p is being changed.\n", text->_priv->layout);
1724 #endif
1725
1726         if (text->_priv->layout->default_style->font_scale != 
1727             GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit) {
1728                 GtkTextTagTable *tag_table;
1729
1730                 text->_priv->layout->default_style->font_scale = 
1731                         GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
1732
1733                 tag_table = gtk_text_buffer_get_tag_table(get_buffer(text));
1734                 gtk_text_tag_table_foreach(tag_table, scale_fonts, text);
1735
1736                 gtk_text_layout_default_style_changed(text->_priv->layout);
1737         }
1738
1739         if (text->_priv->grow_height) {
1740                 int width, height;
1741
1742                 gtk_text_layout_get_size(text->_priv->layout, &width, &height);
1743
1744                 if (height > text->_priv->height)
1745                         text->_priv->height = height;
1746         }
1747
1748         /* We are called from the update cycle; gotta put this in an idle
1749            loop. */
1750         g_idle_add(request_update, text);
1751 } /* changed_handler */
1752
1753
1754 /**
1755  * gnome_canvas_rich_text_set_buffer:
1756  * @text: a #GnomeCanvasRichText.
1757  * @buffer: a #GtkTextBuffer.
1758  *
1759  * Sets the buffer field of the @text to @buffer. 
1760  **/ 
1761 void
1762 gnome_canvas_rich_text_set_buffer(GnomeCanvasRichText *text, 
1763                                   GtkTextBuffer *buffer)
1764 {
1765         g_return_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text));
1766         g_return_if_fail(buffer == NULL || GTK_IS_TEXT_BUFFER(buffer));
1767
1768         if (text->_priv->buffer == buffer)
1769                 return;
1770
1771         if (text->_priv->buffer != NULL) {
1772                 g_object_unref(G_OBJECT(text->_priv->buffer));
1773         }
1774
1775         text->_priv->buffer = buffer;
1776
1777         if (buffer) {
1778                 g_object_ref(G_OBJECT(buffer));
1779
1780                 if (text->_priv->layout)
1781                         gtk_text_layout_set_buffer(text->_priv->layout, buffer);
1782         }
1783
1784         gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
1785 } /* gnome_canvas_rich_text_set_buffer */
1786
1787 static GtkTextBuffer *
1788 get_buffer(GnomeCanvasRichText *text)
1789 {
1790         if (!text->_priv->buffer) {
1791                 GtkTextBuffer *b;
1792
1793                 b = gtk_text_buffer_new(NULL);
1794                 gnome_canvas_rich_text_set_buffer(text, b);
1795                 g_object_unref(G_OBJECT(b));
1796         }
1797
1798         return text->_priv->buffer;
1799 } /* get_buffer */
1800
1801
1802 /**
1803  * gnome_canvas_rich_text_get_buffer:
1804  * @text: a #GnomeCanvasRichText.
1805  *
1806  * Returns a #GtkTextBuffer associated with the #GnomeCanvasRichText.
1807  * This function creates a new #GtkTextBuffer if the text buffer is NULL.
1808  *
1809  * Return value: the #GtkTextBuffer.
1810  **/
1811 GtkTextBuffer *
1812 gnome_canvas_rich_text_get_buffer(GnomeCanvasRichText *text)
1813 {
1814         g_return_val_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text), NULL);
1815
1816         return get_buffer(text);
1817 } /* gnome_canvas_rich_text_get_buffer */
1818
1819
1820 /**
1821  * gnome_canvas_rich_text_get_iter_location:
1822  * @text: a #GnomeCanvasRichText.
1823  * @iter: a #GtkTextIter.
1824  * @location: a #GdkRectangle containing the bounds of the character at @iter.
1825  *
1826  * Gets a rectangle which roughly contains the character at @iter.
1827  **/
1828 void
1829 gnome_canvas_rich_text_get_iter_location (GnomeCanvasRichText *text,
1830                                           const GtkTextIter *iter,
1831                                           GdkRectangle      *location)
1832 {
1833   g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
1834   g_return_if_fail (gtk_text_iter_get_buffer (iter) == text->_priv->buffer);
1835
1836   gtk_text_layout_get_iter_location (text->_priv->layout, iter, location);
1837 }
1838
1839
1840 /**
1841  * gnome_canvas_rich_text_get_iter_at_location:
1842  * @text: a #GnomeCanvasRichText.
1843  * @iter: a #GtkTextIter.
1844  * @x: x position, in buffer coordinates.
1845  * @y: y position, in buffer coordinates.
1846  *
1847  * Retrieves the iterator at the buffer coordinates x and y. 
1848  **/ 
1849 void
1850 gnome_canvas_rich_text_get_iter_at_location (GnomeCanvasRichText *text,
1851                                     GtkTextIter *iter,
1852                                     gint         x,
1853                                     gint         y)
1854 {
1855   g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
1856   g_return_if_fail (iter != NULL);
1857   g_return_if_fail (text->_priv->layout != NULL);
1858
1859   gtk_text_layout_get_iter_at_pixel (text->_priv->layout,
1860                                      iter,
1861                                      x,
1862                                      y);
1863 }
1864
1865
1866 static void
1867 gnome_canvas_rich_text_set_attributes_from_style(GnomeCanvasRichText * text,
1868                                                  GtkTextAttributes *values,
1869                                                  GtkStyle *style)
1870 {
1871         values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
1872         values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
1873         
1874         if (values->font)
1875                 pango_font_description_free (values->font);
1876
1877         values->font = pango_font_description_copy (style->font_desc);
1878
1879 } /* gnome_canvas_rich_text_set_attributes_from_style */
1880
1881 static void
1882 gnome_canvas_rich_text_ensure_layout(GnomeCanvasRichText *text)
1883 {
1884         if (!text->_priv->layout) {
1885                 GtkWidget *canvas;
1886                 GtkTextAttributes *style;
1887                 PangoContext *ltr_context, *rtl_context;
1888
1889                 text->_priv->layout = gtk_text_layout_new();
1890
1891                 gtk_text_layout_set_screen_width(text->_priv->layout, text->_priv->width);
1892
1893                 if (get_buffer(text)) {
1894                         gtk_text_layout_set_buffer(
1895                                 text->_priv->layout, get_buffer(text));
1896                 }
1897
1898                 /* Setup the cursor stuff */
1899                 gtk_text_layout_set_cursor_visible(
1900                         text->_priv->layout, text->_priv->cursor_visible);
1901                 if (text->_priv->cursor_visible && text->_priv->cursor_blink)
1902                         gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
1903                 else
1904                         gnome_canvas_rich_text_stop_cursor_blink(text);
1905
1906                 canvas = GTK_WIDGET(GNOME_CANVAS_ITEM(text)->canvas);
1907
1908                 ltr_context = gtk_widget_create_pango_context(canvas);
1909                 pango_context_set_base_dir(ltr_context, PANGO_DIRECTION_LTR);
1910                 rtl_context = gtk_widget_create_pango_context(canvas);
1911                 pango_context_set_base_dir(rtl_context, PANGO_DIRECTION_RTL);
1912
1913                 gtk_text_layout_set_contexts(
1914                         text->_priv->layout, ltr_context, rtl_context);
1915
1916                 g_object_unref(G_OBJECT(ltr_context));
1917                 g_object_unref(G_OBJECT(rtl_context));
1918
1919                 style = gtk_text_attributes_new();
1920
1921                 gnome_canvas_rich_text_set_attributes_from_style(
1922                         text, style, canvas->style);
1923
1924                 style->pixels_above_lines = text->_priv->pixels_above_lines;
1925                 style->pixels_below_lines = text->_priv->pixels_below_lines;
1926                 style->pixels_inside_wrap = text->_priv->pixels_inside_wrap;
1927                 style->left_margin = text->_priv->left_margin;
1928                 style->right_margin = text->_priv->right_margin;
1929                 style->indent = text->_priv->indent;
1930                 style->tabs = NULL;
1931                 style->wrap_mode = text->_priv->wrap_mode;
1932                 style->justification = text->_priv->justification;
1933                 style->direction = text->_priv->direction;
1934                 style->editable = text->_priv->editable;
1935                 style->invisible = !text->_priv->visible;
1936
1937                 gtk_text_layout_set_default_style(text->_priv->layout, style);
1938
1939                 gtk_text_attributes_unref(style);
1940
1941                 g_signal_connect(
1942                         G_OBJECT(text->_priv->layout), "invalidated",
1943                         G_CALLBACK (invalidated_handler), text);
1944
1945                 g_signal_connect(
1946                         G_OBJECT(text->_priv->layout), "changed",
1947                         G_CALLBACK (changed_handler), text);
1948         }
1949 } /* gnome_canvas_rich_text_ensure_layout */
1950
1951 static void
1952 gnome_canvas_rich_text_destroy_layout(GnomeCanvasRichText *text)
1953 {
1954         if (text->_priv->layout) {
1955                 g_signal_handlers_disconnect_by_func(
1956                         G_OBJECT(text->_priv->layout), invalidated_handler, text);
1957                 g_signal_handlers_disconnect_by_func(
1958                         G_OBJECT(text->_priv->layout), changed_handler, text);
1959                 g_object_unref(G_OBJECT(text->_priv->layout));
1960                 text->_priv->layout = NULL;
1961         }
1962 } /* gnome_canvas_rich_text_destroy_layout */
1963
1964 static void
1965 adjust_for_anchors(GnomeCanvasRichText *text, double *ax, double *ay)
1966 {
1967         double x, y;
1968
1969         x = text->_priv->x;
1970         y = text->_priv->y;
1971
1972         /* Anchor text */
1973         /* X coordinates */
1974         switch (text->_priv->anchor) {
1975         case GTK_ANCHOR_NW:
1976         case GTK_ANCHOR_W:
1977         case GTK_ANCHOR_SW:
1978                 break;
1979
1980         case GTK_ANCHOR_N:
1981         case GTK_ANCHOR_CENTER:
1982         case GTK_ANCHOR_S:
1983                 x -= text->_priv->width / 2;
1984                 break;
1985
1986         case GTK_ANCHOR_NE:
1987         case GTK_ANCHOR_E:
1988         case GTK_ANCHOR_SE:
1989                 x -= text->_priv->width;
1990                 break;
1991         default:
1992                 break;
1993         }
1994
1995         /* Y coordinates */
1996         switch (text->_priv->anchor) {
1997         case GTK_ANCHOR_NW:
1998         case GTK_ANCHOR_N:
1999         case GTK_ANCHOR_NE:
2000                 break;
2001
2002         case GTK_ANCHOR_W:
2003         case GTK_ANCHOR_CENTER:
2004         case GTK_ANCHOR_E:
2005                 y -= text->_priv->height / 2;
2006                 break;
2007
2008         case GTK_ANCHOR_SW:
2009         case GTK_ANCHOR_S:
2010         case GTK_ANCHOR_SE:
2011                 y -= text->_priv->height;
2012                 break;
2013         default:
2014                 break;
2015         }
2016
2017         if (ax)
2018                 *ax = x;
2019         if (ay)
2020                 *ay = y;
2021 } /* adjust_for_anchors */
2022
2023 static void
2024 get_bounds(GnomeCanvasRichText *text, double *px1, double *py1,
2025            double *px2, double *py2)
2026 {
2027         GnomeCanvasItem *item = GNOME_CANVAS_ITEM(text);
2028         double x, y;
2029         double x1, x2, y1, y2;
2030         int cx1, cx2, cy1, cy2;
2031
2032         adjust_for_anchors(text, &x, &y);
2033
2034         x1 = x;
2035         y1 = y;
2036         x2 = x + text->_priv->width;
2037         y2 = y + text->_priv->height;
2038
2039         gnome_canvas_item_i2w(item, &x1, &y1);
2040         gnome_canvas_item_i2w(item, &x2, &y2);
2041         gnome_canvas_w2c(item->canvas, x1, y1, &cx1, &cy1);
2042         gnome_canvas_w2c(item->canvas, x2, y2, &cx2, &cy2);
2043
2044         *px1 = cx1;
2045         *py1 = cy1;
2046         *px2 = cx2;
2047         *py2 = cy2;
2048 } /* get_bounds */
2049
2050 static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *item, double *px1, double *py1,
2051            double *px2, double *py2)
2052 {
2053         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
2054         get_bounds (text, px1, py1, px2, py2);
2055 }
2056
2057 static void
2058 gnome_canvas_rich_text_update(GnomeCanvasItem *item, double *affine,
2059                               ArtSVP *clip_path, int flags)
2060 {
2061         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
2062         double x1, y1, x2, y2;
2063         GtkTextIter start;
2064
2065         (* GNOME_CANVAS_ITEM_CLASS(parent_class)->update)(
2066                 item, affine, clip_path, flags);
2067
2068         get_bounds(text, &x1, &y1, &x2, &y2);
2069
2070         gtk_text_buffer_get_iter_at_offset(text->_priv->buffer, &start, 0);
2071         if (text->_priv->layout)
2072                 gtk_text_layout_validate_yrange(
2073                         text->_priv->layout, &start, 0, y2 - y1);
2074
2075         gnome_canvas_update_bbox(item, x1, y1, x2, y2);
2076 } /* gnome_canvas_rich_text_update */
2077                                                   
2078 static double
2079 gnome_canvas_rich_text_point(GnomeCanvasItem *item, double x, double y,
2080                              int cx, int cy, GnomeCanvasItem **actual_item)
2081 {
2082         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
2083         double ax, ay;
2084         double x1, x2, y1, y2;
2085         double dx, dy;
2086
2087         *actual_item = item;
2088
2089         /* This is a lame cop-out. Anywhere inside of the bounding box. */
2090
2091         adjust_for_anchors(text, &ax, &ay);
2092
2093         x1 = ax;
2094         y1 = ay;
2095         x2 = ax + text->_priv->width;
2096         y2 = ay + text->_priv->height;
2097
2098         if ((x > x1) && (y > y1) && (x < x2) && (y < y2))
2099                 return 0.0;
2100
2101         if (x < x1)
2102                 dx = x1 - x;
2103         else if (x > x2)
2104                 dx = x - x2;
2105         else
2106                 dx = 0.0;
2107
2108         if (y < y1)
2109                 dy = y1 - y;
2110         else if (y > y2)
2111                 dy = y - y2;
2112         else
2113                 dy = 0.0;
2114
2115         return sqrt(dx * dx + dy * dy);
2116 } /* gnome_canvas_rich_text_point */
2117
2118 static void
2119 gnome_canvas_rich_text_draw(GnomeCanvasItem *item, GdkDrawable *drawable,
2120                             int x, int y, int width, int height)
2121 {
2122         GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
2123         double i2w[6], w2c[6], i2c[6];
2124         double ax, ay;
2125         int x1, y1, x2, y2;
2126         ArtPoint i1, i2;
2127         ArtPoint c1, c2;
2128
2129         gnome_canvas_item_i2w_affine(item, i2w);
2130         gnome_canvas_w2c_affine(item->canvas, w2c);
2131         art_affine_multiply(i2c, i2w, w2c);
2132
2133         adjust_for_anchors(text, &ax, &ay);
2134
2135         i1.x = ax;
2136         i1.y = ay;
2137         i2.x = ax + text->_priv->width;
2138         i2.y = ay + text->_priv->height;
2139         art_affine_point(&c1, &i1, i2c);
2140         art_affine_point(&c2, &i2, i2c);
2141
2142         x1 = c1.x;
2143         y1 = c1.y;
2144         x2 = c2.x;
2145         y2 = c2.y;
2146
2147         gtk_text_layout_set_screen_width(text->_priv->layout, x2 - x1);
2148       
2149         /* FIXME: should last arg be NULL? */
2150         gtk_text_layout_draw(
2151                 text->_priv->layout,
2152                 GTK_WIDGET(item->canvas),
2153                 drawable,
2154                 GTK_WIDGET (item->canvas)->style->text_gc[GTK_STATE_NORMAL],
2155                 x - x1, y - y1,
2156                 0, 0, (x2 - x1) - (x - x1), (y2 - y1) - (y - y1),
2157                 NULL);
2158 } /* gnome_canvas_rich_text_draw */
2159
2160 static void
2161 gnome_canvas_rich_text_render(GnomeCanvasItem *item, GnomeCanvasBuf *buf)
2162 {
2163         g_warning ("rich text item not implemented for anti-aliased canvas");
2164 } /* gnome_canvas_rich_text_render */
2165
2166 #if 0
2167 static GtkTextTag *
2168 gnome_canvas_rich_text_add_tag(GnomeCanvasRichText *text, char *tag_name,
2169                                int start_offset, int end_offset, 
2170                                const char *first_property_name, ...)
2171 {
2172         GtkTextTag *tag;
2173         GtkTextIter start, end;
2174         va_list var_args;
2175
2176         g_return_val_if_fail(text, NULL);
2177         g_return_val_if_fail(start_offset >= 0, NULL);
2178         g_return_val_if_fail(end_offset >= 0, NULL);
2179
2180         if (tag_name) {
2181                 GtkTextTagTable *tag_table;
2182
2183                 tag_table = gtk_text_buffer_get_tag_table(get_buffer(text));
2184                 g_return_val_if_fail(gtk_text_tag_table_lookup(tag_table, tag_name) == NULL, NULL);
2185         }
2186
2187         tag = gtk_text_buffer_create_tag(
2188                 get_buffer(text), tag_name, NULL);
2189
2190         va_start(var_args, first_property_name);
2191         g_object_set_valist(G_OBJECT(tag), first_property_name, var_args);
2192         va_end(var_args);
2193
2194         gtk_text_buffer_get_iter_at_offset(
2195                 get_buffer(text), &start, start_offset);
2196         gtk_text_buffer_get_iter_at_offset(
2197                 get_buffer(text), &end, end_offset);
2198         gtk_text_buffer_apply_tag(get_buffer(text), tag, &start, &end);
2199
2200         return tag;
2201 } /* gnome_canvas_rich_text_add_tag */
2202 #endif