source: trunk/third/libgnomecanvas/libgnomecanvas/gnome-canvas-rich-text.c @ 20821

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