1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * e-text.c - Text item for evolution.
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * Chris Lahey <clahey@ximian.com>
8 * Jon Trowbridge <trow@ximian.com>
10 * A majority of code taken from:
12 * Text item type for GnomeCanvas widget
14 * GnomeCanvas is basically a port of the Tk toolkit's most excellent
15 * canvas widget. Tk is copyrighted by the Regents of the University
16 * of California, Sun Microsystems, and other parties.
18 * Copyright (C) 1998 The Free Software Foundation
20 * Author: Federico Mena <federico@nuclecu.unam.mx>
22 * This program is free software; you can redistribute it and/or modify it
23 * under the terms of the GNU Lesser General Public License as published by
24 * published by the Free Software Foundation; either the
26 * This program is distributed in the hope that it will be useful, but
27 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
28 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
31 * You should have received a copy of the GNU Lesser General Public License
32 * along with this program; if not, see <http://www.gnu.org/licenses/>.
33 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
37 #include "evolution-config.h"
43 #include <glib/gi18n.h>
45 #include <gdk/gdkkeysyms.h>
47 #include <libedataserver/libedataserver.h>
49 #include "e-canvas-utils.h"
51 #include "e-marshal.h"
52 #include "e-text-event-processor-emacs-like.h"
53 #include "e-unicode.h"
54 #include "e-misc-utils.h"
55 #include "gal-a11y-e-text.h"
59 G_DEFINE_TYPE (EText
, e_text
, GNOME_TYPE_CANVAS_ITEM
)
65 E_TEXT_POPULATE_POPUP
,
69 static GQuark e_text_signals
[E_TEXT_LAST_SIGNAL
] = { 0 };
71 /* Object argument IDs */
85 PROP_FILL_CLIP_RECTANGLE
,
97 PROP_BREAK_CHARACTERS
,
107 static void e_text_command (ETextEventProcessor
*tep
,
108 ETextEventProcessorCommand
*command
,
111 static void e_text_text_model_changed (ETextModel
*model
,
113 static void e_text_text_model_reposition (ETextModel
*model
,
114 ETextModelReposFn fn
,
118 static void _get_tep (EText
*text
);
120 static void calc_height (EText
*text
);
122 static gboolean
show_pango_rectangle (EText
*text
, PangoRectangle rect
);
124 static void e_text_do_popup (EText
*text
,
125 GdkEvent
*event_button
,
128 static void e_text_update_primary_selection (EText
*text
);
129 static void e_text_paste (EText
*text
, GdkAtom selection
);
130 static void e_text_insert (EText
*text
, const gchar
*string
);
131 static void e_text_reset_im_context (EText
*text
);
133 static void reset_layout_attrs (EText
*text
);
135 /* IM Context Callbacks */
136 static void e_text_commit_cb (GtkIMContext
*context
,
139 static void e_text_preedit_changed_cb (GtkIMContext
*context
,
141 static gboolean
e_text_retrieve_surrounding_cb (GtkIMContext
*context
,
143 static gboolean
e_text_delete_surrounding_cb (GtkIMContext
*context
,
148 static GdkAtom clipboard_atom
= GDK_NONE
;
151 disconnect_im_context (EText
*text
)
153 if (!text
|| !text
->im_context
)
156 g_signal_handlers_disconnect_matched (
157 text
->im_context
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, text
);
158 text
->im_context_signals_registered
= FALSE
;
161 /* Dispose handler for the text item */
163 e_text_dispose (GObject
*object
)
167 g_return_if_fail (object
!= NULL
);
168 g_return_if_fail (E_IS_TEXT (object
));
170 text
= E_TEXT (object
);
172 if (text
->model_changed_signal_id
)
173 g_signal_handler_disconnect (
175 text
->model_changed_signal_id
);
176 text
->model_changed_signal_id
= 0;
178 if (text
->model_repos_signal_id
)
179 g_signal_handler_disconnect (
181 text
->model_repos_signal_id
);
182 text
->model_repos_signal_id
= 0;
185 g_object_unref (text
->model
);
188 if (text
->tep_command_id
)
189 g_signal_handler_disconnect (
191 text
->tep_command_id
);
192 text
->tep_command_id
= 0;
195 g_object_unref (text
->tep
);
198 g_free (text
->revert
);
201 if (text
->timeout_id
) {
202 g_source_remove (text
->timeout_id
);
203 text
->timeout_id
= 0;
207 g_timer_stop (text
->timer
);
208 g_timer_destroy (text
->timer
);
212 if (text
->dbl_timeout
) {
213 g_source_remove (text
->dbl_timeout
);
214 text
->dbl_timeout
= 0;
217 if (text
->tpl_timeout
) {
218 g_source_remove (text
->tpl_timeout
);
219 text
->tpl_timeout
= 0;
223 g_object_unref (text
->layout
);
227 if (text
->im_context
) {
228 disconnect_im_context (text
);
229 g_object_unref (text
->im_context
);
230 text
->im_context
= NULL
;
233 if (text
->font_desc
) {
234 pango_font_description_free (text
->font_desc
);
235 text
->font_desc
= NULL
;
238 /* Chain up to parent's dispose() method. */
239 G_OBJECT_CLASS (e_text_parent_class
)->dispose (object
);
243 insert_preedit_text (EText
*text
)
245 PangoAttrList
*attrs
= NULL
;
246 PangoAttrList
*preedit_attrs
= NULL
;
247 gchar
*preedit_string
= NULL
;
248 GString
*tmp_string
= g_string_new (NULL
);
249 gint length
= 0, cpos
= 0;
250 gboolean new_attrs
= FALSE
;
252 if (text
->layout
== NULL
|| !GTK_IS_IM_CONTEXT (text
->im_context
))
255 text
->text
= e_text_model_get_text (text
->model
);
256 length
= strlen (text
->text
);
258 g_string_prepend_len (tmp_string
, text
->text
,length
);
260 /* we came into this function only when text->preedit_len was not 0
261 * so we can safely fetch the preedit string */
262 gtk_im_context_get_preedit_string (
263 text
->im_context
, &preedit_string
, &preedit_attrs
, NULL
);
265 if (preedit_string
&& g_utf8_validate (preedit_string
, -1, NULL
)) {
267 text
->preedit_len
= strlen (preedit_string
);
269 cpos
= g_utf8_offset_to_pointer (
270 text
->text
, text
->selection_start
) - text
->text
;
272 g_string_insert (tmp_string
, cpos
, preedit_string
);
274 reset_layout_attrs (text
);
276 attrs
= pango_layout_get_attributes (text
->layout
);
278 attrs
= pango_attr_list_new ();
282 pango_layout_set_text (text
->layout
, tmp_string
->str
, tmp_string
->len
);
284 pango_attr_list_splice (attrs
, preedit_attrs
, cpos
, text
->preedit_len
);
287 pango_layout_set_attributes (text
->layout
, attrs
);
288 pango_attr_list_unref (attrs
);
291 text
->preedit_len
= 0;
294 g_free (preedit_string
);
296 pango_attr_list_unref (preedit_attrs
);
298 g_string_free (tmp_string
, TRUE
);
302 reset_layout_attrs (EText
*text
)
304 PangoAttrList
*attrs
= NULL
;
307 if (text
->layout
== NULL
)
310 object_count
= e_text_model_object_count (text
->model
);
312 if (text
->bold
|| text
->strikeout
|| text
->italic
|| object_count
> 0) {
316 attrs
= pango_attr_list_new ();
318 for (i
= 0; i
< object_count
; i
++) {
319 gint start_pos
, end_pos
;
320 PangoAttribute
*attr
;
322 attr
= pango_attr_underline_new (PANGO_UNDERLINE_SINGLE
);
324 e_text_model_get_nth_object_bounds (
325 text
->model
, i
, &start_pos
, &end_pos
);
327 attr
->start_index
= g_utf8_offset_to_pointer (
328 text
->text
, start_pos
) - text
->text
;
329 attr
->end_index
= g_utf8_offset_to_pointer (
330 text
->text
, end_pos
) - text
->text
;
332 pango_attr_list_insert (attrs
, attr
);
335 if (text
->bold
|| text
->strikeout
|| text
->italic
)
336 length
= strlen (text
->text
);
339 PangoAttribute
*attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
340 attr
->start_index
= 0;
341 attr
->end_index
= length
;
343 pango_attr_list_insert_before (attrs
, attr
);
346 if (text
->strikeout
) {
347 PangoAttribute
*attr
= pango_attr_strikethrough_new (TRUE
);
348 attr
->start_index
= 0;
349 attr
->end_index
= length
;
351 pango_attr_list_insert_before (attrs
, attr
);
355 PangoAttribute
*attr
= pango_attr_style_new (PANGO_STYLE_ITALIC
);
356 attr
->start_index
= 0;
357 attr
->end_index
= length
;
359 pango_attr_list_insert_before (attrs
, attr
);
363 pango_layout_set_attributes (text
->layout
, attrs
);
366 pango_attr_list_unref (attrs
);
372 create_layout (EText
*text
)
374 GnomeCanvasItem
*item
= GNOME_CANVAS_ITEM (text
);
379 text
->layout
= gtk_widget_create_pango_layout (
380 GTK_WIDGET (item
->canvas
), text
->text
);
382 pango_layout_set_width (
383 text
->layout
, text
->clip_width
< 0
384 ? -1 : text
->clip_width
* PANGO_SCALE
);
385 reset_layout_attrs (text
);
389 reset_layout (EText
*text
)
391 GnomeCanvasItem
*item
= GNOME_CANVAS_ITEM (text
);
393 if (text
->layout
== NULL
) {
394 create_layout (text
);
396 PangoContext
*pango_context
;
397 PangoFontDescription
*font_desc
;
399 pango_context
= gtk_widget_create_pango_context (GTK_WIDGET (item
->canvas
));
400 font_desc
= pango_context_get_font_description (pango_context
);
402 if (text
->font_desc
) {
403 pango_font_description_free (text
->font_desc
);
405 text
->font_desc
= pango_font_description_new ();
406 if (!pango_font_description_get_size_is_absolute (font_desc
))
407 pango_font_description_set_size (
409 pango_font_description_get_size (font_desc
));
411 pango_font_description_set_absolute_size (
413 pango_font_description_get_size (font_desc
));
414 pango_font_description_set_family (
416 pango_font_description_get_family (font_desc
));
417 pango_layout_set_font_description (text
->layout
, text
->font_desc
);
419 pango_layout_set_text (text
->layout
, text
->text
, -1);
420 reset_layout_attrs (text
);
422 g_object_unref (pango_context
);
425 if (!text
->button_down
) {
426 PangoRectangle strong_pos
, weak_pos
;
427 gchar
*offs
= g_utf8_offset_to_pointer (text
->text
, text
->selection_start
);
429 pango_layout_get_cursor_pos (
430 text
->layout
, offs
- text
->text
,
431 &strong_pos
, &weak_pos
);
433 if (strong_pos
.x
!= weak_pos
.x
||
434 strong_pos
.y
!= weak_pos
.y
||
435 strong_pos
.width
!= weak_pos
.width
||
436 strong_pos
.height
!= weak_pos
.height
)
437 show_pango_rectangle (text
, weak_pos
);
439 show_pango_rectangle (text
, strong_pos
);
444 e_text_text_model_changed (ETextModel
*model
,
447 gint model_len
= e_text_model_get_text_length (model
);
448 text
->text
= e_text_model_get_text (model
);
450 /* Make sure our selection doesn't extend past the bounds of our text. */
451 text
->selection_start
= CLAMP (text
->selection_start
, 0, model_len
);
452 text
->selection_end
= CLAMP (text
->selection_end
, 0, model_len
);
454 text
->needs_reset_layout
= 1;
455 text
->needs_split_into_lines
= 1;
456 text
->needs_redraw
= 1;
457 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (text
));
458 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text
));
460 g_signal_emit (text
, e_text_signals
[E_TEXT_CHANGED
], 0);
464 e_text_text_model_reposition (ETextModel
*model
,
465 ETextModelReposFn fn
,
469 EText
*text
= E_TEXT (user_data
);
470 gint model_len
= e_text_model_get_text_length (model
);
472 text
->selection_start
= fn (text
->selection_start
, repos_data
);
473 text
->selection_end
= fn (text
->selection_end
, repos_data
);
475 /* Our repos function should make sure we don't overrun the buffer, but it never
476 * hurts to be paranoid. */
477 text
->selection_start
= CLAMP (text
->selection_start
, 0, model_len
);
478 text
->selection_end
= CLAMP (text
->selection_end
, 0, model_len
);
480 if (text
->selection_start
> text
->selection_end
) {
481 gint tmp
= text
->selection_start
;
482 text
->selection_start
= text
->selection_end
;
483 text
->selection_end
= tmp
;
488 get_bounds (EText
*text
,
494 GnomeCanvasItem
*item
;
495 gdouble wx
, wy
, clip_width
, clip_height
;
497 item
= GNOME_CANVAS_ITEM (text
);
499 /* Get canvas pixel coordinates for text position */
503 gnome_canvas_item_i2w (item
, &wx
, &wy
);
504 gnome_canvas_w2c (item
->canvas
, wx
, wy
, &text
->cx
, &text
->cy
);
505 gnome_canvas_w2c (item
->canvas
, wx
, wy
, &text
->clip_cx
, &text
->clip_cy
);
507 if (text
->clip_width
< 0)
508 clip_width
= text
->width
;
510 clip_width
= text
->clip_width
;
512 if (text
->clip_height
< 0)
513 clip_height
= text
->height
;
515 clip_height
= text
->clip_height
;
517 /* Get canvas pixel coordinates for clip rectangle position */
518 text
->clip_cwidth
= clip_width
;
519 text
->clip_cheight
= clip_height
;
521 text
->text_cx
= text
->cx
;
522 text
->text_cy
= text
->cy
;
527 *px1
= text
->clip_cx
;
528 *py1
= text
->clip_cy
;
529 *px2
= text
->clip_cx
+ text
->clip_cwidth
;
530 *py2
= text
->clip_cy
+ text
->clip_cheight
;
534 *px2
= text
->cx
+ text
->width
;
535 *py2
= text
->cy
+ text
->height
;
540 calc_height (EText
*text
)
542 GnomeCanvasItem
*item
;
548 item
= GNOME_CANVAS_ITEM (text
);
550 /* Calculate text dimensions */
552 old_height
= text
->height
;
553 old_width
= text
->width
;
556 pango_layout_get_pixel_size (text
->layout
, &width
, &height
);
558 text
->height
= height
;
561 if (old_height
!= text
->height
|| old_width
!= text
->width
)
562 e_canvas_item_request_parent_reflow (item
);
566 calc_ellipsis (EText
*text
)
568 /* FIXME: a pango layout per calc_ellipsis sucks */
570 PangoLayout
*layout
= gtk_widget_create_pango_layout (
571 GTK_WIDGET (GNOME_CANVAS_ITEM (text
)->canvas
),
572 text
->ellipsis
? text
->ellipsis
: "...");
573 pango_layout_get_size (layout
, &width
, NULL
);
575 text
->ellipsis_width
= width
;
577 g_object_unref (layout
);
581 split_into_lines (EText
*text
)
583 text
->num_lines
= pango_layout_get_line_count (text
->layout
);
586 /* Set_arg handler for the text item */
588 e_text_set_property (GObject
*object
,
593 GnomeCanvasItem
*item
;
595 GdkColor color
= { 0, 0, 0, 0, };
598 gboolean needs_update
= 0;
599 gboolean needs_reflow
= 0;
601 item
= GNOME_CANVAS_ITEM (object
);
602 text
= E_TEXT (object
);
604 switch (property_id
) {
607 if (text
->model_changed_signal_id
)
608 g_signal_handler_disconnect (
610 text
->model_changed_signal_id
);
612 if (text
->model_repos_signal_id
)
613 g_signal_handler_disconnect (
615 text
->model_repos_signal_id
);
617 g_object_unref (text
->model
);
618 text
->model
= E_TEXT_MODEL (g_value_get_object (value
));
619 g_object_ref (text
->model
);
621 text
->model_changed_signal_id
= g_signal_connect (
622 text
->model
, "changed",
623 G_CALLBACK (e_text_text_model_changed
), text
);
625 text
->model_repos_signal_id
= g_signal_connect (
626 text
->model
, "reposition",
627 G_CALLBACK (e_text_text_model_reposition
), text
);
629 text
->text
= e_text_model_get_text (text
->model
);
630 g_signal_emit (text
, e_text_signals
[E_TEXT_CHANGED
], 0);
632 text
->needs_split_into_lines
= 1;
636 case PROP_EVENT_PROCESSOR
:
637 if (text
->tep
&& text
->tep_command_id
)
638 g_signal_handler_disconnect (
640 text
->tep_command_id
);
642 g_object_unref (text
->tep
);
644 text
->tep
= E_TEXT_EVENT_PROCESSOR (g_value_get_object (value
));
645 g_object_ref (text
->tep
);
647 text
->tep_command_id
= g_signal_connect (
648 text
->tep
, "command",
649 G_CALLBACK (e_text_command
), text
);
651 if (!text
->allow_newlines
)
654 "allow_newlines", FALSE
,
659 e_text_model_set_text (text
->model
, g_value_get_string (value
));
663 text
->bold
= g_value_get_boolean (value
);
665 text
->needs_redraw
= 1;
666 text
->needs_recalc_bounds
= 1;
668 text
->needs_split_into_lines
= 1;
670 text
->needs_calc_height
= 1;
677 text
->strikeout
= g_value_get_boolean (value
);
678 text
->needs_redraw
= 1;
683 text
->italic
= g_value_get_boolean (value
);
684 text
->needs_redraw
= 1;
688 case PROP_JUSTIFICATION
:
689 text
->justification
= g_value_get_enum (value
);
690 text
->needs_redraw
= 1;
694 case PROP_CLIP_WIDTH
:
695 text
->clip_width
= fabs (g_value_get_double (value
));
696 calc_ellipsis (text
);
697 if (text
->line_wrap
) {
699 pango_layout_set_width (
700 text
->layout
, text
->clip_width
< 0
701 ? -1 : text
->clip_width
* PANGO_SCALE
);
702 text
->needs_split_into_lines
= 1;
704 text
->needs_calc_height
= 1;
709 case PROP_CLIP_HEIGHT
:
710 text
->clip_height
= fabs (g_value_get_double (value
));
711 text
->needs_recalc_bounds
= 1;
712 /* toshok: kind of a hack - set needs_reset_layout
713 * here so when something about the style/them
714 * changes, we redraw the text at the proper size/with
715 * the proper font. */
716 text
->needs_reset_layout
= 1;
721 text
->clip
= g_value_get_boolean (value
);
722 calc_ellipsis (text
);
724 text
->needs_split_into_lines
= 1;
726 text
->needs_calc_height
= 1;
731 case PROP_FILL_CLIP_RECTANGLE
:
732 text
->fill_clip_rectangle
= g_value_get_boolean (value
);
737 text
->xofs
= g_value_get_double (value
);
738 text
->needs_recalc_bounds
= 1;
743 text
->yofs
= g_value_get_double (value
);
744 text
->needs_recalc_bounds
= 1;
748 case PROP_FILL_COLOR
:
749 if (g_value_get_string (value
) &&
750 !gdk_color_parse (g_value_get_string (value
), &color
)) {
751 g_warning ("%s: Failed to parse color '%s'", G_STRFUNC
, g_value_get_string (value
));
755 text
->rgba
= ((e_color_to_value (&color
) & 0xffffff) << 8) | 0xff;
756 text
->rgba_set
= TRUE
;
757 text
->needs_redraw
= 1;
761 case PROP_FILL_COLOR_GDK
:
762 pcolor
= g_value_get_boxed (value
);
767 text
->rgba
= ((e_color_to_value (&color
) & 0xffffff) << 8) | 0xff;
768 text
->rgba_set
= TRUE
;
769 text
->needs_redraw
= 1;
773 case PROP_FILL_COLOR_RGBA
:
774 text
->rgba
= g_value_get_uint (value
);
775 color
.red
= ((text
->rgba
>> 24) & 0xff) * 0x101;
776 color
.green
= ((text
->rgba
>> 16) & 0xff) * 0x101;
777 color
.blue
= ((text
->rgba
>> 8) & 0xff) * 0x101;
778 text
->rgba_set
= TRUE
;
779 text
->needs_redraw
= 1;
784 text
->editable
= g_value_get_boolean (value
);
785 text
->needs_redraw
= 1;
789 case PROP_USE_ELLIPSIS
:
790 text
->use_ellipsis
= g_value_get_boolean (value
);
796 g_free (text
->ellipsis
);
798 text
->ellipsis
= g_strdup (g_value_get_string (value
));
799 calc_ellipsis (text
);
804 text
->line_wrap
= g_value_get_boolean (value
);
805 if (text
->line_wrap
) {
807 pango_layout_set_width (
808 text
->layout
, text
->width
< 0
809 ? -1 : text
->width
* PANGO_SCALE
);
812 text
->needs_split_into_lines
= 1;
816 case PROP_BREAK_CHARACTERS
:
817 if (text
->break_characters
) {
818 g_free (text
->break_characters
);
819 text
->break_characters
= NULL
;
821 if (g_value_get_string (value
))
822 text
->break_characters
= g_strdup (g_value_get_string (value
));
823 text
->needs_split_into_lines
= 1;
828 text
->max_lines
= g_value_get_int (value
);
829 text
->needs_split_into_lines
= 1;
834 text
->clip_width
= fabs (g_value_get_double (value
));
835 calc_ellipsis (text
);
836 if (text
->line_wrap
) {
838 pango_layout_set_width (
839 text
->layout
, text
->width
< 0 ?
840 -1 : text
->width
* PANGO_SCALE
);
842 text
->needs_split_into_lines
= 1;
845 text
->needs_calc_height
= 1;
850 case PROP_ALLOW_NEWLINES
:
851 text
->allow_newlines
= g_value_get_boolean (value
);
855 "allow_newlines", g_value_get_boolean (value
),
859 case PROP_CURSOR_POS
: {
860 ETextEventProcessorCommand command
;
862 command
.action
= E_TEP_MOVE
;
863 command
.position
= E_TEP_VALUE
;
864 command
.value
= g_value_get_int (value
);
865 command
.time
= GDK_CURRENT_TIME
;
866 e_text_command (text
->tep
, &command
, text
);
870 case PROP_IM_CONTEXT
:
871 if (text
->im_context
) {
872 disconnect_im_context (text
);
873 g_object_unref (text
->im_context
);
876 text
->im_context
= g_value_get_object (value
);
877 if (text
->im_context
)
878 g_object_ref (text
->im_context
);
880 text
->need_im_reset
= TRUE
;
883 case PROP_HANDLE_POPUP
:
884 text
->handle_popup
= g_value_get_boolean (value
);
892 e_canvas_item_request_reflow (item
);
894 gnome_canvas_item_request_update (item
);
897 /* Get_arg handler for the text item */
899 e_text_get_property (GObject
*object
,
906 text
= E_TEXT (object
);
908 switch (property_id
) {
910 g_value_set_object (value
, text
->model
);
913 case PROP_EVENT_PROCESSOR
:
915 g_value_set_object (value
, text
->tep
);
919 g_value_set_string (value
, text
->text
);
923 g_value_set_boolean (value
, text
->bold
);
927 g_value_set_boolean (value
, text
->strikeout
);
931 g_value_set_boolean (value
, text
->italic
);
934 case PROP_JUSTIFICATION
:
935 g_value_set_enum (value
, text
->justification
);
938 case PROP_CLIP_WIDTH
:
939 g_value_set_double (value
, text
->clip_width
);
942 case PROP_CLIP_HEIGHT
:
943 g_value_set_double (value
, text
->clip_height
);
947 g_value_set_boolean (value
, text
->clip
);
950 case PROP_FILL_CLIP_RECTANGLE
:
951 g_value_set_boolean (value
, text
->fill_clip_rectangle
);
955 g_value_set_double (value
, text
->xofs
);
959 g_value_set_double (value
, text
->yofs
);
962 case PROP_FILL_COLOR_RGBA
:
963 g_value_set_uint (value
, text
->rgba
);
966 case PROP_TEXT_WIDTH
:
967 g_value_set_double (value
, text
->width
);
970 case PROP_TEXT_HEIGHT
:
971 g_value_set_double (value
, text
->height
);
975 g_value_set_boolean (value
, text
->editable
);
978 case PROP_USE_ELLIPSIS
:
979 g_value_set_boolean (value
, text
->use_ellipsis
);
983 g_value_set_string (value
, text
->ellipsis
);
987 g_value_set_boolean (value
, text
->line_wrap
);
990 case PROP_BREAK_CHARACTERS
:
991 g_value_set_string (value
, text
->break_characters
);
995 g_value_set_int (value
, text
->max_lines
);
999 g_value_set_double (value
, text
->clip_width
);
1003 g_value_set_double (
1004 value
, text
->clip
&&
1005 text
->clip_height
!= -1 ?
1006 text
->clip_height
: text
->height
);
1009 case PROP_ALLOW_NEWLINES
:
1010 g_value_set_boolean (value
, text
->allow_newlines
);
1013 case PROP_CURSOR_POS
:
1014 g_value_set_int (value
, text
->selection_start
);
1017 case PROP_IM_CONTEXT
:
1018 g_value_set_object (value
, text
->im_context
);
1021 case PROP_HANDLE_POPUP
:
1022 g_value_set_boolean (value
, text
->handle_popup
);
1026 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1031 /* Update handler for the text item */
1033 e_text_reflow (GnomeCanvasItem
*item
,
1038 text
= E_TEXT (item
);
1040 if (text
->needs_reset_layout
) {
1041 reset_layout (text
);
1042 text
->needs_reset_layout
= 0;
1043 text
->needs_calc_height
= 1;
1046 if (text
->needs_split_into_lines
) {
1047 split_into_lines (text
);
1049 text
->needs_split_into_lines
= 0;
1050 text
->needs_calc_height
= 1;
1053 if (text
->needs_calc_height
) {
1055 gnome_canvas_item_request_update (item
);
1056 text
->needs_calc_height
= 0;
1057 text
->needs_recalc_bounds
= 1;
1061 /* Update handler for the text item */
1063 e_text_update (GnomeCanvasItem
*item
,
1064 const cairo_matrix_t
*i2c
,
1068 gdouble x1
, y1
, x2
, y2
;
1070 text
= E_TEXT (item
);
1072 if (GNOME_CANVAS_ITEM_CLASS (e_text_parent_class
)->update
)
1073 GNOME_CANVAS_ITEM_CLASS (e_text_parent_class
)->update (
1076 if (text
->needs_recalc_bounds
1077 || (flags
& GNOME_CANVAS_UPDATE_AFFINE
)) {
1078 get_bounds (text
, &x1
, &y1
, &x2
, &y2
);
1079 if (item
->x1
!= x1
||
1083 gnome_canvas_request_redraw (
1084 item
->canvas
, item
->x1
, item
->y1
,
1085 item
->x2
, item
->y2
);
1090 text
->needs_redraw
= 1;
1091 item
->canvas
->need_repick
= TRUE
;
1093 if (!text
->fill_clip_rectangle
)
1094 item
->canvas
->need_repick
= TRUE
;
1095 text
->needs_recalc_bounds
= 0;
1097 if (text
->needs_redraw
) {
1098 gnome_canvas_request_redraw (
1099 item
->canvas
, item
->x1
, item
->y1
, item
->x2
, item
->y2
);
1100 text
->needs_redraw
= 0;
1104 /* Realize handler for the text item */
1106 e_text_realize (GnomeCanvasItem
*item
)
1110 text
= E_TEXT (item
);
1112 if (GNOME_CANVAS_ITEM_CLASS (e_text_parent_class
)->realize
)
1113 (* GNOME_CANVAS_ITEM_CLASS (e_text_parent_class
)->realize
) (item
);
1115 create_layout (text
);
1117 text
->i_cursor
= gdk_cursor_new (GDK_XTERM
);
1118 text
->default_cursor
= gdk_cursor_new (GDK_LEFT_PTR
);
1121 /* Unrealize handler for the text item */
1123 e_text_unrealize (GnomeCanvasItem
*item
)
1127 text
= E_TEXT (item
);
1129 g_object_unref (text
->i_cursor
);
1130 text
->i_cursor
= NULL
;
1131 g_object_unref (text
->default_cursor
);
1132 text
->default_cursor
= NULL
;
1134 if (GNOME_CANVAS_ITEM_CLASS (e_text_parent_class
)->unrealize
)
1135 (* GNOME_CANVAS_ITEM_CLASS (e_text_parent_class
)->unrealize
) (item
);
1139 _get_tep (EText
*text
)
1142 text
->tep
= e_text_event_processor_emacs_like_new ();
1144 text
->tep_command_id
= g_signal_connect (
1145 text
->tep
, "command",
1146 G_CALLBACK (e_text_command
), text
);
1151 draw_pango_rectangle (cairo_t
*cr
,
1154 PangoRectangle rect
)
1156 gint width
= rect
.width
/ PANGO_SCALE
;
1157 gint height
= rect
.height
/ PANGO_SCALE
;
1165 cr
, x1
+ rect
.x
/ PANGO_SCALE
,
1166 y1
+ rect
.y
/ PANGO_SCALE
, width
, height
);
1171 show_pango_rectangle (EText
*text
,
1172 PangoRectangle rect
)
1174 gint x1
= rect
.x
/ PANGO_SCALE
;
1175 gint x2
= (rect
.x
+ rect
.width
) / PANGO_SCALE
;
1177 gint y1
= rect
.y
/ PANGO_SCALE
;
1178 gint y2
= (rect
.y
+ rect
.height
) / PANGO_SCALE
;
1180 gint new_xofs_edit
= text
->xofs_edit
;
1181 gint new_yofs_edit
= text
->yofs_edit
;
1183 gint clip_width
, clip_height
;
1185 clip_width
= text
->clip_width
;
1186 clip_height
= text
->clip_height
;
1188 if (x1
< new_xofs_edit
)
1191 if (y1
< new_yofs_edit
)
1194 if (clip_width
>= 0) {
1195 if (2 + x2
- clip_width
> new_xofs_edit
)
1196 new_xofs_edit
= 2 + x2
- clip_width
;
1201 if (clip_height
>= 0) {
1202 if (y2
- clip_height
> new_yofs_edit
)
1203 new_yofs_edit
= y2
- clip_height
;
1208 if (new_xofs_edit
< 0)
1210 if (new_yofs_edit
< 0)
1213 if (new_xofs_edit
!= text
->xofs_edit
||
1214 new_yofs_edit
!= text
->yofs_edit
) {
1215 text
->xofs_edit
= new_xofs_edit
;
1216 text
->yofs_edit
= new_yofs_edit
;
1223 /* Draw handler for the text item */
1225 e_text_draw (GnomeCanvasItem
*item
,
1234 GnomeCanvas
*canvas
;
1239 text
= E_TEXT (item
);
1240 canvas
= GNOME_CANVAS_ITEM (text
)->canvas
;
1241 widget
= GTK_WIDGET (canvas
);
1242 backdrop
= (gtk_widget_get_state_flags (widget
) & GTK_STATE_FLAG_BACKDROP
) != 0;
1246 if (!text
->rgba_set
) {
1247 e_utils_get_theme_color (widget
, backdrop
? "theme_unfocused_fg_color,theme_fg_color" : "theme_fg_color", E_UTILS_DEFAULT_THEME_FG_COLOR
, &rgba
);
1248 gdk_cairo_set_source_rgba (cr
, &rgba
);
1250 cairo_set_source_rgba (
1252 ((text
->rgba
>> 24) & 0xff) / 255.0,
1253 ((text
->rgba
>> 16) & 0xff) / 255.0,
1254 ((text
->rgba
>> 8) & 0xff) / 255.0,
1255 ( text
->rgba
& 0xff) / 255.0);
1258 /* Insert preedit text only when im_context signals are connected &
1259 * text->preedit_len is not zero */
1260 if (text
->im_context_signals_registered
&& text
->preedit_len
)
1261 insert_preedit_text (text
);
1263 /* Need to reset the layout to cleanly clear the preedit buffer when
1264 * typing in CJK & using backspace on the preedit */
1265 if (!text
->preedit_len
)
1266 reset_layout (text
);
1268 if (!pango_layout_get_text (text
->layout
)) {
1273 xpos
= text
->text_cx
;
1274 ypos
= text
->text_cy
;
1276 xpos
= xpos
- x
+ text
->xofs
;
1277 ypos
= ypos
- y
+ text
->yofs
;
1284 text
->clip_cwidth
- text
->xofs
,
1285 text
->clip_cheight
- text
->yofs
);
1289 if (text
->editing
) {
1290 xpos
-= text
->xofs_edit
;
1291 ypos
-= text
->yofs_edit
;
1294 cairo_move_to (cr
, xpos
, ypos
);
1295 pango_cairo_show_layout (cr
, text
->layout
);
1297 if (text
->editing
) {
1298 if (text
->selection_start
!= text
->selection_end
) {
1299 cairo_region_t
*clip_region
;
1303 text
->selection_start
,
1304 text
->selection_end
);
1306 text
->selection_start
,
1307 text
->selection_end
);
1309 /* convert these into byte indices */
1310 indices
[0] = g_utf8_offset_to_pointer (
1311 text
->text
, indices
[0]) - text
->text
;
1312 indices
[1] = g_utf8_offset_to_pointer (
1313 text
->text
, indices
[1]) - text
->text
;
1315 clip_region
= gdk_pango_layout_get_clip_region (
1316 text
->layout
, xpos
, ypos
, indices
, 1);
1317 gdk_cairo_region (cr
, clip_region
);
1319 cairo_region_destroy (clip_region
);
1321 e_utils_get_theme_color (widget
, backdrop
? "theme_unfocused_base_color,theme_base_color" : "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR
, &rgba
);
1323 gdk_cairo_set_source_rgba (cr
, &rgba
);
1326 e_utils_get_theme_color (widget
, backdrop
? "theme_unfocused_text_color,theme_text_color,theme_fg_color" : "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR
, &rgba
);
1327 gdk_cairo_set_source_rgba (cr
, &rgba
);
1328 cairo_move_to (cr
, xpos
, ypos
);
1329 pango_cairo_show_layout (cr
, text
->layout
);
1331 if (text
->show_cursor
) {
1332 PangoRectangle strong_pos
, weak_pos
;
1335 offs
= g_utf8_offset_to_pointer (
1336 text
->text
, text
->selection_start
);
1338 pango_layout_get_cursor_pos (
1339 text
->layout
, offs
- text
->text
+
1340 text
->preedit_len
, &strong_pos
,
1342 draw_pango_rectangle (cr
, xpos
, ypos
, strong_pos
);
1343 if (strong_pos
.x
!= weak_pos
.x
||
1344 strong_pos
.y
!= weak_pos
.y
||
1345 strong_pos
.width
!= weak_pos
.width
||
1346 strong_pos
.height
!= weak_pos
.height
)
1347 draw_pango_rectangle (cr
, xpos
, ypos
, weak_pos
);
1356 /* Point handler for the text item */
1357 static GnomeCanvasItem
*
1358 e_text_point (GnomeCanvasItem
*item
,
1366 gdouble clip_height
;
1368 text
= E_TEXT (item
);
1370 /* The idea is to build bounding rectangles for each of the lines of
1371 * text (clipped by the clipping rectangle, if it is activated) and see
1372 * whether the point is inside any of these. If it is, we are done.
1373 * Otherwise, calculate the distance to the nearest rectangle.
1376 if (text
->clip_width
< 0)
1377 clip_width
= text
->width
;
1379 clip_width
= text
->clip_width
;
1381 if (text
->clip_height
< 0)
1382 clip_height
= text
->height
;
1384 clip_height
= text
->clip_height
;
1386 if (cx
< text
->clip_cx
||
1387 cx
> text
->clip_cx
+ clip_width
||
1388 cy
< text
->clip_cy
||
1389 cy
> text
->clip_cy
+ clip_height
)
1392 if (text
->fill_clip_rectangle
|| !text
->text
|| !*text
->text
)
1397 if (pango_layout_xy_to_index (text
->layout
, cx
, cy
, NULL
, NULL
))
1403 /* Bounds handler for the text item */
1405 e_text_bounds (GnomeCanvasItem
*item
,
1412 gdouble width
, height
;
1414 text
= E_TEXT (item
);
1419 width
= text
->width
;
1420 height
= text
->height
;
1423 if (text
->clip_width
>= 0)
1424 width
= text
->clip_width
;
1425 if (text
->clip_height
>= 0)
1426 height
= text
->clip_height
;
1434 get_position_from_xy (EText
*text
,
1444 if (text
->editing
) {
1445 x
+= text
->xofs_edit
;
1446 y
+= text
->yofs_edit
;
1452 pango_layout_xy_to_index (
1453 text
->layout
, x
* PANGO_SCALE
,
1454 y
* PANGO_SCALE
, &index
, &trailing
);
1456 return g_utf8_pointer_to_offset (text
->text
, text
->text
+ index
+ trailing
);
1459 #define SCROLL_WAIT_TIME 30000
1462 _blink_scroll_timeout (gpointer data
)
1464 EText
*text
= E_TEXT (data
);
1465 gulong current_time
;
1466 gboolean scroll
= FALSE
;
1467 gboolean redraw
= FALSE
;
1469 g_timer_elapsed (text
->timer
, ¤t_time
);
1471 if (text
->scroll_start
+ SCROLL_WAIT_TIME
> 1000000) {
1472 if (current_time
> text
->scroll_start
- (1000000 - SCROLL_WAIT_TIME
) &&
1473 current_time
< text
->scroll_start
)
1476 if (current_time
> text
->scroll_start
+ SCROLL_WAIT_TIME
||
1477 current_time
< text
->scroll_start
)
1480 if (scroll
&& text
->button_down
&& text
->clip
) {
1481 gint old_xofs_edit
= text
->xofs_edit
;
1482 gint old_yofs_edit
= text
->yofs_edit
;
1484 if (text
->clip_cwidth
>= 0 &&
1485 text
->lastx
- text
->clip_cx
> text
->clip_cwidth
&&
1486 text
->xofs_edit
< text
->width
- text
->clip_cwidth
) {
1487 text
->xofs_edit
+= 4;
1488 if (text
->xofs_edit
> text
->width
- text
->clip_cwidth
+ 1)
1489 text
->xofs_edit
= text
->width
- text
->clip_cwidth
+ 1;
1491 if (text
->lastx
- text
->clip_cx
< 0 &&
1492 text
->xofs_edit
> 0) {
1493 text
->xofs_edit
-= 4;
1494 if (text
->xofs_edit
< 0)
1495 text
->xofs_edit
= 0;
1498 if (text
->clip_cheight
>= 0 &&
1499 text
->lasty
- text
->clip_cy
> text
->clip_cheight
&&
1500 text
->yofs_edit
< text
->height
- text
->clip_cheight
) {
1501 text
->yofs_edit
+= 4;
1502 if (text
->yofs_edit
> text
->height
- text
->clip_cheight
+ 1)
1503 text
->yofs_edit
= text
->height
- text
->clip_cheight
+ 1;
1505 if (text
->lasty
- text
->clip_cy
< 0 &&
1506 text
->yofs_edit
> 0) {
1507 text
->yofs_edit
-= 4;
1508 if (text
->yofs_edit
< 0)
1509 text
->yofs_edit
= 0;
1512 if (old_xofs_edit
!= text
->xofs_edit
||
1513 old_yofs_edit
!= text
->yofs_edit
) {
1514 ETextEventProcessorEvent e_tep_event
;
1515 e_tep_event
.type
= GDK_MOTION_NOTIFY
;
1516 e_tep_event
.motion
.state
= text
->last_state
;
1517 e_tep_event
.motion
.time
= 0;
1518 e_tep_event
.motion
.position
=
1519 get_position_from_xy (
1520 text
, text
->lastx
, text
->lasty
);
1522 e_text_event_processor_handle_event (
1525 text
->scroll_start
= current_time
;
1530 if (!((current_time
/ 500000) % 2)) {
1531 if (!text
->show_cursor
)
1533 text
->show_cursor
= TRUE
;
1535 if (text
->show_cursor
)
1537 text
->show_cursor
= FALSE
;
1540 text
->needs_redraw
= 1;
1541 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text
));
1547 start_editing (EText
*text
)
1552 e_text_reset_im_context (text
);
1554 g_free (text
->revert
);
1555 text
->revert
= g_strdup (text
->text
);
1557 text
->editing
= TRUE
;
1558 if (text
->pointer_in
) {
1561 window
= gtk_widget_get_window (
1562 GTK_WIDGET (GNOME_CANVAS_ITEM (text
)->canvas
));
1564 if (text
->default_cursor_shown
) {
1565 gdk_window_set_cursor (window
, text
->i_cursor
);
1566 text
->default_cursor_shown
= FALSE
;
1569 text
->select_by_word
= FALSE
;
1570 text
->xofs_edit
= 0;
1571 text
->yofs_edit
= 0;
1572 if (text
->timeout_id
== 0) {
1573 text
->timeout_id
= e_named_timeout_add (
1574 10, _blink_scroll_timeout
, text
);
1576 text
->timer
= g_timer_new ();
1577 g_timer_elapsed (text
->timer
, &(text
->scroll_start
));
1578 g_timer_start (text
->timer
);
1582 e_text_stop_editing (EText
*text
)
1587 g_free (text
->revert
);
1588 text
->revert
= NULL
;
1590 text
->editing
= FALSE
;
1591 if (!text
->default_cursor_shown
) {
1594 window
= gtk_widget_get_window (
1595 GTK_WIDGET (GNOME_CANVAS_ITEM (text
)->canvas
));
1596 gdk_window_set_cursor (window
, text
->default_cursor
);
1597 text
->default_cursor_shown
= TRUE
;
1600 g_timer_stop (text
->timer
);
1601 g_timer_destroy (text
->timer
);
1605 text
->need_im_reset
= TRUE
;
1606 text
->preedit_len
= 0;
1607 text
->preedit_pos
= 0;
1611 e_text_cancel_editing (EText
*text
)
1614 e_text_model_set_text (text
->model
, text
->revert
);
1615 e_text_stop_editing (text
);
1619 _click (gpointer data
)
1626 e_text_event (GnomeCanvasItem
*item
,
1629 EText
*text
= E_TEXT (item
);
1630 ETextEventProcessorEvent e_tep_event
;
1632 gint return_val
= 0;
1637 window
= gtk_widget_get_window (GTK_WIDGET (item
->canvas
));
1639 e_tep_event
.type
= event
->type
;
1640 switch (event
->type
) {
1641 case GDK_FOCUS_CHANGE
:
1642 if (text
->editable
) {
1643 GdkEventFocus
*focus_event
;
1644 focus_event
= (GdkEventFocus
*) event
;
1645 if (focus_event
->in
) {
1646 if (text
->im_context
) {
1647 if (!text
->im_context_signals_registered
) {
1649 text
->im_context
, "commit",
1650 G_CALLBACK (e_text_commit_cb
), text
);
1652 text
->im_context
, "preedit_changed",
1653 G_CALLBACK (e_text_preedit_changed_cb
), text
);
1655 text
->im_context
, "retrieve_surrounding",
1656 G_CALLBACK (e_text_retrieve_surrounding_cb
), text
);
1658 text
->im_context
, "delete_surrounding",
1659 G_CALLBACK (e_text_delete_surrounding_cb
), text
);
1660 text
->im_context_signals_registered
= TRUE
;
1662 gtk_im_context_focus_in (text
->im_context
);
1665 start_editing (text
);
1667 /* So we'll redraw and the
1668 * cursor will be shown. */
1669 text
->show_cursor
= FALSE
;
1671 if (text
->im_context
) {
1672 gtk_im_context_focus_out (text
->im_context
);
1673 disconnect_im_context (text
);
1674 text
->need_im_reset
= TRUE
;
1677 e_text_stop_editing (text
);
1678 if (text
->timeout_id
) {
1679 g_source_remove (text
->timeout_id
);
1680 text
->timeout_id
= 0;
1682 if (text
->show_cursor
) {
1683 text
->show_cursor
= FALSE
;
1684 text
->needs_redraw
= 1;
1685 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text
));
1688 if (text
->line_wrap
)
1689 text
->needs_split_into_lines
= 1;
1690 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (text
));
1696 /* Handle S-F10 key binding here. */
1698 if (event
->key
.keyval
== GDK_KEY_F10
1699 && (event
->key
.state
& GDK_SHIFT_MASK
)
1700 && text
->handle_popup
) {
1702 /* Simulate a GdkEventButton here, so that we can
1703 * call e_text_do_popup directly */
1705 GdkEvent
*button_event
;
1707 button_event
= gdk_event_new (GDK_BUTTON_PRESS
);
1708 button_event
->button
.time
= event
->key
.time
;
1709 button_event
->button
.button
= 0;
1710 e_text_do_popup (text
, button_event
, 0);
1716 case GDK_KEY_RELEASE
:
1718 if (text
->editing
) {
1722 if (text
->im_context
&&
1723 gtk_im_context_filter_keypress (
1725 (GdkEventKey
*) event
)) {
1726 text
->need_im_reset
= TRUE
;
1731 e_tep_event
.key
.time
= key
.time
;
1732 e_tep_event
.key
.state
= key
.state
;
1733 e_tep_event
.key
.keyval
= key
.keyval
;
1735 /* This is probably ugly hack, but we
1736 * have to handle UTF-8 input somehow. */
1737 e_tep_event
.key
.string
= e_utf8_from_gtk_event_key (
1738 GTK_WIDGET (item
->canvas
),
1739 key
.keyval
, key
.string
);
1740 if (e_tep_event
.key
.string
!= NULL
) {
1741 e_tep_event
.key
.length
= strlen (e_tep_event
.key
.string
);
1743 e_tep_event
.key
.length
= 0;
1747 ret
= e_text_event_processor_handle_event (text
->tep
, &e_tep_event
);
1749 if (event
->type
== GDK_KEY_PRESS
)
1751 text
, e_text_signals
[E_TEXT_KEYPRESS
], 0,
1752 e_tep_event
.key
.keyval
, e_tep_event
.key
.state
);
1754 if (e_tep_event
.key
.string
)
1755 g_free ((gpointer
) e_tep_event
.key
.string
);
1760 case GDK_BUTTON_PRESS
: /* Fall Through */
1761 case GDK_BUTTON_RELEASE
:
1762 if ((!text
->editing
)
1764 && (event
->button
.button
== 1 ||
1765 event
->button
.button
== 2)) {
1766 e_canvas_item_grab_focus (item
, TRUE
);
1767 start_editing (text
);
1770 /* We follow convention and emit popup events on right-clicks. */
1771 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
.button
== 3) {
1772 if (text
->handle_popup
) {
1775 get_position_from_xy (
1776 text
, event
->button
.x
,
1785 /* Create our own double and triple click events,
1786 * as gnome-canvas doesn't forward them to us */
1787 if (event
->type
== GDK_BUTTON_PRESS
) {
1788 if (text
->dbl_timeout
== 0 &&
1789 text
->tpl_timeout
== 0) {
1790 text
->dbl_timeout
= e_named_timeout_add (
1791 200, _click
, &(text
->dbl_timeout
));
1793 if (text
->tpl_timeout
== 0) {
1794 e_tep_event
.type
= GDK_2BUTTON_PRESS
;
1795 text
->tpl_timeout
= e_named_timeout_add (
1796 200, _click
, &(text
->tpl_timeout
));
1798 e_tep_event
.type
= GDK_3BUTTON_PRESS
;
1803 if (text
->editing
) {
1804 GdkEventButton button
= event
->button
;
1805 e_tep_event
.button
.time
= button
.time
;
1806 e_tep_event
.button
.state
= button
.state
;
1807 e_tep_event
.button
.button
= button
.button
;
1808 e_tep_event
.button
.position
=
1809 get_position_from_xy (
1810 text
, button
.x
, button
.y
);
1811 e_tep_event
.button
.device
=
1812 gdk_event_get_device (event
);
1814 return_val
= e_text_event_processor_handle_event (
1815 text
->tep
, &e_tep_event
);
1816 if (event
->button
.button
== 1) {
1817 if (event
->type
== GDK_BUTTON_PRESS
)
1818 text
->button_down
= TRUE
;
1820 text
->button_down
= FALSE
;
1822 text
->lastx
= button
.x
;
1823 text
->lasty
= button
.y
;
1824 text
->last_state
= button
.state
;
1827 case GDK_MOTION_NOTIFY
:
1828 if (text
->editing
) {
1829 GdkEventMotion motion
= event
->motion
;
1830 e_tep_event
.motion
.time
= motion
.time
;
1831 e_tep_event
.motion
.state
= motion
.state
;
1832 e_tep_event
.motion
.position
=
1833 get_position_from_xy (
1834 text
, motion
.x
, motion
.y
);
1836 return_val
= e_text_event_processor_handle_event (
1837 text
->tep
, &e_tep_event
);
1838 text
->lastx
= motion
.x
;
1839 text
->lasty
= motion
.y
;
1840 text
->last_state
= motion
.state
;
1843 case GDK_ENTER_NOTIFY
:
1844 text
->pointer_in
= TRUE
;
1845 if (text
->editing
) {
1846 if (text
->default_cursor_shown
) {
1847 gdk_window_set_cursor (window
, text
->i_cursor
);
1848 text
->default_cursor_shown
= FALSE
;
1852 case GDK_LEAVE_NOTIFY
:
1853 text
->pointer_in
= FALSE
;
1854 if (text
->editing
) {
1855 if (!text
->default_cursor_shown
) {
1856 gdk_window_set_cursor (
1857 window
, text
->default_cursor
);
1858 text
->default_cursor_shown
= TRUE
;
1867 if (GNOME_CANVAS_ITEM_CLASS (e_text_parent_class
)->event
)
1868 return GNOME_CANVAS_ITEM_CLASS (e_text_parent_class
)->event (item
, event
);
1874 e_text_copy_clipboard (EText
*text
)
1876 gint selection_start_pos
;
1877 gint selection_end_pos
;
1879 selection_start_pos
= MIN (text
->selection_start
, text
->selection_end
);
1880 selection_end_pos
= MAX (text
->selection_start
, text
->selection_end
);
1882 /* convert sel_start/sel_end to byte indices */
1883 selection_start_pos
= g_utf8_offset_to_pointer (
1884 text
->text
, selection_start_pos
) - text
->text
;
1885 selection_end_pos
= g_utf8_offset_to_pointer (
1886 text
->text
, selection_end_pos
) - text
->text
;
1888 gtk_clipboard_set_text (
1889 gtk_widget_get_clipboard (
1890 GTK_WIDGET (GNOME_CANVAS_ITEM (text
)->canvas
),
1891 GDK_SELECTION_CLIPBOARD
),
1892 text
->text
+ selection_start_pos
,
1893 selection_end_pos
- selection_start_pos
);
1897 e_text_delete_selection (EText
*text
)
1899 gint sel_start
, sel_end
;
1901 sel_start
= MIN (text
->selection_start
, text
->selection_end
);
1902 sel_end
= MAX (text
->selection_start
, text
->selection_end
);
1904 if (sel_start
!= sel_end
)
1905 e_text_model_delete (text
->model
, sel_start
, sel_end
- sel_start
);
1906 text
->need_im_reset
= TRUE
;
1910 e_text_cut_clipboard (EText
*text
)
1912 e_text_copy_clipboard (text
);
1913 e_text_delete_selection (text
);
1917 e_text_paste_clipboard (EText
*text
)
1919 ETextEventProcessorCommand command
;
1921 command
.action
= E_TEP_PASTE
;
1922 command
.position
= E_TEP_SELECTION
;
1923 command
.string
= "";
1925 e_text_command (text
->tep
, &command
, text
);
1929 e_text_select_all (EText
*text
)
1931 ETextEventProcessorCommand command
;
1933 command
.action
= E_TEP_SELECT
;
1934 command
.position
= E_TEP_SELECT_ALL
;
1935 command
.string
= "";
1937 e_text_command (text
->tep
, &command
, text
);
1941 primary_get_cb (GtkClipboard
*clipboard
,
1942 GtkSelectionData
*selection_data
,
1946 EText
*text
= E_TEXT (data
);
1947 gint sel_start
, sel_end
;
1949 sel_start
= MIN (text
->selection_start
, text
->selection_end
);
1950 sel_end
= MAX (text
->selection_start
, text
->selection_end
);
1952 /* convert sel_start/sel_end to byte indices */
1953 sel_start
= g_utf8_offset_to_pointer (text
->text
, sel_start
) - text
->text
;
1954 sel_end
= g_utf8_offset_to_pointer (text
->text
, sel_end
) - text
->text
;
1956 if (sel_start
!= sel_end
) {
1957 gtk_selection_data_set_text (
1959 text
->text
+ sel_start
,
1960 sel_end
- sel_start
);
1965 primary_clear_cb (GtkClipboard
*clipboard
,
1970 gtk_editable_select_region (
1971 GTK_EDITABLE (entry
), entry
->current_pos
, entry
->current_pos
);
1976 e_text_update_primary_selection (EText
*text
)
1978 static const GtkTargetEntry targets
[] = {
1979 { (gchar
*) "UTF8_STRING", 0, 0 },
1980 { (gchar
*) "UTF-8", 0, 0 },
1981 { (gchar
*) "STRING", 0, 0 },
1982 { (gchar
*) "TEXT", 0, 0 },
1983 { (gchar
*) "COMPOUND_TEXT", 0, 0 }
1985 GtkClipboard
*clipboard
;
1987 clipboard
= gtk_widget_get_clipboard (
1988 GTK_WIDGET (GNOME_CANVAS_ITEM (text
)->canvas
),
1989 GDK_SELECTION_PRIMARY
);
1991 if (text
->selection_start
!= text
->selection_end
) {
1992 if (!gtk_clipboard_set_with_owner (
1993 clipboard
, targets
, G_N_ELEMENTS (targets
),
1994 primary_get_cb
, primary_clear_cb
, G_OBJECT (text
)))
1995 primary_clear_cb (clipboard
, text
);
1997 if (gtk_clipboard_get_owner (clipboard
) == G_OBJECT (text
))
1998 gtk_clipboard_clear (clipboard
);
2003 paste_received (GtkClipboard
*clipboard
,
2007 EText
*etext
= E_TEXT (data
);
2009 if (text
&& g_utf8_validate (text
, strlen (text
), NULL
)) {
2010 if (etext
->selection_end
!= etext
->selection_start
)
2011 e_text_delete_selection (etext
);
2013 e_text_insert (etext
, text
);
2016 g_object_unref (etext
);
2020 e_text_paste (EText
*text
,
2023 g_object_ref (text
);
2024 gtk_clipboard_request_text (
2025 gtk_widget_get_clipboard (
2026 GTK_WIDGET (GNOME_CANVAS_ITEM (text
)->canvas
),
2027 selection
), paste_received
, text
);
2037 popup_targets_received (GtkClipboard
*clipboard
,
2038 GtkSelectionData
*data
,
2041 PopupClosure
*closure
= user_data
;
2042 EText
*text
= closure
->text
;
2043 GdkEvent
*event
= closure
->event
;
2044 gint position
= closure
->position
;
2045 GtkWidget
*popup_menu
= gtk_menu_new ();
2046 GtkWidget
*menuitem
, *submenu
;
2047 guint event_button
= 0;
2050 gdk_event_get_button (event
, &event_button
);
2054 gtk_menu_attach_to_widget (
2055 GTK_MENU (popup_menu
),
2056 GTK_WIDGET (GNOME_CANVAS_ITEM (text
)->canvas
),
2058 g_signal_connect (popup_menu
, "deactivate", G_CALLBACK (gtk_menu_detach
), NULL
);
2061 menuitem
= gtk_image_menu_item_new_with_mnemonic (_("Cu_t"));
2062 gtk_image_menu_item_set_image (
2063 GTK_IMAGE_MENU_ITEM (menuitem
),
2064 gtk_image_new_from_icon_name ("edit-cut", GTK_ICON_SIZE_MENU
));
2065 gtk_widget_show (menuitem
);
2066 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu
), menuitem
);
2067 g_signal_connect_swapped (
2068 menuitem
, "activate",
2069 G_CALLBACK (e_text_cut_clipboard
), text
);
2070 gtk_widget_set_sensitive (
2071 menuitem
, text
->editable
&&
2072 (text
->selection_start
!= text
->selection_end
));
2074 /* copy menu item */
2075 menuitem
= gtk_image_menu_item_new_with_mnemonic (_("_Copy"));
2076 gtk_image_menu_item_set_image (
2077 GTK_IMAGE_MENU_ITEM (menuitem
),
2078 gtk_image_new_from_icon_name ("edit-copy", GTK_ICON_SIZE_MENU
));
2079 gtk_widget_show (menuitem
);
2080 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu
), menuitem
);
2081 g_signal_connect_swapped (
2082 menuitem
, "activate",
2083 G_CALLBACK (e_text_copy_clipboard
), text
);
2084 gtk_widget_set_sensitive (menuitem
, text
->selection_start
!= text
->selection_end
);
2086 /* paste menu item */
2087 menuitem
= gtk_image_menu_item_new_with_mnemonic (_("_Paste"));
2088 gtk_image_menu_item_set_image (
2089 GTK_IMAGE_MENU_ITEM (menuitem
),
2090 gtk_image_new_from_icon_name ("edit-paste", GTK_ICON_SIZE_MENU
));
2091 gtk_widget_show (menuitem
);
2092 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu
), menuitem
);
2093 g_signal_connect_swapped (
2094 menuitem
, "activate",
2095 G_CALLBACK (e_text_paste_clipboard
), text
);
2096 gtk_widget_set_sensitive (
2097 menuitem
, text
->editable
&&
2098 gtk_selection_data_targets_include_text (data
));
2100 menuitem
= gtk_menu_item_new_with_label (_("Select All"));
2101 gtk_widget_show (menuitem
);
2102 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu
), menuitem
);
2103 g_signal_connect_swapped (
2104 menuitem
, "activate",
2105 G_CALLBACK (e_text_select_all
), text
);
2106 gtk_widget_set_sensitive (menuitem
, strlen (text
->text
) > 0);
2108 menuitem
= gtk_separator_menu_item_new ();
2109 gtk_widget_show (menuitem
);
2110 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu
), menuitem
);
2112 if (text
->im_context
&& GTK_IS_IM_MULTICONTEXT (text
->im_context
)) {
2113 menuitem
= gtk_menu_item_new_with_label (_("Input Methods"));
2114 gtk_widget_show (menuitem
);
2115 submenu
= gtk_menu_new ();
2116 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem
), submenu
);
2118 gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu
), menuitem
);
2120 gtk_im_multicontext_append_menuitems (
2121 GTK_IM_MULTICONTEXT (text
->im_context
),
2122 GTK_MENU_SHELL (submenu
));
2127 e_text_signals
[E_TEXT_POPULATE_POPUP
],
2133 /* If invoked by S-F10 key binding, button will be 0. */
2134 if (event_button
== 0 && text
->item
.canvas
) {
2135 rect
.x
= text
->item
.x1
;
2136 rect
.y
= text
->item
.y1
;
2137 rect
.width
= text
->width
;
2138 rect
.height
= text
->height
;
2140 gtk_menu_popup_at_rect (GTK_MENU (popup_menu
),
2141 gtk_widget_get_window (GTK_WIDGET (text
->item
.canvas
)),
2144 GDK_GRAVITY_NORTH_WEST
,
2147 gtk_menu_popup_at_pointer (GTK_MENU (popup_menu
), event
);
2149 g_object_unref (text
);
2150 gdk_event_free (event
);
2154 e_text_do_popup (EText
*text
,
2155 GdkEvent
*button_event
,
2158 PopupClosure
*closure
= g_new (PopupClosure
, 1);
2160 closure
->text
= g_object_ref (text
);
2161 closure
->event
= gdk_event_copy (button_event
);
2162 closure
->position
= position
;
2164 gtk_clipboard_request_contents (
2165 gtk_widget_get_clipboard (
2166 GTK_WIDGET (GNOME_CANVAS_ITEM (text
)->canvas
),
2167 GDK_SELECTION_CLIPBOARD
),
2168 gdk_atom_intern ("TARGETS", FALSE
),
2169 popup_targets_received
,
2174 e_text_reset_im_context (EText
*text
)
2176 if (text
->need_im_reset
&& text
->im_context
) {
2177 text
->need_im_reset
= FALSE
;
2178 gtk_im_context_reset (text
->im_context
);
2185 next_word (EText
*text
,
2188 gchar
*p
= g_utf8_offset_to_pointer (text
->text
, start
);
2191 length
= g_utf8_strlen (text
->text
, -1);
2193 if (start
>= length
) {
2196 p
= g_utf8_next_char (p
);
2200 gunichar unival
= g_utf8_get_char (p
);
2201 if (g_unichar_isspace (unival
)) {
2205 p
= g_utf8_next_char (p
);
2211 return g_utf8_pointer_to_offset (text
->text
, p
);
2215 find_offset_into_line (EText
*text
,
2216 gint offset_into_text
,
2217 gchar
**start_of_line
)
2221 p
= g_utf8_offset_to_pointer (text
->text
, offset_into_text
);
2223 if (p
== text
->text
) {
2225 *start_of_line
= (gchar
*)text
->text
;
2229 p
= g_utf8_find_prev_char (text
->text
, p
);
2231 while (p
&& p
> text
->text
) {
2234 *start_of_line
= p
+1;
2235 return offset_into_text
-
2236 g_utf8_pointer_to_offset (
2239 p
= g_utf8_find_prev_char (text
->text
, p
);
2243 *start_of_line
= (gchar
*)text
->text
;
2244 return offset_into_text
;
2248 /* direction = TRUE (move forward), FALSE (move backward)
2249 * Any error shall return length (text->text) or 0 or
2250 * text->selection_end (as deemed fit) */
2252 _get_updated_position (EText
*text
,
2255 PangoLogAttr
*log_attrs
= NULL
;
2261 /* Basic sanity test, return whatever position we are currently at. */
2262 g_return_val_if_fail (text
->layout
!= NULL
, text
->selection_end
);
2264 length
= g_utf8_strlen (text
->text
, -1);
2266 /* length checks to make sure we are not wandering
2267 * off into nonexistant memory... */
2268 if ((text
->selection_end
>= length
) && (TRUE
== direction
)) /* forward */
2270 /* checking for -ve value wont hurt! */
2271 if ((text
->selection_end
<= 0) && (FALSE
== direction
)) /* backward */
2274 /* check for validness of full text->text */
2275 if (!g_utf8_validate (text
->text
, -1, NULL
))
2276 return text
->selection_end
;
2278 /* get layout's PangoLogAttr to facilitate moving when
2279 * moving across grapheme cluster as in indic langs */
2280 pango_layout_get_log_attrs (text
->layout
, &log_attrs
, &n_attrs
);
2282 /* Fetch the current gchar index in the line & keep moving
2283 * forward until we can display cursor */
2284 p
= g_utf8_offset_to_pointer (text
->text
, text
->selection_end
);
2286 new_pos
= text
->selection_end
;
2289 /* check before moving forward/backwards
2290 * if we have more chars to move or not */
2291 if (TRUE
== direction
)
2292 p
= g_utf8_next_char (p
);
2294 p
= g_utf8_prev_char (p
);
2296 /* validate the new string & return with original position if check fails */
2297 if (!g_utf8_validate (p
, -1, NULL
))
2298 break; /* will return old value of new_pos */
2300 new_pos
= g_utf8_pointer_to_offset (text
->text
, p
);
2302 /* if is_cursor_position is set, cursor can appear in front of character.
2303 * i.e. this is a grapheme boundary AND make some sanity checks */
2304 if ((new_pos
>=0) && (new_pos
< n_attrs
) &&
2305 (log_attrs
[new_pos
].is_cursor_position
))
2307 else if ((new_pos
< 0) || (new_pos
>= n_attrs
))
2309 new_pos
= text
->selection_end
;
2321 _get_position (EText
*text
,
2322 ETextEventProcessorCommand
*command
)
2324 gint length
, obj_num
;
2329 switch (command
->position
) {
2332 new_pos
= command
->value
;
2335 case E_TEP_SELECTION
:
2336 new_pos
= text
->selection_end
;
2339 case E_TEP_START_OF_BUFFER
:
2343 case E_TEP_END_OF_BUFFER
:
2344 new_pos
= strlen (text
->text
);
2347 case E_TEP_START_OF_LINE
:
2349 if (text
->selection_end
>= 1) {
2351 p
= g_utf8_offset_to_pointer (text
->text
, text
->selection_end
);
2352 if (p
!= text
->text
) {
2353 p
= g_utf8_find_prev_char (text
->text
, p
);
2354 while (p
&& p
> text
->text
) {
2356 new_pos
= g_utf8_pointer_to_offset (text
->text
, p
) + 1;
2359 p
= g_utf8_find_prev_char (text
->text
, p
);
2366 case E_TEP_END_OF_LINE
:
2368 length
= g_utf8_strlen (text
->text
, -1);
2370 if (text
->selection_end
>= length
) {
2374 p
= g_utf8_offset_to_pointer (text
->text
, text
->selection_end
);
2378 new_pos
= g_utf8_pointer_to_offset (text
->text
, p
);
2381 p
= g_utf8_next_char (p
);
2386 new_pos
= g_utf8_pointer_to_offset (text
->text
, p
);
2390 case E_TEP_FORWARD_CHARACTER
:
2391 length
= g_utf8_strlen (text
->text
, -1);
2393 if (text
->selection_end
>= length
)
2396 /* get updated position to display cursor */
2397 new_pos
= _get_updated_position (text
, TRUE
);
2401 case E_TEP_BACKWARD_CHARACTER
:
2403 if (text
->selection_end
>= 1)
2404 /* get updated position to display cursor */
2405 new_pos
= _get_updated_position (text
, FALSE
);
2409 case E_TEP_FORWARD_WORD
:
2410 new_pos
= next_word (text
, text
->selection_end
);
2413 case E_TEP_BACKWARD_WORD
:
2415 if (text
->selection_end
>= 1) {
2416 gint pos
= text
->selection_end
;
2418 p
= g_utf8_find_prev_char (
2419 text
->text
, g_utf8_offset_to_pointer (
2420 text
->text
, text
->selection_end
));
2423 if (p
!= text
->text
) {
2424 p
= g_utf8_find_prev_char (text
->text
, p
);
2427 while (p
&& p
> text
->text
) {
2428 unival
= g_utf8_get_char (p
);
2429 if (g_unichar_isspace (unival
)) {
2434 p
= g_utf8_find_prev_char (text
->text
, p
);
2443 case E_TEP_FORWARD_LINE
: {
2444 gint offset_into_line
;
2446 offset_into_line
= find_offset_into_line (text
, text
->selection_end
, NULL
);
2447 if (offset_into_line
== -1)
2448 return text
->selection_end
;
2450 /* now we search forward til we hit a \n, and then
2451 * offset_into_line more characters */
2452 p
= g_utf8_offset_to_pointer (text
->text
, text
->selection_end
);
2456 p
= g_utf8_next_char (p
);
2458 if (p
&& *p
== '\n') {
2459 /* now we loop forward offset_into_line
2460 * characters, or until we hit \n or \0 */
2462 p
= g_utf8_next_char (p
);
2463 while (offset_into_line
> 0 && p
&& *p
!= '\n' && *p
!= '\0') {
2464 p
= g_utf8_next_char (p
);
2469 /* at this point, p points to the new location,
2470 * convert it to an offset and we're done */
2471 new_pos
= g_utf8_pointer_to_offset (text
->text
, p
);
2474 case E_TEP_BACKWARD_LINE
: {
2475 gint offset_into_line
;
2477 offset_into_line
= find_offset_into_line (
2478 text
, text
->selection_end
, &p
);
2480 if (offset_into_line
== -1)
2481 return text
->selection_end
;
2483 /* p points to the first character on our line. if we
2484 * have a \n before it, skip it and scan til we hit
2486 if (p
!= text
->text
) {
2487 p
= g_utf8_find_prev_char (text
->text
, p
);
2489 p
= g_utf8_find_prev_char (text
->text
, p
);
2490 while (p
> text
->text
) {
2495 p
= g_utf8_find_prev_char (text
->text
, p
);
2500 /* at this point 'p' points to the start of the
2501 * previous line, move forward 'offset_into_line'
2504 while (offset_into_line
> 0 && p
&& *p
!= '\n' && *p
!= '\0') {
2505 p
= g_utf8_next_char (p
);
2509 /* at this point, p points to the new location,
2510 * convert it to an offset and we're done */
2511 new_pos
= g_utf8_pointer_to_offset (text
->text
, p
);
2514 case E_TEP_SELECT_WORD
:
2515 /* This is a silly hack to cause double-clicking on an object
2516 * to activate that object.
2517 * (Normally, double click == select word, which is why this is here.) */
2519 obj_num
= e_text_model_get_object_at_offset (
2520 text
->model
, text
->selection_start
);
2521 if (obj_num
!= -1) {
2522 e_text_model_activate_nth_object (text
->model
, obj_num
);
2523 new_pos
= text
->selection_start
;
2527 if (text
->selection_end
< 1) {
2532 p
= g_utf8_offset_to_pointer (text
->text
, text
->selection_end
);
2534 p
= g_utf8_find_prev_char (text
->text
, p
);
2536 while (p
&& p
> text
->text
) {
2537 unival
= g_utf8_get_char (p
);
2538 if (g_unichar_isspace (unival
)) {
2539 p
= g_utf8_next_char (p
);
2542 p
= g_utf8_find_prev_char (text
->text
, p
);
2546 text
->selection_start
= 0;
2548 text
->selection_start
= g_utf8_pointer_to_offset (text
->text
, p
);
2550 text
->selection_start
=
2551 e_text_model_validate_position (
2552 text
->model
, text
->selection_start
);
2554 length
= g_utf8_strlen (text
->text
, -1);
2555 if (text
->selection_end
>= length
) {
2560 p
= g_utf8_offset_to_pointer (text
->text
, text
->selection_end
);
2562 unival
= g_utf8_get_char (p
);
2563 if (g_unichar_isspace (unival
)) {
2564 new_pos
= g_utf8_pointer_to_offset (text
->text
, p
);
2567 p
= g_utf8_next_char (p
);
2571 new_pos
= g_utf8_strlen (text
->text
, -1);
2575 case E_TEP_SELECT_ALL
:
2576 text
->selection_start
= 0;
2577 new_pos
= g_utf8_strlen (text
->text
, -1);
2580 case E_TEP_FORWARD_PARAGRAPH
:
2581 case E_TEP_BACKWARD_PARAGRAPH
:
2583 case E_TEP_FORWARD_PAGE
:
2584 case E_TEP_BACKWARD_PAGE
:
2585 new_pos
= text
->selection_end
;
2589 new_pos
= text
->selection_end
;
2593 new_pos
= e_text_model_validate_position (text
->model
, new_pos
);
2599 e_text_insert (EText
*text
,
2600 const gchar
*string
)
2602 gint len
= strlen (string
);
2607 if (!text
->allow_newlines
) {
2609 gchar
*new_string
= g_malloc (len
+ 1);
2610 gchar
*j
= new_string
;
2612 for (i
= string
; *i
; i
= g_utf8_next_char (i
)) {
2617 c
= g_utf8_get_char (i
);
2618 charlen
= g_unichar_to_utf8 (c
, j
);
2624 e_text_model_insert_length (
2625 text
->model
, text
->selection_start
,
2626 new_string
, utf8len
);
2627 g_free (new_string
);
2630 utf8len
= g_utf8_strlen (string
, -1);
2631 e_text_model_insert_length (
2632 text
->model
, text
->selection_start
,
2639 capitalize (EText
*text
,
2642 ETextEventProcessorCaps type
)
2644 gboolean first
= TRUE
;
2645 const gchar
*p
= g_utf8_offset_to_pointer (text
->text
, start
);
2646 const gchar
*text_end
= g_utf8_offset_to_pointer (text
->text
, end
);
2647 gint utf8len
= text_end
- p
;
2650 gchar
*new_text
= g_new0 (char, utf8len
* 6);
2651 gchar
*output
= new_text
;
2653 while (p
&& *p
&& p
< text_end
) {
2654 gunichar unival
= g_utf8_get_char (p
);
2655 gunichar newval
= unival
;
2658 case E_TEP_CAPS_UPPER
:
2659 newval
= g_unichar_toupper (unival
);
2661 case E_TEP_CAPS_LOWER
:
2662 newval
= g_unichar_tolower (unival
);
2664 case E_TEP_CAPS_TITLE
:
2665 if (g_unichar_isalpha (unival
)) {
2667 newval
= g_unichar_totitle (unival
);
2669 newval
= g_unichar_tolower (unival
);
2676 g_unichar_to_utf8 (newval
, output
);
2677 output
= g_utf8_next_char (output
);
2679 p
= g_utf8_next_char (p
);
2683 e_text_model_delete (text
->model
, start
, utf8len
);
2684 e_text_model_insert_length (text
->model
, start
, new_text
, utf8len
);
2690 e_text_command (ETextEventProcessor
*tep
,
2691 ETextEventProcessorCommand
*command
,
2694 EText
*text
= E_TEXT (data
);
2695 gboolean scroll
= TRUE
;
2696 gboolean use_start
= TRUE
;
2698 switch (command
->action
) {
2700 text
->selection_start
= _get_position (text
, command
);
2701 text
->selection_end
= text
->selection_start
;
2703 g_timer_reset (text
->timer
);
2706 text
->need_im_reset
= TRUE
;
2710 text
->selection_start
=
2711 e_text_model_validate_position (
2712 text
->model
, text
->selection_start
); /* paranoia */
2713 text
->selection_end
= _get_position (text
, command
);
2715 e_text_update_primary_selection (text
);
2717 text
->need_im_reset
= TRUE
;
2722 if (text
->selection_end
== text
->selection_start
) {
2723 text
->selection_end
= _get_position (text
, command
);
2725 e_text_delete_selection (text
);
2727 g_timer_reset (text
->timer
);
2730 text
->need_im_reset
= TRUE
;
2736 if (g_utf8_validate (command
->string
, command
->value
, NULL
)) {
2737 if (text
->selection_end
!= text
->selection_start
) {
2738 e_text_delete_selection (text
);
2740 e_text_insert (text
, command
->string
);
2742 g_timer_reset (text
->timer
);
2744 text
->need_im_reset
= TRUE
;
2748 e_text_copy_clipboard (text
);
2751 g_timer_reset (text
->timer
);
2756 e_text_paste (text
, GDK_NONE
);
2758 g_timer_reset (text
->timer
);
2760 text
->need_im_reset
= TRUE
;
2762 case E_TEP_GET_SELECTION
:
2763 e_text_paste (text
, GDK_SELECTION_PRIMARY
);
2765 case E_TEP_ACTIVATE
:
2766 g_signal_emit (text
, e_text_signals
[E_TEXT_ACTIVATE
], 0);
2768 g_timer_reset (text
->timer
);
2771 case E_TEP_SET_SELECT_BY_WORD
:
2772 text
->select_by_word
= command
->value
;
2775 e_canvas_item_grab (
2776 E_CANVAS (GNOME_CANVAS_ITEM (text
)->canvas
),
2777 GNOME_CANVAS_ITEM (text
),
2778 GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK
,
2787 e_canvas_item_ungrab (
2788 E_CANVAS (GNOME_CANVAS_ITEM (text
)->canvas
),
2789 GNOME_CANVAS_ITEM (text
),
2794 if (text
->selection_start
== text
->selection_end
) {
2796 text
, text
->selection_start
,
2797 next_word (text
, text
->selection_start
),
2800 gint selection_start
= MIN (
2801 text
->selection_start
, text
->selection_end
);
2802 gint selection_end
= MAX (
2803 text
->selection_start
, text
->selection_end
);
2805 text
, selection_start
,
2806 selection_end
, command
->value
);
2814 e_text_reset_im_context (text
);
2816 /* it's possible to get here without ever having been realized
2817 * by our canvas (if the e-text started completely obscured.)
2818 * so let's create our layout object if we don't already have
2821 create_layout (text
);
2823 /* We move cursor only if scroll is TRUE */
2824 if (scroll
&& !text
->button_down
) {
2825 /* XXX do we really need the @trailing logic here? if
2826 * we don't we can scrap the loop and just use
2827 * pango_layout_index_to_pos */
2828 PangoLayoutLine
*cur_line
= NULL
;
2829 gint selection_index
;
2830 PangoLayoutIter
*iter
= pango_layout_get_iter (text
->layout
);
2832 /* check if we are using selection_start or selection_end for moving? */
2833 selection_index
= use_start
? text
->selection_start
: text
->selection_end
;
2835 /* convert to a byte index */
2836 selection_index
= g_utf8_offset_to_pointer (
2837 text
->text
, selection_index
) - text
->text
;
2840 PangoLayoutLine
*line
= pango_layout_iter_get_line (iter
);
2842 if (selection_index
>= line
->start_index
&&
2843 selection_index
<= line
->start_index
+ line
->length
) {
2844 /* found the line with the start of the selection */
2849 } while (pango_layout_iter_next_line (iter
));
2853 gdouble clip_width
, clip_height
;
2854 /* gboolean trailing = FALSE; */
2855 PangoRectangle pango_pos
;
2857 if (selection_index
> 0 && selection_index
==
2858 cur_line
->start_index
+ cur_line
->length
) {
2860 /* trailing = TRUE; */
2863 pango_layout_index_to_pos (text
->layout
, selection_index
, &pango_pos
);
2865 pango_pos
.x
= PANGO_PIXELS (pango_pos
.x
);
2866 pango_pos
.y
= PANGO_PIXELS (pango_pos
.y
);
2867 pango_pos
.width
= (pango_pos
.width
+ PANGO_SCALE
/ 2) / PANGO_SCALE
;
2868 pango_pos
.height
= (pango_pos
.height
+ PANGO_SCALE
/ 2) / PANGO_SCALE
;
2871 xpos
= pango_pos
.x
; /* + (trailing ? 0 : pango_pos.width);*/
2873 if (xpos
+ 2 < text
->xofs_edit
) {
2874 text
->xofs_edit
= xpos
;
2877 clip_width
= text
->clip_width
;
2879 if (xpos
+ pango_pos
.width
- clip_width
> text
->xofs_edit
) {
2880 text
->xofs_edit
= xpos
+ pango_pos
.width
- clip_width
;
2884 if (pango_pos
.y
+ 2 < text
->yofs_edit
) {
2886 text
->yofs_edit
= ypos
;
2889 ypos
= pango_pos
.y
+ pango_pos
.height
;
2892 if (text
->clip_height
< 0)
2893 clip_height
= text
->height
;
2895 clip_height
= text
->clip_height
;
2897 if (ypos
- clip_height
> text
->yofs_edit
) {
2898 text
->yofs_edit
= ypos
- clip_height
;
2903 pango_layout_iter_free (iter
);
2906 text
->needs_redraw
= 1;
2907 gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text
));
2910 /* Class initialization function for the text item */
2912 e_text_class_init (ETextClass
*class)
2914 GObjectClass
*gobject_class
;
2915 GnomeCanvasItemClass
*item_class
;
2917 gobject_class
= (GObjectClass
*) class;
2918 item_class
= (GnomeCanvasItemClass
*) class;
2920 gobject_class
->dispose
= e_text_dispose
;
2921 gobject_class
->set_property
= e_text_set_property
;
2922 gobject_class
->get_property
= e_text_get_property
;
2924 item_class
->update
= e_text_update
;
2925 item_class
->realize
= e_text_realize
;
2926 item_class
->unrealize
= e_text_unrealize
;
2927 item_class
->draw
= e_text_draw
;
2928 item_class
->point
= e_text_point
;
2929 item_class
->bounds
= e_text_bounds
;
2930 item_class
->event
= e_text_event
;
2932 class->changed
= NULL
;
2933 class->activate
= NULL
;
2935 e_text_signals
[E_TEXT_CHANGED
] = g_signal_new (
2937 G_OBJECT_CLASS_TYPE (gobject_class
),
2939 G_STRUCT_OFFSET (ETextClass
, changed
),
2941 g_cclosure_marshal_VOID__VOID
,
2944 e_text_signals
[E_TEXT_ACTIVATE
] = g_signal_new (
2946 G_OBJECT_CLASS_TYPE (gobject_class
),
2948 G_STRUCT_OFFSET (ETextClass
, activate
),
2950 g_cclosure_marshal_VOID__VOID
,
2953 e_text_signals
[E_TEXT_KEYPRESS
] = g_signal_new (
2955 G_OBJECT_CLASS_TYPE (gobject_class
),
2957 G_STRUCT_OFFSET (ETextClass
, keypress
),
2959 e_marshal_VOID__INT_INT
,
2964 e_text_signals
[E_TEXT_POPULATE_POPUP
] = g_signal_new (
2966 G_OBJECT_CLASS_TYPE (gobject_class
),
2968 G_STRUCT_OFFSET (ETextClass
, populate_popup
),
2970 e_marshal_VOID__POINTER_INT_OBJECT
,
2976 g_object_class_install_property (
2979 g_param_spec_object (
2984 G_PARAM_READWRITE
));
2986 g_object_class_install_property (
2988 PROP_EVENT_PROCESSOR
,
2989 g_param_spec_object (
2993 E_TYPE_TEXT_EVENT_PROCESSOR
,
2994 G_PARAM_READWRITE
));
2996 g_object_class_install_property (
2999 g_param_spec_string (
3004 G_PARAM_READWRITE
));
3006 g_object_class_install_property (
3009 g_param_spec_boolean (
3014 G_PARAM_READWRITE
));
3016 g_object_class_install_property (
3019 g_param_spec_boolean (
3024 G_PARAM_READWRITE
));
3026 g_object_class_install_property (
3029 g_param_spec_boolean (
3034 G_PARAM_READWRITE
));
3036 g_object_class_install_property (
3043 GTK_TYPE_JUSTIFICATION
,
3045 G_PARAM_READWRITE
));
3047 g_object_class_install_property (
3050 g_param_spec_double (
3054 0.0, G_MAXDOUBLE
, 0.0,
3055 G_PARAM_READWRITE
));
3057 g_object_class_install_property (
3060 g_param_spec_double (
3064 0.0, G_MAXDOUBLE
, 0.0,
3065 G_PARAM_READWRITE
));
3067 g_object_class_install_property (
3070 g_param_spec_boolean (
3075 G_PARAM_READWRITE
));
3077 g_object_class_install_property (
3079 PROP_FILL_CLIP_RECTANGLE
,
3080 g_param_spec_boolean (
3081 "fill_clip_rectangle",
3082 "Fill clip rectangle",
3083 "Fill clip rectangle",
3085 G_PARAM_READWRITE
));
3087 g_object_class_install_property (
3090 g_param_spec_double (
3094 0.0, G_MAXDOUBLE
, 0.0,
3095 G_PARAM_READWRITE
));
3097 g_object_class_install_property (
3100 g_param_spec_double (
3104 0.0, G_MAXDOUBLE
, 0.0,
3105 G_PARAM_READWRITE
));
3107 g_object_class_install_property (
3110 g_param_spec_string (
3117 g_object_class_install_property (
3119 PROP_FILL_COLOR_GDK
,
3120 g_param_spec_boxed (
3127 g_object_class_install_property (
3129 PROP_FILL_COLOR_RGBA
,
3135 G_PARAM_READWRITE
));
3137 g_object_class_install_property (
3140 g_param_spec_double (
3144 0.0, G_MAXDOUBLE
, 0.0,
3147 g_object_class_install_property (
3150 g_param_spec_double (
3154 0.0, G_MAXDOUBLE
, 0.0,
3157 g_object_class_install_property (
3160 g_param_spec_boolean (
3165 G_PARAM_READWRITE
));
3167 g_object_class_install_property (
3170 g_param_spec_boolean (
3175 G_PARAM_READWRITE
));
3177 g_object_class_install_property (
3180 g_param_spec_string (
3185 G_PARAM_READWRITE
));
3187 g_object_class_install_property (
3190 g_param_spec_boolean (
3195 G_PARAM_READWRITE
));
3197 g_object_class_install_property (
3199 PROP_BREAK_CHARACTERS
,
3200 g_param_spec_string (
3205 G_PARAM_READWRITE
));
3207 g_object_class_install_property (
3208 gobject_class
, PROP_MAX_LINES
,
3214 G_PARAM_READWRITE
));
3216 g_object_class_install_property (
3219 g_param_spec_double (
3223 0.0, G_MAXDOUBLE
, 0.0,
3224 G_PARAM_READWRITE
));
3226 g_object_class_install_property (
3229 g_param_spec_double (
3233 0.0, G_MAXDOUBLE
, 0.0,
3234 G_PARAM_READWRITE
));
3236 g_object_class_install_property (
3238 PROP_ALLOW_NEWLINES
,
3239 g_param_spec_boolean (
3244 G_PARAM_READWRITE
));
3246 g_object_class_install_property (
3254 G_PARAM_READWRITE
));
3256 g_object_class_install_property (
3259 g_param_spec_object (
3263 GTK_TYPE_IM_CONTEXT
,
3264 G_PARAM_READWRITE
));
3266 g_object_class_install_property (
3269 g_param_spec_boolean (
3274 G_PARAM_READWRITE
));
3276 if (!clipboard_atom
)
3277 clipboard_atom
= gdk_atom_intern ("CLIPBOARD", FALSE
);
3279 gal_a11y_e_text_init ();
3282 /* Object initialization function for the text item */
3284 e_text_init (EText
*text
)
3286 text
->model
= e_text_model_new ();
3287 text
->text
= e_text_model_get_text (text
->model
);
3288 text
->preedit_len
= 0;
3289 text
->preedit_pos
= 0;
3290 text
->layout
= NULL
;
3292 text
->revert
= NULL
;
3294 text
->model_changed_signal_id
= g_signal_connect (
3295 text
->model
, "changed",
3296 G_CALLBACK (e_text_text_model_changed
), text
);
3298 text
->model_repos_signal_id
= g_signal_connect (
3299 text
->model
, "reposition",
3300 G_CALLBACK (e_text_text_model_reposition
), text
);
3302 text
->justification
= GTK_JUSTIFY_LEFT
;
3303 text
->clip_width
= -1.0;
3304 text
->clip_height
= -1.0;
3308 text
->ellipsis
= NULL
;
3309 text
->use_ellipsis
= FALSE
;
3310 text
->ellipsis_width
= 0;
3312 text
->editable
= FALSE
;
3313 text
->editing
= FALSE
;
3314 text
->xofs_edit
= 0;
3315 text
->yofs_edit
= 0;
3317 text
->selection_start
= 0;
3318 text
->selection_end
= 0;
3319 text
->select_by_word
= FALSE
;
3321 text
->timeout_id
= 0;
3326 text
->last_state
= 0;
3328 text
->scroll_start
= 0;
3329 text
->show_cursor
= TRUE
;
3330 text
->button_down
= FALSE
;
3333 text
->tep_command_id
= 0;
3335 text
->pointer_in
= FALSE
;
3336 text
->default_cursor_shown
= TRUE
;
3337 text
->line_wrap
= FALSE
;
3338 text
->break_characters
= NULL
;
3339 text
->max_lines
= -1;
3340 text
->dbl_timeout
= 0;
3341 text
->tpl_timeout
= 0;
3344 text
->strikeout
= FALSE
;
3345 text
->italic
= FALSE
;
3347 text
->allow_newlines
= TRUE
;
3349 text
->last_type_request
= -1;
3350 text
->last_time_request
= 0;
3351 text
->queued_requests
= NULL
;
3353 text
->im_context
= NULL
;
3354 text
->need_im_reset
= FALSE
;
3355 text
->im_context_signals_registered
= FALSE
;
3357 text
->handle_popup
= FALSE
;
3358 text
->rgba_set
= FALSE
;
3360 e_canvas_item_set_reflow_callback (GNOME_CANVAS_ITEM (text
), e_text_reflow
);
3363 /* IM Context Callbacks */
3365 e_text_commit_cb (GtkIMContext
*context
,
3369 if (g_utf8_validate (str
, strlen (str
), NULL
)) {
3370 if (text
->selection_end
!= text
->selection_start
)
3371 e_text_delete_selection (text
);
3372 e_text_insert (text
, str
);
3373 g_signal_emit (text
, e_text_signals
[E_TEXT_KEYPRESS
], 0, 0, 0);
3378 e_text_preedit_changed_cb (GtkIMContext
*context
,
3381 gchar
*preedit_string
= NULL
;
3384 gtk_im_context_get_preedit_string (
3385 context
, &preedit_string
,
3388 cursor_pos
= CLAMP (cursor_pos
, 0, g_utf8_strlen (preedit_string
, -1));
3389 etext
->preedit_len
= strlen (preedit_string
);
3390 etext
->preedit_pos
= g_utf8_offset_to_pointer (
3391 preedit_string
, cursor_pos
) - preedit_string
;
3392 g_free (preedit_string
);
3394 g_signal_emit (etext
, e_text_signals
[E_TEXT_KEYPRESS
], 0, 0, 0);
3398 e_text_retrieve_surrounding_cb (GtkIMContext
*context
,
3401 gtk_im_context_set_surrounding (
3402 context
, text
->text
, strlen (text
->text
),
3403 g_utf8_offset_to_pointer (text
->text
, MIN (
3404 text
->selection_start
, text
->selection_end
)) - text
->text
);
3410 e_text_delete_surrounding_cb (GtkIMContext
*context
,
3415 e_text_model_delete (
3417 MIN (text
->selection_start
, text
->selection_end
) + offset
,