1 /* Editable GnomeCanvas text item based on GtkTextLayout, borrowed heavily
4 * Copyright (c) 2000 Red Hat, Inc.
5 * Copyright (c) 2001 Joe Shaw
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.
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.
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.
27 #include <gdk/gdkkeysyms.h>
29 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
30 #include <gtk/gtktextdisplay.h>
32 #include "gnome-canvas.h"
33 #include "gnome-canvas-util.h"
34 #include "gnome-canvas-rich-text.h"
35 #include "gnome-canvas-i18n.h"
37 struct _GnomeCanvasRichTextPrivate {
38 GtkTextLayout *layout;
39 GtkTextBuffer *buffer;
43 /* Position at anchor */
47 /* Top-left canvas coordinates for text */
50 gboolean cursor_visible;
51 gboolean cursor_blink;
55 GtkWrapMode wrap_mode;
56 GtkJustification justification;
57 GtkTextDirection direction;
59 int pixels_above_lines;
60 int pixels_below_lines;
61 int pixels_inside_wrap;
66 guint preblink_timeout;
69 guint selection_drag_handler;
74 gboolean just_selected_element;
96 PROP_PIXELS_ABOVE_LINES,
97 PROP_PIXELS_BELOW_LINES,
98 PROP_PIXELS_INSIDE_WRAP,
109 static GnomeCanvasItemClass *parent_class;
110 static guint signals[LAST_SIGNAL] = { 0 };
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,
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,
133 static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *text, double *px1, double *py1,
134 double *px2, double *py2);
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,
143 gboolean extend_selection);
147 static GtkTextBuffer *get_buffer(GnomeCanvasRichText *text);
148 static gint blink_cb(gpointer data);
150 #define PREBLINK_TIME 300
151 #define CURSOR_ON_TIME 800
152 #define CURSOR_OFF_TIME 400
155 gnome_canvas_rich_text_get_type(void)
157 static GType rich_text_type;
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),
169 (GInstanceInitFunc) gnome_canvas_rich_text_init,
170 NULL /* value_table */
173 rich_text_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasRichText",
177 return rich_text_type;
181 gnome_canvas_rich_text_finalize(GObject *object)
183 GnomeCanvasRichText *text;
185 text = GNOME_CANVAS_RICH_TEXT(object);
187 g_free (text->_priv);
190 if (G_OBJECT_CLASS (parent_class)->finalize)
191 G_OBJECT_CLASS (parent_class)->finalize (object);
195 gnome_canvas_rich_text_class_init(GnomeCanvasRichTextClass *klass)
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);
201 parent_class = g_type_class_peek_parent (klass);
203 gobject_class->set_property = gnome_canvas_rich_text_set_property;
204 gobject_class->get_property = gnome_canvas_rich_text_get_property;
206 g_object_class_install_property (
209 g_param_spec_string ("text",
211 _("Text to display"),
214 g_object_class_install_property (
217 g_param_spec_double ("x",
220 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
222 g_object_class_install_property (
225 g_param_spec_double ("y",
228 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
230 g_object_class_install_property (
233 g_param_spec_double ("width",
235 _("Width for text box"),
236 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
238 g_object_class_install_property (
241 g_param_spec_double ("height",
243 _("Height for text box"),
244 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
246 g_object_class_install_property (
249 g_param_spec_boolean ("editable",
251 _("Is this rich text item editable?"),
254 g_object_class_install_property (
257 g_param_spec_boolean ("visible",
259 _("Is this rich text item visible?"),
262 g_object_class_install_property (
265 g_param_spec_boolean ("cursor_visible",
267 _("Is the cursor visible in this rich text item?"),
270 g_object_class_install_property (
273 g_param_spec_boolean ("cursor_blink",
275 _("Does the cursor blink in this rich text item?"),
278 g_object_class_install_property (
281 g_param_spec_boolean ("grow_height",
283 _("Should the text box height grow if the text does not fit?"),
286 g_object_class_install_property (
289 g_param_spec_enum ("wrap_mode",
291 _("Wrap mode for multiline text"),
295 g_object_class_install_property (
298 g_param_spec_enum ("justification",
300 _("Justification mode"),
301 GTK_TYPE_JUSTIFICATION,
304 g_object_class_install_property (
307 g_param_spec_enum ("direction",
310 GTK_TYPE_DIRECTION_TYPE,
311 gtk_widget_get_default_direction (),
313 g_object_class_install_property (
316 g_param_spec_enum ("anchor",
318 _("Anchor point for text"),
319 GTK_TYPE_ANCHOR_TYPE,
322 g_object_class_install_property (
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"),
331 g_object_class_install_property (
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"),
340 g_object_class_install_property (
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"),
349 g_object_class_install_property (
352 g_param_spec_int ("left_margin",
354 _("Number of pixels in the left margin"),
358 g_object_class_install_property (
361 g_param_spec_int ("right_margin",
363 _("Number of pixels in the right margin"),
367 g_object_class_install_property (
370 g_param_spec_int ("indent",
372 _("Number of pixels for indentation"),
378 signals[TAG_CHANGED] = g_signal_new(
380 G_TYPE_FROM_CLASS(object_class),
382 G_STRUCT_OFFSET(GnomeCanvasRichTextClass, tag_changed),
384 g_cclosure_marshal_VOID__OBJECT,
388 gobject_class->finalize = gnome_canvas_rich_text_finalize;
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 */
401 gnome_canvas_rich_text_init(GnomeCanvasRichText *text)
404 GtkObject *object = GTK_OBJECT(text);
406 object->flags |= GNOME_CANVAS_ITEM_ALWAYS_REDRAW;
408 text->_priv = g_new0(GnomeCanvasRichTextPrivate, 1);
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;
421 text->_priv->blink_timeout = 0;
422 text->_priv->preblink_timeout = 0;
424 text->_priv->clicks = 0;
425 text->_priv->click_timeout = 0;
426 } /* gnome_canvas_rich_text_init */
429 gnome_canvas_rich_text_set_property (GObject *object, guint property_id,
430 const GValue *value, GParamSpec *pspec)
432 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
434 switch (property_id) {
436 if (text->_priv->text)
437 g_free(text->_priv->text);
439 text->_priv->text = g_value_dup_string (value);
441 gtk_text_buffer_set_text(
442 get_buffer(text), text->_priv->text, strlen(text->_priv->text));
446 text->_priv->x = g_value_get_double (value);
449 text->_priv->y = g_value_get_double (value);
452 text->_priv->width = g_value_get_double (value);
455 text->_priv->height = g_value_get_double (value);
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);
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);
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);
479 if (text->_priv->cursor_visible && text->_priv->cursor_blink) {
480 gnome_canvas_rich_text_start_cursor_blink(
484 gnome_canvas_rich_text_stop_cursor_blink(text);
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(
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);
501 case PROP_GROW_HEIGHT:
502 text->_priv->grow_height = g_value_get_boolean (value);
503 /* FIXME: Recalc here */
506 text->_priv->wrap_mode = g_value_get_enum (value);
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);
514 case PROP_JUSTIFICATION:
515 text->_priv->justification = g_value_get_enum (value);
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);
524 text->_priv->direction = g_value_get_enum (value);
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);
533 text->_priv->anchor = g_value_get_enum (value);
535 case PROP_PIXELS_ABOVE_LINES:
536 text->_priv->pixels_above_lines = g_value_get_int (value);
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);
544 case PROP_PIXELS_BELOW_LINES:
545 text->_priv->pixels_below_lines = g_value_get_int (value);
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);
553 case PROP_PIXELS_INSIDE_WRAP:
554 text->_priv->pixels_inside_wrap = g_value_get_int (value);
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);
562 case PROP_LEFT_MARGIN:
563 text->_priv->left_margin = g_value_get_int (value);
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);
571 case PROP_RIGHT_MARGIN:
572 text->_priv->right_margin = g_value_get_int (value);
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);
581 text->_priv->pixels_above_lines = g_value_get_int (value);
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);
590 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
594 gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
598 gnome_canvas_rich_text_get_property (GObject *object, guint property_id,
599 GValue *value, GParamSpec *pspec)
601 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
603 switch (property_id) {
605 g_value_set_string (value, text->_priv->text);
608 g_value_set_double (value, text->_priv->x);
611 g_value_set_double (value, text->_priv->y);
614 g_value_set_double (value, text->_priv->height);
617 g_value_set_double (value, text->_priv->width);
620 g_value_set_boolean (value, text->_priv->editable);
622 case PROP_CURSOR_VISIBLE:
623 g_value_set_boolean (value, text->_priv->cursor_visible);
625 case PROP_CURSOR_BLINK:
626 g_value_set_boolean (value, text->_priv->cursor_blink);
628 case PROP_GROW_HEIGHT:
629 g_value_set_boolean (value, text->_priv->grow_height);
632 g_value_set_enum (value, text->_priv->wrap_mode);
634 case PROP_JUSTIFICATION:
635 g_value_set_enum (value, text->_priv->justification);
638 g_value_set_enum (value, text->_priv->direction);
641 g_value_set_enum (value, text->_priv->anchor);
643 case PROP_PIXELS_ABOVE_LINES:
644 g_value_set_enum (value, text->_priv->pixels_above_lines);
646 case PROP_PIXELS_BELOW_LINES:
647 g_value_set_int (value, text->_priv->pixels_below_lines);
649 case PROP_PIXELS_INSIDE_WRAP:
650 g_value_set_int (value, text->_priv->pixels_inside_wrap);
652 case PROP_LEFT_MARGIN:
653 g_value_set_int (value, text->_priv->left_margin);
655 case PROP_RIGHT_MARGIN:
656 g_value_set_int (value, text->_priv->right_margin);
659 g_value_set_int (value, text->_priv->indent);
662 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
668 gnome_canvas_rich_text_realize(GnomeCanvasItem *item)
670 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
672 (* GNOME_CANVAS_ITEM_CLASS(parent_class)->realize)(item);
674 gnome_canvas_rich_text_ensure_layout(text);
675 } /* gnome_canvas_rich_text_realize */
678 gnome_canvas_rich_text_unrealize(GnomeCanvasItem *item)
680 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
682 gnome_canvas_rich_text_destroy_layout(text);
684 (* GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize)(item);
685 } /* gnome_canvas_rich_text_unrealize */
688 gnome_canvas_rich_text_move_iter_by_lines(GnomeCanvasRichText *text,
689 GtkTextIter *newplace, gint count)
692 gtk_text_layout_move_iter_to_previous_line(
693 text->_priv->layout, newplace);
698 gtk_text_layout_move_iter_to_next_line(
699 text->_priv->layout, newplace);
702 } /* gnome_canvas_rich_text_move_iter_by_lines */
705 gnome_canvas_rich_text_get_cursor_x_position(GnomeCanvasRichText *text)
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);
717 } /* gnome_canvas_rich_text_get_cursor_x_position */
720 gnome_canvas_rich_text_move_cursor(GnomeCanvasRichText *text,
721 GtkMovementStep step,
722 gint count, gboolean extend_selection)
724 GtkTextIter insert, newplace;
726 gtk_text_buffer_get_iter_at_mark(
727 get_buffer(text), &insert,
728 gtk_text_buffer_get_mark(get_buffer(text), "insert"));
733 case GTK_MOVEMENT_LOGICAL_POSITIONS:
734 gtk_text_iter_forward_cursor_positions(&newplace, count);
736 case GTK_MOVEMENT_VISUAL_POSITIONS:
737 gtk_text_layout_move_iter_visually(
738 text->_priv->layout, &newplace, count);
740 case GTK_MOVEMENT_WORDS:
742 gtk_text_iter_backward_word_starts(&newplace, -count);
744 gtk_text_iter_forward_word_ends(&newplace, count);
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));
753 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
755 gnome_canvas_rich_text_move_iter_by_lines(
756 text, &newplace, --count);
758 else if (count < -1) {
759 gnome_canvas_rich_text_move_iter_by_lines(
760 text, &newplace, ++count);
764 gtk_text_layout_move_iter_to_line_end(
765 text->_priv->layout, &newplace, count);
768 case GTK_MOVEMENT_PARAGRAPHS:
769 /* FIXME: Busted in gtktextview.c too */
771 case GTK_MOVEMENT_PARAGRAPH_ENDS:
773 gtk_text_iter_forward_to_line_end(&newplace);
775 gtk_text_iter_set_line_offset(&newplace, 0);
777 case GTK_MOVEMENT_BUFFER_ENDS:
779 gtk_text_buffer_get_end_iter(
780 get_buffer(text), &newplace);
782 else if (count < 0) {
783 gtk_text_buffer_get_iter_at_offset(
784 get_buffer(text), &newplace, 0);
791 if (!gtk_text_iter_equal(&insert, &newplace)) {
792 if (extend_selection) {
793 gtk_text_buffer_move_mark(
795 gtk_text_buffer_get_mark(
796 get_buffer(text), "insert"),
800 gtk_text_buffer_place_cursor(
801 get_buffer(text), &newplace);
805 gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
806 } /* gnome_canvas_rich_text_move_cursor */
809 whitespace(gunichar ch, gpointer ignored)
811 return (ch == ' ' || ch == '\t');
815 not_whitespace(gunichar ch, gpointer ignored)
817 return !whitespace(ch, ignored);
818 } /* not_whitespace */
821 find_whitespace_region(const GtkTextIter *center,
822 GtkTextIter *start, GtkTextIter *end)
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);
832 return !gtk_text_iter_equal(start, end);
833 } /* find_whitespace_region */
836 gnome_canvas_rich_text_delete_from_cursor(GnomeCanvasRichText *text,
840 GtkTextIter insert, start, end;
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))
850 gtk_text_buffer_get_iter_at_mark(
851 get_buffer(text), &insert,
852 gtk_text_buffer_get_mark(get_buffer(text), "insert"));
858 case GTK_DELETE_CHARS:
859 gtk_text_iter_forward_cursor_positions(&end, count);
861 case GTK_DELETE_WORD_ENDS:
863 gtk_text_iter_forward_word_ends(&end, count);
865 gtk_text_iter_backward_word_starts(&start, -count);
867 case GTK_DELETE_WORDS:
869 case GTK_DELETE_DISPLAY_LINE_ENDS:
871 case GTK_DELETE_PARAGRAPH_ENDS:
872 if (gtk_text_iter_ends_line(&end)) {
873 gtk_text_iter_forward_line(&end);
878 if (!gtk_text_iter_forward_to_line_end(&end))
884 case GTK_DELETE_PARAGRAPHS:
886 gtk_text_iter_set_line_offset(&start, 0);
887 gtk_text_iter_forward_to_line_end(&end);
889 /* Do the lines beyond the first. */
891 gtk_text_iter_forward_to_line_end(&end);
896 case GTK_DELETE_WHITESPACE:
897 find_whitespace_region(&insert, &start, &end);
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));
910 } /* gnome_canvas_rich_text_delete_from_cursor */
913 selection_motion_event_handler(GnomeCanvasRichText *text, GdkEvent *event,
916 GtkTextIter newplace;
920 /* We only want to handle motion events... */
921 if (event->type != GDK_MOTION_NOTIFY)
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;
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);
934 } /* selection_motion_event_handler */
937 gnome_canvas_rich_text_start_selection_drag(GnomeCanvasRichText *text,
938 const GtkTextIter *iter,
939 GdkEventButton * button)
941 GtkTextIter newplace;
943 g_return_if_fail(text->_priv->selection_drag_handler == 0);
946 gnome_canvas_item_grab_focus(GNOME_CANVAS_ITEM(text));
951 gtk_text_buffer_place_cursor(get_buffer(text), &newplace);
953 text->_priv->selection_drag_handler = g_signal_connect(
955 G_CALLBACK (selection_motion_event_handler),
957 } /* gnome_canvas_rich_text_start_selection_drag */
960 gnome_canvas_rich_text_end_selection_drag(GnomeCanvasRichText *text,
961 GdkEventButton * event)
963 if (text->_priv->selection_drag_handler == 0)
966 g_signal_handler_disconnect (text, text->_priv->selection_drag_handler);
967 text->_priv->selection_drag_handler = 0;
970 gnome_canvas_item_grab(NULL);
974 } /* gnome_canvas_rich_text_end_selection_drag */
977 gnome_canvas_rich_text_emit_tag_changed(GnomeCanvasRichText *text,
980 g_signal_emit(G_OBJECT(text), signals[TAG_CHANGED], 0, tag);
981 } /* gnome_canvas_rich_text_emit_tag_changed */
984 gnome_canvas_rich_text_key_press_event(GnomeCanvasItem *item,
987 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
988 gboolean extend_selection = FALSE;
989 gboolean handled = FALSE;
992 printf("Key press event\n");
995 if (!text->_priv->layout || !text->_priv->buffer)
998 if (event->state & GDK_SHIFT_MASK)
999 extend_selection = TRUE;
1001 switch (event->keyval) {
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);
1012 gtk_text_buffer_insert_interactive_at_cursor(
1013 get_buffer(text), "\t", 1, text->_priv->editable);
1019 if (event->state & GDK_CONTROL_MASK) {
1020 gnome_canvas_rich_text_move_cursor(
1021 text, GTK_MOVEMENT_WORDS, 1,
1026 gnome_canvas_rich_text_move_cursor(
1027 text, GTK_MOVEMENT_VISUAL_POSITIONS, 1,
1033 if (event->state & GDK_CONTROL_MASK) {
1034 gnome_canvas_rich_text_move_cursor(
1035 text, GTK_MOVEMENT_WORDS, -1,
1040 gnome_canvas_rich_text_move_cursor(
1041 text, GTK_MOVEMENT_VISUAL_POSITIONS, -1,
1047 if (event->state & GDK_CONTROL_MASK) {
1048 gnome_canvas_rich_text_move_cursor(
1049 text, GTK_MOVEMENT_LOGICAL_POSITIONS, 1,
1053 else if (event->state & GDK_MOD1_MASK) {
1054 gnome_canvas_rich_text_move_cursor(
1055 text, GTK_MOVEMENT_WORDS, 1,
1061 if (event->state & GDK_CONTROL_MASK) {
1062 gnome_canvas_rich_text_move_cursor(
1063 text, GTK_MOVEMENT_LOGICAL_POSITIONS, -1,
1067 else if (event->state & GDK_MOD1_MASK) {
1068 gnome_canvas_rich_text_move_cursor(
1069 text, GTK_MOVEMENT_WORDS, -1,
1075 gnome_canvas_rich_text_move_cursor(
1076 text, GTK_MOVEMENT_DISPLAY_LINES, -1,
1081 gnome_canvas_rich_text_move_cursor(
1082 text, GTK_MOVEMENT_DISPLAY_LINES, 1,
1087 if (event->state & GDK_CONTROL_MASK) {
1088 gnome_canvas_rich_text_move_cursor(
1089 text, GTK_MOVEMENT_DISPLAY_LINES, -1,
1095 if (event->state & GDK_CONTROL_MASK) {
1096 gnome_canvas_rich_text_move_cursor(
1097 text, GTK_MOVEMENT_DISPLAY_LINES, 1,
1103 gnome_canvas_rich_text_move_cursor(
1104 text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
1109 gnome_canvas_rich_text_move_cursor(
1110 text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
1115 if (event->state & GDK_CONTROL_MASK) {
1116 gnome_canvas_rich_text_move_cursor(
1117 text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
1123 if (event->state & GDK_CONTROL_MASK) {
1124 gnome_canvas_rich_text_move_cursor(
1125 text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
1134 if (event->state & GDK_CONTROL_MASK) {
1135 gnome_canvas_rich_text_delete_from_cursor(
1136 text, GTK_DELETE_WORD_ENDS, 1);
1140 gnome_canvas_rich_text_delete_from_cursor(
1141 text, GTK_DELETE_CHARS, 1);
1146 if (event->state & GDK_CONTROL_MASK) {
1147 gnome_canvas_rich_text_delete_from_cursor(
1148 text, GTK_DELETE_CHARS, 1);
1151 else if (event->state & GDK_MOD1_MASK) {
1152 gnome_canvas_rich_text_delete_from_cursor(
1153 text, GTK_DELETE_WORD_ENDS, 1);
1158 if (event->state & GDK_CONTROL_MASK) {
1159 gnome_canvas_rich_text_delete_from_cursor(
1160 text, GTK_DELETE_WORD_ENDS, -1);
1164 gnome_canvas_rich_text_delete_from_cursor(
1165 text, GTK_DELETE_CHARS, -1);
1170 if (event->state & GDK_CONTROL_MASK) {
1171 gnome_canvas_rich_text_delete_from_cursor(
1172 text, GTK_DELETE_PARAGRAPH_ENDS, 1);
1177 if (event->state & GDK_CONTROL_MASK) {
1178 gnome_canvas_rich_text_delete_from_cursor(
1179 text, GTK_DELETE_PARAGRAPHS, 1);
1184 if (event->state & GDK_MOD1_MASK) {
1185 gnome_canvas_rich_text_delete_from_cursor(
1186 text, GTK_DELETE_WHITESPACE, 1);
1191 if (event->state & GDK_MOD1_MASK) {
1192 gnome_canvas_rich_text_delete_from_cursor(
1193 text, GTK_DELETE_WHITESPACE, 1);
1201 /* An empty string, click just pressing "Alt" by itself or whatever. */
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);
1213 gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
1216 } /* gnome_canvas_rich_text_key_press_event */
1219 gnome_canvas_rich_text_key_release_event(GnomeCanvasItem * item,
1223 } /* gnome_canvas_rich_text_key_release_event */
1226 _click(gpointer data)
1228 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1230 text->_priv->clicks = 0;
1231 text->_priv->click_timeout = 0;
1237 gnome_canvas_rich_text_button_press_event(GnomeCanvasItem *item,
1238 GdkEventButton *event)
1240 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
1242 GdkEventType event_type;
1245 newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
1246 newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
1248 gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, newx, newy);
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);
1257 if (text->_priv->clicks > 3)
1258 text->_priv->clicks = text->_priv->clicks % 3;
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;
1267 printf("ZERO CLICKS!\n");
1270 if (event->button == 1 && event_type == GDK_BUTTON_PRESS) {
1271 GtkTextIter start, end;
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;
1280 gnome_canvas_rich_text_start_selection_drag(
1281 text, &iter, event);
1286 else if (event->button == 1 && event_type == GDK_2BUTTON_PRESS) {
1287 GtkTextIter start, end;
1290 printf("double-click\n");
1293 gnome_canvas_rich_text_end_selection_drag(text, event);
1298 if (gtk_text_iter_inside_word(&start)) {
1299 if (!gtk_text_iter_starts_word(&start))
1300 gtk_text_iter_backward_word_start(&start);
1302 if (!gtk_text_iter_ends_word(&end))
1303 gtk_text_iter_forward_word_end(&end);
1306 gtk_text_buffer_move_mark(
1308 gtk_text_buffer_get_selection_bound(get_buffer(text)),
1310 gtk_text_buffer_move_mark(
1312 gtk_text_buffer_get_insert(get_buffer(text)), &end);
1314 text->_priv->just_selected_element = TRUE;
1318 else if (event->button == 1 && event_type == GDK_3BUTTON_PRESS) {
1319 GtkTextIter start, end;
1322 printf("triple-click\n");
1325 gnome_canvas_rich_text_end_selection_drag(text, event);
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);
1335 gtk_text_layout_move_iter_to_line_end(
1336 text->_priv->layout, &start, -1);
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);
1345 gtk_text_buffer_move_mark(
1347 gtk_text_buffer_get_selection_bound(get_buffer(text)),
1349 gtk_text_buffer_move_mark(
1351 gtk_text_buffer_get_insert(get_buffer(text)), &end);
1353 text->_priv->just_selected_element = TRUE;
1357 else if (event->button == 2 && event_type == GDK_BUTTON_PRESS) {
1358 gtk_text_buffer_paste_clipboard(
1360 gtk_clipboard_get (GDK_SELECTION_PRIMARY),
1361 &iter, text->_priv->editable);
1365 } /* gnome_canvas_rich_text_button_press_event */
1368 gnome_canvas_rich_text_button_release_event(GnomeCanvasItem *item,
1369 GdkEventButton *event)
1371 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
1374 newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
1375 newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
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;
1383 if (gnome_canvas_rich_text_end_selection_drag(text, event))
1385 else if (text->_priv->just_selected_element) {
1386 text->_priv->just_selected_element = FALSE;
1392 gtk_text_layout_get_iter_at_pixel(
1393 text->_priv->layout, &iter, newx, newy);
1395 gtk_text_buffer_place_cursor(get_buffer(text), &iter);
1402 } /* gnome_canvas_rich_text_button_release_event */
1405 gnome_canvas_rich_text_focus_in_event(GnomeCanvasItem *item,
1406 GdkEventFocus * event)
1408 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
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);
1416 } /* gnome_canvas_rich_text_focus_in_event */
1419 gnome_canvas_rich_text_focus_out_event(GnomeCanvasItem *item,
1420 GdkEventFocus * event)
1422 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
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);
1430 } /* gnome_canvas_rich_text_focus_out_event */
1433 get_event_coordinates(GdkEvent *event, gint *x, gint *y)
1435 g_return_val_if_fail(event, FALSE);
1437 switch (event->type) {
1438 case GDK_MOTION_NOTIFY:
1439 *x = event->motion.x;
1440 *y = event->motion.y;
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;
1453 } /* get_event_coordinates */
1456 emit_event_on_tags(GnomeCanvasRichText *text, GdkEvent *event,
1462 tags = gtk_text_iter_get_tags(iter);
1466 GtkTextTag *tag = i->data;
1468 gtk_text_tag_event(tag, G_OBJECT(text), event, iter);
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(
1479 i = g_slist_next(i);
1483 } /* emit_event_on_tags */
1486 gnome_canvas_rich_text_event(GnomeCanvasItem *item, GdkEvent *event)
1488 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
1491 if (get_event_coordinates(event, &x, &y)) {
1494 x -= text->_priv->x;
1495 y -= text->_priv->y;
1497 gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, x, y);
1498 emit_event_on_tags(text, event, &iter);
1500 else if (event->type == GDK_KEY_PRESS ||
1501 event->type == GDK_KEY_RELEASE) {
1502 GtkTextMark *insert;
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);
1511 switch (event->type) {
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)
1529 if (((GdkEventFocus *) event)->in)
1530 return gnome_canvas_rich_text_focus_in_event(
1531 item, (GdkEventFocus *) event);
1533 return gnome_canvas_rich_text_focus_out_event(
1534 item, (GdkEventFocus *) event);
1538 } /* gnome_canvas_rich_text_event */
1540 /* Cut/Copy/Paste */
1543 * gnome_canvas_rich_text_cut_clipboard:
1544 * @text: a #GnomeCanvasRichText.
1546 * Copies the currently selected @text to clipboard, then deletes said text
1550 gnome_canvas_rich_text_cut_clipboard(GnomeCanvasRichText *text)
1552 g_return_if_fail(text);
1553 g_return_if_fail(get_buffer(text));
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 */
1562 * gnome_canvas_rich_text_copy_clipboard:
1563 * @text: a #GnomeCanvasRichText.
1565 * Copies the currently selected @text to clipboard.
1568 gnome_canvas_rich_text_copy_clipboard(GnomeCanvasRichText *text)
1570 g_return_if_fail(text);
1571 g_return_if_fail(get_buffer(text));
1573 gtk_text_buffer_copy_clipboard(get_buffer(text),
1574 gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1575 } /* gnome_canvas_rich_text_cut_clipboard */
1579 * gnome_canvas_rich_text_paste_clipboard:
1580 * @text: a #GnomeCanvasRichText.
1582 * Pastes the contents of the clipboard at the insertion point.
1585 gnome_canvas_rich_text_paste_clipboard(GnomeCanvasRichText *text)
1587 g_return_if_fail(text);
1588 g_return_if_fail(get_buffer(text));
1590 gtk_text_buffer_paste_clipboard(get_buffer(text),
1591 gtk_clipboard_get (GDK_SELECTION_PRIMARY),
1593 text->_priv->editable);
1594 } /* gnome_canvas_rich_text_cut_clipboard */
1597 preblink_cb(gpointer data)
1599 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1601 text->_priv->preblink_timeout = 0;
1602 gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
1604 /* Remove ourselves */
1609 blink_cb(gpointer data)
1611 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1614 g_assert(text->_priv->layout);
1615 g_assert(text->_priv->cursor_visible);
1617 visible = gtk_text_layout_get_cursor_visible(text->_priv->layout);
1619 text->_priv->blink_timeout = g_timeout_add(
1620 CURSOR_OFF_TIME, blink_cb, text);
1622 text->_priv->blink_timeout = g_timeout_add(
1623 CURSOR_ON_TIME, blink_cb, text);
1625 gtk_text_layout_set_cursor_visible(text->_priv->layout, !visible);
1627 /* Remove ourself */
1632 gnome_canvas_rich_text_start_cursor_blink(GnomeCanvasRichText *text,
1633 gboolean with_delay)
1635 if (!text->_priv->layout)
1638 if (!text->_priv->cursor_visible || !text->_priv->cursor_blink)
1641 if (text->_priv->preblink_timeout != 0) {
1642 g_source_remove(text->_priv->preblink_timeout);
1643 text->_priv->preblink_timeout = 0;
1647 if (text->_priv->blink_timeout != 0) {
1648 g_source_remove(text->_priv->blink_timeout);
1649 text->_priv->blink_timeout = 0;
1652 gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
1654 text->_priv->preblink_timeout = g_timeout_add(
1655 PREBLINK_TIME, preblink_cb, text);
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);
1664 } /* gnome_canvas_rich_text_start_cursor_blink */
1667 gnome_canvas_rich_text_stop_cursor_blink(GnomeCanvasRichText *text)
1669 if (text->_priv->blink_timeout) {
1670 g_source_remove(text->_priv->blink_timeout);
1671 text->_priv->blink_timeout = 0;
1673 } /* gnome_canvas_rich_text_stop_cursor_blink */
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. */
1678 request_update(gpointer data)
1680 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1682 gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
1685 } /* request_update */
1688 invalidated_handler(GtkTextLayout * layout, gpointer data)
1690 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1693 printf("Text is being invalidated.\n");
1696 gtk_text_layout_validate(text->_priv->layout, 2000);
1698 /* We are called from the update cycle; gotta put this in an idle
1700 g_idle_add(request_update, text);
1701 } /* invalidated_handler */
1704 scale_fonts(GtkTextTag *tag, gpointer data)
1706 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1712 G_OBJECT(tag), "scale",
1713 text->_priv->layout->default_style->font_scale, NULL);
1717 changed_handler(GtkTextLayout * layout, gint start_y,
1718 gint old_height, gint new_height, gpointer data)
1720 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
1723 printf("Layout %p is being changed.\n", text->_priv->layout);
1726 if (text->_priv->layout->default_style->font_scale !=
1727 GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit) {
1728 GtkTextTagTable *tag_table;
1730 text->_priv->layout->default_style->font_scale =
1731 GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
1733 tag_table = gtk_text_buffer_get_tag_table(get_buffer(text));
1734 gtk_text_tag_table_foreach(tag_table, scale_fonts, text);
1736 gtk_text_layout_default_style_changed(text->_priv->layout);
1739 if (text->_priv->grow_height) {
1742 gtk_text_layout_get_size(text->_priv->layout, &width, &height);
1744 if (height > text->_priv->height)
1745 text->_priv->height = height;
1748 /* We are called from the update cycle; gotta put this in an idle
1750 g_idle_add(request_update, text);
1751 } /* changed_handler */
1755 * gnome_canvas_rich_text_set_buffer:
1756 * @text: a #GnomeCanvasRichText.
1757 * @buffer: a #GtkTextBuffer.
1759 * Sets the buffer field of the @text to @buffer.
1762 gnome_canvas_rich_text_set_buffer(GnomeCanvasRichText *text,
1763 GtkTextBuffer *buffer)
1765 g_return_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text));
1766 g_return_if_fail(buffer == NULL || GTK_IS_TEXT_BUFFER(buffer));
1768 if (text->_priv->buffer == buffer)
1771 if (text->_priv->buffer != NULL) {
1772 g_object_unref(G_OBJECT(text->_priv->buffer));
1775 text->_priv->buffer = buffer;
1778 g_object_ref(G_OBJECT(buffer));
1780 if (text->_priv->layout)
1781 gtk_text_layout_set_buffer(text->_priv->layout, buffer);
1784 gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
1785 } /* gnome_canvas_rich_text_set_buffer */
1787 static GtkTextBuffer *
1788 get_buffer(GnomeCanvasRichText *text)
1790 if (!text->_priv->buffer) {
1793 b = gtk_text_buffer_new(NULL);
1794 gnome_canvas_rich_text_set_buffer(text, b);
1795 g_object_unref(G_OBJECT(b));
1798 return text->_priv->buffer;
1803 * gnome_canvas_rich_text_get_buffer:
1804 * @text: a #GnomeCanvasRichText.
1806 * Returns a #GtkTextBuffer associated with the #GnomeCanvasRichText.
1807 * This function creates a new #GtkTextBuffer if the text buffer is NULL.
1809 * Return value: the #GtkTextBuffer.
1812 gnome_canvas_rich_text_get_buffer(GnomeCanvasRichText *text)
1814 g_return_val_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text), NULL);
1816 return get_buffer(text);
1817 } /* gnome_canvas_rich_text_get_buffer */
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.
1826 * Gets a rectangle which roughly contains the character at @iter.
1829 gnome_canvas_rich_text_get_iter_location (GnomeCanvasRichText *text,
1830 const GtkTextIter *iter,
1831 GdkRectangle *location)
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);
1836 gtk_text_layout_get_iter_location (text->_priv->layout, iter, location);
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.
1847 * Retrieves the iterator at the buffer coordinates x and y.
1850 gnome_canvas_rich_text_get_iter_at_location (GnomeCanvasRichText *text,
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);
1859 gtk_text_layout_get_iter_at_pixel (text->_priv->layout,
1867 gnome_canvas_rich_text_set_attributes_from_style(GnomeCanvasRichText * text,
1868 GtkTextAttributes *values,
1871 values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
1872 values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
1875 pango_font_description_free (values->font);
1877 values->font = pango_font_description_copy (style->font_desc);
1879 } /* gnome_canvas_rich_text_set_attributes_from_style */
1882 gnome_canvas_rich_text_ensure_layout(GnomeCanvasRichText *text)
1884 if (!text->_priv->layout) {
1886 GtkTextAttributes *style;
1887 PangoContext *ltr_context, *rtl_context;
1889 text->_priv->layout = gtk_text_layout_new();
1891 gtk_text_layout_set_screen_width(text->_priv->layout, text->_priv->width);
1893 if (get_buffer(text)) {
1894 gtk_text_layout_set_buffer(
1895 text->_priv->layout, get_buffer(text));
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);
1904 gnome_canvas_rich_text_stop_cursor_blink(text);
1906 canvas = GTK_WIDGET(GNOME_CANVAS_ITEM(text)->canvas);
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);
1913 gtk_text_layout_set_contexts(
1914 text->_priv->layout, ltr_context, rtl_context);
1916 g_object_unref(G_OBJECT(ltr_context));
1917 g_object_unref(G_OBJECT(rtl_context));
1919 style = gtk_text_attributes_new();
1921 gnome_canvas_rich_text_set_attributes_from_style(
1922 text, style, canvas->style);
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;
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;
1937 gtk_text_layout_set_default_style(text->_priv->layout, style);
1939 gtk_text_attributes_unref(style);
1942 G_OBJECT(text->_priv->layout), "invalidated",
1943 G_CALLBACK (invalidated_handler), text);
1946 G_OBJECT(text->_priv->layout), "changed",
1947 G_CALLBACK (changed_handler), text);
1949 } /* gnome_canvas_rich_text_ensure_layout */
1952 gnome_canvas_rich_text_destroy_layout(GnomeCanvasRichText *text)
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;
1962 } /* gnome_canvas_rich_text_destroy_layout */
1965 adjust_for_anchors(GnomeCanvasRichText *text, double *ax, double *ay)
1974 switch (text->_priv->anchor) {
1981 case GTK_ANCHOR_CENTER:
1983 x -= text->_priv->width / 2;
1989 x -= text->_priv->width;
1996 switch (text->_priv->anchor) {
2003 case GTK_ANCHOR_CENTER:
2005 y -= text->_priv->height / 2;
2011 y -= text->_priv->height;
2021 } /* adjust_for_anchors */
2024 get_bounds(GnomeCanvasRichText *text, double *px1, double *py1,
2025 double *px2, double *py2)
2027 GnomeCanvasItem *item = GNOME_CANVAS_ITEM(text);
2029 double x1, x2, y1, y2;
2030 int cx1, cx2, cy1, cy2;
2032 adjust_for_anchors(text, &x, &y);
2036 x2 = x + text->_priv->width;
2037 y2 = y + text->_priv->height;
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);
2050 static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *item, double *px1, double *py1,
2051 double *px2, double *py2)
2053 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
2054 get_bounds (text, px1, py1, px2, py2);
2058 gnome_canvas_rich_text_update(GnomeCanvasItem *item, double *affine,
2059 ArtSVP *clip_path, int flags)
2061 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
2062 double x1, y1, x2, y2;
2065 (* GNOME_CANVAS_ITEM_CLASS(parent_class)->update)(
2066 item, affine, clip_path, flags);
2068 get_bounds(text, &x1, &y1, &x2, &y2);
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);
2075 gnome_canvas_update_bbox(item, x1, y1, x2, y2);
2076 } /* gnome_canvas_rich_text_update */
2079 gnome_canvas_rich_text_point(GnomeCanvasItem *item, double x, double y,
2080 int cx, int cy, GnomeCanvasItem **actual_item)
2082 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
2084 double x1, x2, y1, y2;
2087 *actual_item = item;
2089 /* This is a lame cop-out. Anywhere inside of the bounding box. */
2091 adjust_for_anchors(text, &ax, &ay);
2095 x2 = ax + text->_priv->width;
2096 y2 = ay + text->_priv->height;
2098 if ((x > x1) && (y > y1) && (x < x2) && (y < y2))
2115 return sqrt(dx * dx + dy * dy);
2116 } /* gnome_canvas_rich_text_point */
2119 gnome_canvas_rich_text_draw(GnomeCanvasItem *item, GdkDrawable *drawable,
2120 int x, int y, int width, int height)
2122 GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
2123 double i2w[6], w2c[6], i2c[6];
2129 gnome_canvas_item_i2w_affine(item, i2w);
2130 gnome_canvas_w2c_affine(item->canvas, w2c);
2131 art_affine_multiply(i2c, i2w, w2c);
2133 adjust_for_anchors(text, &ax, &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);
2147 gtk_text_layout_set_screen_width(text->_priv->layout, x2 - x1);
2149 /* FIXME: should last arg be NULL? */
2150 gtk_text_layout_draw(
2151 text->_priv->layout,
2152 GTK_WIDGET(item->canvas),
2154 GTK_WIDGET (item->canvas)->style->text_gc[GTK_STATE_NORMAL],
2156 0, 0, (x2 - x1) - (x - x1), (y2 - y1) - (y - y1),
2158 } /* gnome_canvas_rich_text_draw */
2161 gnome_canvas_rich_text_render(GnomeCanvasItem *item, GnomeCanvasBuf *buf)
2163 g_warning ("rich text item not implemented for anti-aliased canvas");
2164 } /* gnome_canvas_rich_text_render */
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, ...)
2173 GtkTextIter start, end;
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);
2181 GtkTextTagTable *tag_table;
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);
2187 tag = gtk_text_buffer_create_tag(
2188 get_buffer(text), tag_name, NULL);
2190 va_start(var_args, first_property_name);
2191 g_object_set_valist(G_OBJECT(tag), first_property_name, var_args);
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);
2201 } /* gnome_canvas_rich_text_add_tag */