4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2008 Intel Corporation.
8 * Authored By: Øyvind Kolås <pippin@o-hand.com>
9 * Emmanuele Bassi <ebassi@linux.intel.com>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
26 * SECTION:clutter-text
27 * @short_description: An actor for displaying and editing text
29 * #ClutterText is an actor that displays custom text using Pango
30 * as the text rendering engine.
32 * #ClutterText also allows inline editing of the text if the
33 * actor is set editable using clutter_text_set_editable().
35 * Selection using keyboard or pointers can be enabled using
36 * clutter_text_set_selectable().
38 * #ClutterText is available since Clutter 1.0
48 #include "clutter-text.h"
50 #include "clutter-actor-private.h"
51 #include "clutter-animatable.h"
52 #include "clutter-binding-pool.h"
53 #include "clutter-color.h"
54 #include "clutter-debug.h"
55 #include "clutter-enum-types.h"
56 #include "clutter-keysyms.h"
57 #include "clutter-main.h"
58 #include "clutter-marshal.h"
59 #include "clutter-private.h" /* includes <cogl-pango/cogl-pango.h> */
60 #include "clutter-profile.h"
61 #include "clutter-property-transition.h"
62 #include "clutter-text-buffer.h"
63 #include "clutter-units.h"
64 #include "clutter-paint-volume-private.h"
65 #include "clutter-scriptable.h"
67 /* cursor width in pixels */
68 #define DEFAULT_CURSOR_SIZE 2
70 /* We need at least three cached layouts to run the allocation without
71 * regenerating a new layout. First the layout will be generated at
72 * full width to get the preferred width, then it will be generated at
73 * the preferred width to get the preferred height and then it might
74 * be regenerated at a different width to get the height for the
75 * actual allocated width
77 * since we might get multiple queries from layout managers doing a
78 * double-pass allocations, like tabular ones, we should use 6 slots
80 #define N_CACHED_LAYOUTS 6
82 #define CLUTTER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate))
84 typedef struct _LayoutCache LayoutCache;
86 static const ClutterColor default_cursor_color = { 0, 0, 0, 255 };
87 static const ClutterColor default_selection_color = { 0, 0, 0, 255 };
88 static const ClutterColor default_text_color = { 0, 0, 0, 255 };
89 static const ClutterColor default_selected_text_color = { 0, 0, 0, 255 };
91 static ClutterAnimatableIface *parent_animatable_iface = NULL;
92 static ClutterScriptableIface *parent_scriptable_iface = NULL;
94 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
95 static void clutter_animatable_iface_init (ClutterAnimatableIface *iface);
97 G_DEFINE_TYPE_WITH_CODE (ClutterText,
100 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
101 clutter_scriptable_iface_init)
102 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
103 clutter_animatable_iface_init));
107 /* Cached layout. Pango internally caches the computed extents
108 * when they are requested so there is no need to cache that as
113 /* A number representing the age of this cache (so that when a
114 * new layout is needed the last used cache is replaced)
119 struct _ClutterTextPrivate
121 PangoFontDescription *font_desc;
123 /* the displayed text */
124 ClutterTextBuffer *buffer;
130 ClutterColor text_color;
132 LayoutCache cached_layouts[N_CACHED_LAYOUTS];
135 /* These are the attributes set by the attributes property */
136 PangoAttrList *attrs;
137 /* These are the attributes derived from the text when the
138 use-markup property is set */
139 PangoAttrList *markup_attrs;
140 /* This is the combination of the above two lists. It is set to NULL
141 whenever either of them changes and then regenerated by merging
142 the two lists whenever a layout is needed */
143 PangoAttrList *effective_attrs;
144 /* These are the attributes for the preedit string. These are merged
145 with the effective attributes into a temporary list before
147 PangoAttrList *preedit_attrs;
149 /* current cursor position */
152 /* current 'other end of selection' position */
153 gint selection_bound;
155 /* the x position in the PangoLayout, used to
156 * avoid drifting when repeatedly moving up|down
160 /* the x position of the PangoLayout when in
161 * single line mode, to scroll the contents of the
166 /* the y position of the PangoLayout, fixed to 0 by
170 /* Where to draw the cursor */
171 ClutterGeometry cursor_pos;
172 ClutterColor cursor_color;
175 /* Box representing the paint volume. The box is lazily calculated
177 ClutterPaintVolume paint_volume;
179 guint preedit_cursor_pos;
180 gint preedit_n_chars;
182 ClutterColor selection_color;
184 ClutterColor selected_text_color;
186 gunichar password_char;
188 guint password_hint_id;
189 guint password_hint_timeout;
191 /* Signal handler for when the backend changes its font settings */
192 guint settings_changed_id;
194 /* Signal handler for when the :text-direction changes */
195 guint direction_changed_id;
200 guint use_underline : 1;
201 guint use_markup : 1;
203 guint single_line_mode : 1;
207 guint cursor_visible : 1;
208 guint activatable : 1;
209 guint selectable : 1;
210 guint selection_color_set : 1;
211 guint in_select_drag : 1;
212 guint cursor_color_set : 1;
213 guint preedit_set : 1;
214 guint is_default_font : 1;
216 guint selected_text_color_set : 1;
217 guint paint_volume_valid : 1;
218 guint show_password_hint : 1;
219 guint password_hint_visible : 1;
228 PROP_FONT_DESCRIPTION,
239 PROP_SELECTION_BOUND,
240 PROP_SELECTION_COLOR,
241 PROP_SELECTION_COLOR_SET,
244 PROP_CURSOR_COLOR_SET,
251 PROP_SINGLE_LINE_MODE,
252 PROP_SELECTED_TEXT_COLOR,
253 PROP_SELECTED_TEXT_COLOR_SET,
258 static GParamSpec *obj_props[PROP_LAST];
271 static guint text_signals[LAST_SIGNAL] = { 0, };
273 static void clutter_text_settings_changed_cb (ClutterText *text);
274 static void buffer_connect_signals (ClutterText *self);
275 static void buffer_disconnect_signals (ClutterText *self);
276 static ClutterTextBuffer *get_buffer (ClutterText *self);
279 clutter_text_dirty_paint_volume (ClutterText *text)
281 ClutterTextPrivate *priv = text->priv;
283 if (priv->paint_volume_valid)
285 clutter_paint_volume_free (&priv->paint_volume);
286 priv->paint_volume_valid = FALSE;
291 clutter_text_queue_redraw (ClutterActor *self)
293 /* This is a wrapper for clutter_actor_queue_redraw that also
294 dirties the cached paint volume. It would be nice if we could
295 just override the default implementation of the queue redraw
296 signal to do this instead but that doesn't work because the
297 signal isn't immediately emitted when queue_redraw is called.
298 Clutter will however immediately call get_paint_volume when
299 queue_redraw is called so we do need to dirty it immediately. */
301 clutter_text_dirty_paint_volume (CLUTTER_TEXT (self));
303 clutter_actor_queue_redraw (self);
306 #define clutter_actor_queue_redraw \
307 Please_use_clutter_text_queue_redraw_instead
309 #define offset_real(t,p) ((p) == -1 ? g_utf8_strlen ((t), -1) : (p))
312 offset_to_bytes (const gchar *text,
318 return strlen (text);
320 /* Loop over each character in the string until we either reach the
321 end or the requested position */
322 for (ptr = text; *ptr && pos-- > 0; ptr = g_utf8_next_char (ptr));
327 #define bytes_to_offset(t,p) (g_utf8_pointer_to_offset ((t), (t) + (p)))
330 clutter_text_clear_selection (ClutterText *self)
332 ClutterTextPrivate *priv = self->priv;
334 if (priv->selection_bound != priv->position)
336 priv->selection_bound = priv->position;
337 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
338 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
343 clutter_text_get_display_text (ClutterText *self)
345 ClutterTextPrivate *priv = self->priv;
346 ClutterTextBuffer *buffer;
349 buffer = get_buffer (self);
350 text = clutter_text_buffer_get_text (buffer);
352 /* simple short-circuit to avoid going through GString
353 * with an empty text and a password char set
356 return g_strdup ("");
358 if (G_LIKELY (priv->password_char == 0))
359 return g_strdup (text);
363 gunichar invisible_char;
368 n_chars = clutter_text_buffer_get_length (buffer);
369 str = g_string_sized_new (clutter_text_buffer_get_bytes (buffer));
370 invisible_char = priv->password_char;
372 /* we need to convert the string built of invisible
373 * characters into UTF-8 for it to be fed to the Pango
376 memset (buf, 0, sizeof (buf));
377 char_len = g_unichar_to_utf8 (invisible_char, buf);
379 if (priv->show_password_hint && priv->password_hint_visible)
383 for (i = 0; i < n_chars - 1; i++)
384 g_string_append_len (str, buf, char_len);
386 last_char = g_utf8_offset_to_pointer (text, n_chars - 1);
387 g_string_append (str, last_char);
391 for (i = 0; i < n_chars; i++)
392 g_string_append_len (str, buf, char_len);
395 return g_string_free (str, FALSE);
400 clutter_text_ensure_effective_attributes (ClutterText *self)
402 ClutterTextPrivate *priv = self->priv;
404 /* If we already have the effective attributes then we don't need to
406 if (priv->effective_attrs != NULL)
409 /* same as if we don't have any attribute at all */
410 if (priv->attrs == NULL && priv->markup_attrs == NULL)
413 if (priv->attrs != NULL)
415 /* If there are no markup attributes then we can just use
416 these attributes directly */
417 if (priv->markup_attrs == NULL)
418 priv->effective_attrs = pango_attr_list_ref (priv->attrs);
421 /* Otherwise we need to merge the two lists */
422 PangoAttrIterator *iter;
423 GSList *attributes, *l;
425 priv->effective_attrs = pango_attr_list_copy (priv->markup_attrs);
427 iter = pango_attr_list_get_iterator (priv->attrs);
430 attributes = pango_attr_iterator_get_attrs (iter);
432 for (l = attributes; l != NULL; l = l->next)
434 PangoAttribute *attr = l->data;
436 pango_attr_list_insert (priv->effective_attrs, attr);
439 g_slist_free (attributes);
441 while (pango_attr_iterator_next (iter));
443 pango_attr_iterator_destroy (iter);
446 else if (priv->markup_attrs != NULL)
448 /* We can just use the markup attributes directly */
449 priv->effective_attrs = pango_attr_list_ref (priv->markup_attrs);
454 clutter_text_create_layout_no_cache (ClutterText *text,
457 PangoEllipsizeMode ellipsize)
459 ClutterTextPrivate *priv = text->priv;
464 CLUTTER_STATIC_TIMER (text_layout_timer,
470 CLUTTER_TIMER_START (_clutter_uprof_context, text_layout_timer);
472 layout = clutter_actor_create_pango_layout (CLUTTER_ACTOR (text), NULL);
473 pango_layout_set_font_description (layout, priv->font_desc);
475 contents = clutter_text_get_display_text (text);
476 contents_len = strlen (contents);
478 if (priv->editable && priv->preedit_set)
480 GString *tmp = g_string_new (contents);
481 PangoAttrList *tmp_attrs = pango_attr_list_new ();
484 if (priv->position == 0)
487 cursor_index = offset_to_bytes (contents, priv->position);
489 g_string_insert (tmp, cursor_index, priv->preedit_str);
491 pango_layout_set_text (layout, tmp->str, tmp->len);
493 if (priv->preedit_attrs != NULL)
495 pango_attr_list_splice (tmp_attrs, priv->preedit_attrs,
497 strlen (priv->preedit_str));
499 pango_layout_set_attributes (layout, tmp_attrs);
502 g_string_free (tmp, TRUE);
503 pango_attr_list_unref (tmp_attrs);
506 pango_layout_set_text (layout, contents, contents_len);
510 /* This will merge the markup attributes and the attributes
511 property if needed */
512 clutter_text_ensure_effective_attributes (text);
514 if (priv->effective_attrs != NULL)
515 pango_layout_set_attributes (layout, priv->effective_attrs);
518 pango_layout_set_alignment (layout, priv->alignment);
519 pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode);
520 pango_layout_set_justify (layout, priv->justify);
521 pango_layout_set_wrap (layout, priv->wrap_mode);
523 pango_layout_set_ellipsize (layout, ellipsize);
524 pango_layout_set_width (layout, width);
525 pango_layout_set_height (layout, height);
529 CLUTTER_TIMER_STOP (_clutter_uprof_context, text_layout_timer);
535 clutter_text_dirty_cache (ClutterText *text)
537 ClutterTextPrivate *priv = text->priv;
540 /* Delete the cached layouts so they will be recreated the next time
542 for (i = 0; i < N_CACHED_LAYOUTS; i++)
543 if (priv->cached_layouts[i].layout)
545 g_object_unref (priv->cached_layouts[i].layout);
546 priv->cached_layouts[i].layout = NULL;
549 clutter_text_dirty_paint_volume (text);
553 * clutter_text_set_font_description_internal:
554 * @self: a #ClutterText
555 * @desc: a #PangoFontDescription
557 * Sets @desc as the font description to be used by the #ClutterText
558 * actor. The font description ownership is transferred to @self so
559 * the #PangoFontDescription must not be freed after this function
561 * This function will also set the :font-name field as a side-effect
563 * This function will evict the layout cache, and queue a relayout if
564 * the #ClutterText actor has contents.
567 clutter_text_set_font_description_internal (ClutterText *self,
568 PangoFontDescription *desc)
570 ClutterTextPrivate *priv = self->priv;
572 if (priv->font_desc == desc)
575 if (priv->font_desc != NULL)
576 pango_font_description_free (priv->font_desc);
578 priv->font_desc = desc;
580 /* update the font name string we use */
581 g_free (priv->font_name);
582 priv->font_name = pango_font_description_to_string (priv->font_desc);
584 clutter_text_dirty_cache (self);
586 if (clutter_text_buffer_get_length (get_buffer (self)) != 0)
587 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
589 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_DESCRIPTION]);
593 clutter_text_settings_changed_cb (ClutterText *text)
595 ClutterTextPrivate *priv = text->priv;
596 guint password_hint_time = 0;
597 ClutterSettings *settings;
599 settings = clutter_settings_get_default ();
601 g_object_get (settings, "password-hint-time", &password_hint_time, NULL);
603 priv->show_password_hint = password_hint_time > 0;
604 priv->password_hint_timeout = password_hint_time;
606 if (priv->is_default_font)
608 PangoFontDescription *font_desc;
609 gchar *font_name = NULL;
611 g_object_get (settings, "font-name", &font_name, NULL);
613 CLUTTER_NOTE (ACTOR, "Text[%p]: default font changed to '%s'",
617 font_desc = pango_font_description_from_string (font_name);
618 clutter_text_set_font_description_internal (text, font_desc);
623 clutter_text_dirty_cache (text);
624 clutter_actor_queue_relayout (CLUTTER_ACTOR (text));
628 clutter_text_direction_changed_cb (GObject *gobject,
631 clutter_text_dirty_cache (CLUTTER_TEXT (gobject));
633 /* no need to queue a relayout: set_text_direction() will do that for us */
637 * clutter_text_create_layout:
638 * @text: a #ClutterText
639 * @allocation_width: the allocation width
640 * @allocation_height: the allocation height
642 * Like clutter_text_create_layout_no_cache(), but will also ensure
643 * the glyphs cache. If a previously cached layout generated using the
644 * same width is available then that will be used instead of
645 * generating a new one.
648 clutter_text_create_layout (ClutterText *text,
649 gfloat allocation_width,
650 gfloat allocation_height)
652 ClutterTextPrivate *priv = text->priv;
653 LayoutCache *oldest_cache = priv->cached_layouts;
654 gboolean found_free_cache = FALSE;
657 PangoEllipsizeMode ellipsize = PANGO_ELLIPSIZE_NONE;
660 CLUTTER_STATIC_COUNTER (text_cache_hit_counter,
661 "Text layout cache hit counter",
662 "Increments for each layout cache hit",
664 CLUTTER_STATIC_COUNTER (text_cache_miss_counter,
665 "Text layout cache miss counter",
666 "Increments for each layout cache miss",
669 /* First determine the width, height, and ellipsize mode that
670 * we need for the layout. The ellipsize mode depends on
671 * allocation_width/allocation_size as follows:
673 * Cases, assuming ellipsize != NONE on actor:
675 * Width request: ellipsization can be set or not on layout,
678 * Height request: ellipsization must never be set on layout
679 * if wrap=true, because we need to measure the wrapped
680 * height. It must always be set if wrap=false.
682 * Allocate: ellipsization must always be set.
684 * See http://bugzilla.gnome.org/show_bug.cgi?id=560931
687 if (priv->ellipsize != PANGO_ELLIPSIZE_NONE)
689 if (allocation_height < 0 && priv->wrap)
690 ; /* must not set ellipsization on wrap=true height request */
694 ellipsize = priv->ellipsize;
698 /* When painting, we always need to set the width, since
699 * we might need to align to the right. When getting the
700 * height, however, there are some cases where we know that
701 * the width won't affect the width.
703 * - editable, single-line text actors, since those can
705 * - non-wrapping, non-ellipsizing actors.
707 if (allocation_width >= 0 &&
708 (allocation_height >= 0 ||
709 !((priv->editable && priv->single_line_mode) ||
710 (priv->ellipsize == PANGO_ELLIPSIZE_NONE && !priv->wrap))))
712 width = allocation_width * 1024 + 0.5f;
715 /* Pango only uses height if ellipsization is enabled, so don't set
716 * height if ellipsize isn't set. Pango implicitly enables wrapping
717 * if height is set, so don't set height if wrapping is disabled.
718 * In other words, only set height if we want to both wrap then
719 * ellipsize and we're not in single line mode.
721 * See http://bugzilla.gnome.org/show_bug.cgi?id=560931 if this
724 if (allocation_height >= 0 &&
726 priv->ellipsize != PANGO_ELLIPSIZE_NONE &&
727 !priv->single_line_mode)
729 height = allocation_height * 1024 + 0.5f;
732 /* Search for a cached layout with the same width and keep
733 * track of the oldest one
735 for (i = 0; i < N_CACHED_LAYOUTS; i++)
737 if (priv->cached_layouts[i].layout == NULL)
739 /* Always prefer free cache spaces */
740 found_free_cache = TRUE;
741 oldest_cache = priv->cached_layouts + i;
745 PangoLayout *cached = priv->cached_layouts[i].layout;
746 gint cached_width = pango_layout_get_width (cached);
747 gint cached_height = pango_layout_get_height (cached);
748 gint cached_ellipsize = pango_layout_get_ellipsize (cached);
750 if (cached_width == width &&
751 cached_height == height &&
752 cached_ellipsize == ellipsize)
754 /* If this cached layout is using the same size then we can
755 * just return that directly
758 "ClutterText: %p: cache hit for size %.2fx%.2f",
763 CLUTTER_COUNTER_INC (_clutter_uprof_context,
764 text_cache_hit_counter);
766 return priv->cached_layouts[i].layout;
769 /* When getting the preferred height for a specific width,
770 * we might be able to reuse the layout from getting the
771 * preferred width. If the width that the layout gives
772 * unconstrained is less than the width that we are using
773 * than the height will be unaffected by that width.
775 if (allocation_height < 0 &&
776 cached_width == -1 &&
777 cached_ellipsize == ellipsize)
779 PangoRectangle logical_rect;
781 pango_layout_get_extents (priv->cached_layouts[i].layout,
785 if (logical_rect.width <= width)
787 /* We've been asked for our height for the width we gave as a result
788 * of a _get_preferred_width call
791 "ClutterText: %p: cache hit for size %.2fx%.2f "
792 "(unwrapped width narrower than given width)",
797 CLUTTER_COUNTER_INC (_clutter_uprof_context,
798 text_cache_hit_counter);
800 return priv->cached_layouts[i].layout;
804 if (!found_free_cache &&
805 (priv->cached_layouts[i].age < oldest_cache->age))
807 oldest_cache = priv->cached_layouts + i;
812 CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache miss for size %.2fx%.2f",
817 CLUTTER_COUNTER_INC (_clutter_uprof_context, text_cache_miss_counter);
819 /* If we make it here then we didn't have a cached version so we
820 need to recreate the layout */
821 if (oldest_cache->layout)
822 g_object_unref (oldest_cache->layout);
824 oldest_cache->layout =
825 clutter_text_create_layout_no_cache (text, width, height, ellipsize);
827 cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout);
829 /* Mark the 'time' this cache was created and advance the time */
830 oldest_cache->age = priv->cache_age++;
831 return oldest_cache->layout;
835 * clutter_text_coords_to_position:
836 * @self: a #ClutterText
837 * @x: the X coordinate, relative to the actor
838 * @y: the Y coordinate, relative to the actor
840 * Retrieves the position of the character at the given coordinates.
842 * Return: the position of the character
847 clutter_text_coords_to_position (ClutterText *self,
855 g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
857 /* Take any offset due to scrolling into account, and normalize
858 * the coordinates to PangoScale units
860 px = (x - self->priv->text_x) * PANGO_SCALE;
861 py = (y - self->priv->text_y) * PANGO_SCALE;
863 pango_layout_xy_to_index (clutter_text_get_layout (self),
867 return index_ + trailing;
871 * clutter_text_position_to_coords:
872 * @self: a #ClutterText
873 * @position: position in characters
874 * @x: (out): return location for the X coordinate, or %NULL
875 * @y: (out): return location for the Y coordinate, or %NULL
876 * @line_height: (out): return location for the line height, or %NULL
878 * Retrieves the coordinates of the given @position.
880 * Return value: %TRUE if the conversion was successful
885 clutter_text_position_to_coords (ClutterText *self,
891 ClutterTextPrivate *priv;
894 gint password_char_bytes = 1;
898 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
902 n_chars = clutter_text_buffer_get_length (get_buffer (self));
903 if (priv->preedit_set)
904 n_chars += priv->preedit_n_chars;
906 if (position < -1 || position > n_chars)
909 if (priv->password_char != 0)
910 password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL);
914 if (priv->password_char == 0)
916 n_bytes = clutter_text_buffer_get_bytes (get_buffer (self));
917 if (priv->editable && priv->preedit_set)
918 index_ = n_bytes + strlen (priv->preedit_str);
923 index_ = n_chars * password_char_bytes;
925 else if (position == 0)
931 gchar *text = clutter_text_get_display_text (self);
932 GString *tmp = g_string_new (text);
935 cursor_index = offset_to_bytes (text, priv->position);
937 if (priv->preedit_str != NULL)
938 g_string_insert (tmp, cursor_index, priv->preedit_str);
940 if (priv->password_char == 0)
941 index_ = offset_to_bytes (tmp->str, position);
943 index_ = position * password_char_bytes;
946 g_string_free (tmp, TRUE);
949 pango_layout_get_cursor_pos (clutter_text_get_layout (self),
955 *x = (gfloat) rect.x / 1024.0f;
957 /* Take any offset due to scrolling into account */
958 if (priv->single_line_mode)
963 *y = (gfloat) rect.y / 1024.0f;
966 *line_height = (gfloat) rect.height / 1024.0f;
972 clutter_text_ensure_cursor_position (ClutterText *self)
974 ClutterTextPrivate *priv = self->priv;
975 gfloat x, y, cursor_height;
976 ClutterGeometry cursor_pos = { 0, };
977 gboolean x_changed, y_changed;
978 gboolean width_changed, height_changed;
981 position = priv->position;
983 if (priv->editable && priv->preedit_set)
986 position = clutter_text_buffer_get_length (get_buffer (self));
987 position += priv->preedit_cursor_pos;
990 CLUTTER_NOTE (MISC, "Cursor at %d (preedit %s at pos: %d)",
992 priv->preedit_set ? "set" : "unset",
993 priv->preedit_set ? priv->preedit_cursor_pos : 0);
995 x = y = cursor_height = 0;
996 clutter_text_position_to_coords (self, position,
1001 cursor_pos.y = y + 2;
1002 cursor_pos.width = priv->cursor_size;
1003 cursor_pos.height = cursor_height - 4;
1005 x_changed = priv->cursor_pos.x != cursor_pos.x;
1006 y_changed = priv->cursor_pos.y != cursor_pos.y;
1007 width_changed = priv->cursor_pos.width != cursor_pos.width;
1008 height_changed = priv->cursor_pos.height != cursor_pos.height;
1010 if (x_changed || y_changed || width_changed || height_changed)
1012 priv->cursor_pos = cursor_pos;
1014 g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos);
1019 * clutter_text_delete_selection:
1020 * @self: a #ClutterText
1022 * Deletes the currently selected text
1024 * This function is only useful in subclasses of #ClutterText
1026 * Return value: %TRUE if text was deleted or if the text actor
1027 * is empty, and %FALSE otherwise
1032 clutter_text_delete_selection (ClutterText *self)
1034 ClutterTextPrivate *priv;
1037 gint old_position, old_selection;
1040 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
1044 n_chars = clutter_text_buffer_get_length (get_buffer (self));
1048 start_index = priv->position == -1 ? n_chars : priv->position;
1049 end_index = priv->selection_bound == -1 ? n_chars : priv->selection_bound;
1051 if (end_index == start_index)
1054 if (end_index < start_index)
1056 gint temp = start_index;
1057 start_index = end_index;
1061 old_position = priv->position;
1062 old_selection = priv->selection_bound;
1064 clutter_text_delete_text (self, start_index, end_index);
1066 priv->position = start_index;
1067 priv->selection_bound = start_index;
1069 /* Not required to be guarded by g_object_freeze/thaw_notify */
1070 if (priv->position != old_position)
1071 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]);
1073 if (priv->selection_bound != old_selection)
1074 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
1080 * Utility function to update both cursor position and selection bound
1084 clutter_text_set_positions (ClutterText *self,
1088 g_object_freeze_notify (G_OBJECT (self));
1089 clutter_text_set_cursor_position (self, new_pos);
1090 clutter_text_set_selection_bound (self, new_bound);
1091 g_object_thaw_notify (G_OBJECT (self));
1095 clutter_text_set_markup_internal (ClutterText *self,
1098 ClutterTextPrivate *priv = self->priv;
1101 PangoAttrList *attrs = NULL;
1104 g_assert (str != NULL);
1107 res = pango_parse_markup (str, -1, 0,
1115 if (G_LIKELY (error != NULL))
1117 g_warning ("Failed to set the markup of the actor '%s': %s",
1118 _clutter_actor_get_debug_name (CLUTTER_ACTOR (self)),
1120 g_error_free (error);
1123 g_warning ("Failed to set the markup of the actor '%s'",
1124 _clutter_actor_get_debug_name (CLUTTER_ACTOR (self)));
1131 clutter_text_buffer_set_text (get_buffer (self), text, -1);
1135 /* Store the new markup attributes */
1136 if (priv->markup_attrs != NULL)
1137 pango_attr_list_unref (priv->markup_attrs);
1139 priv->markup_attrs = attrs;
1141 /* Clear the effective attributes so they will be regenerated when a
1142 layout is created */
1143 if (priv->effective_attrs != NULL)
1145 pango_attr_list_unref (priv->effective_attrs);
1146 priv->effective_attrs = NULL;
1151 clutter_text_set_property (GObject *gobject,
1153 const GValue *value,
1156 ClutterText *self = CLUTTER_TEXT (gobject);
1161 clutter_text_set_buffer (self, g_value_get_object (value));
1166 const char *str = g_value_get_string (value);
1167 if (self->priv->use_markup)
1168 clutter_text_set_markup_internal (self, str ? str : "");
1170 clutter_text_buffer_set_text (get_buffer (self), str ? str : "", -1);
1175 clutter_text_set_color (self, clutter_value_get_color (value));
1178 case PROP_FONT_NAME:
1179 clutter_text_set_font_name (self, g_value_get_string (value));
1182 case PROP_FONT_DESCRIPTION:
1183 clutter_text_set_font_description (self, g_value_get_boxed (value));
1186 case PROP_USE_MARKUP:
1187 clutter_text_set_use_markup (self, g_value_get_boolean (value));
1190 case PROP_ATTRIBUTES:
1191 clutter_text_set_attributes (self, g_value_get_boxed (value));
1194 case PROP_LINE_ALIGNMENT:
1195 clutter_text_set_line_alignment (self, g_value_get_enum (value));
1198 case PROP_LINE_WRAP:
1199 clutter_text_set_line_wrap (self, g_value_get_boolean (value));
1202 case PROP_LINE_WRAP_MODE:
1203 clutter_text_set_line_wrap_mode (self, g_value_get_enum (value));
1207 clutter_text_set_justify (self, g_value_get_boolean (value));
1210 case PROP_ELLIPSIZE:
1211 clutter_text_set_ellipsize (self, g_value_get_enum (value));
1215 clutter_text_set_cursor_position (self, g_value_get_int (value));
1218 case PROP_SELECTION_BOUND:
1219 clutter_text_set_selection_bound (self, g_value_get_int (value));
1222 case PROP_SELECTION_COLOR:
1223 clutter_text_set_selection_color (self, g_value_get_boxed (value));
1226 case PROP_CURSOR_VISIBLE:
1227 clutter_text_set_cursor_visible (self, g_value_get_boolean (value));
1230 case PROP_CURSOR_COLOR:
1231 clutter_text_set_cursor_color (self, g_value_get_boxed (value));
1234 case PROP_CURSOR_SIZE:
1235 clutter_text_set_cursor_size (self, g_value_get_int (value));
1239 clutter_text_set_editable (self, g_value_get_boolean (value));
1242 case PROP_ACTIVATABLE:
1243 clutter_text_set_activatable (self, g_value_get_boolean (value));
1246 case PROP_SELECTABLE:
1247 clutter_text_set_selectable (self, g_value_get_boolean (value));
1250 case PROP_PASSWORD_CHAR:
1251 clutter_text_set_password_char (self, g_value_get_uint (value));
1254 case PROP_MAX_LENGTH:
1255 clutter_text_set_max_length (self, g_value_get_int (value));
1258 case PROP_SINGLE_LINE_MODE:
1259 clutter_text_set_single_line_mode (self, g_value_get_boolean (value));
1262 case PROP_SELECTED_TEXT_COLOR:
1263 clutter_text_set_selected_text_color (self, clutter_value_get_color (value));
1267 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1272 clutter_text_get_property (GObject *gobject,
1277 ClutterText *self = CLUTTER_TEXT (gobject);
1278 ClutterTextPrivate *priv = self->priv;
1283 g_value_set_object (value, clutter_text_get_buffer (self));
1287 g_value_set_string (value, clutter_text_buffer_get_text (get_buffer (self)));
1290 case PROP_FONT_NAME:
1291 g_value_set_string (value, priv->font_name);
1294 case PROP_FONT_DESCRIPTION:
1295 g_value_set_boxed (value, priv->font_desc);
1298 case PROP_USE_MARKUP:
1299 g_value_set_boolean (value, priv->use_markup);
1303 clutter_value_set_color (value, &priv->text_color);
1306 case PROP_CURSOR_VISIBLE:
1307 g_value_set_boolean (value, priv->cursor_visible);
1310 case PROP_CURSOR_COLOR:
1311 clutter_value_set_color (value, &priv->cursor_color);
1314 case PROP_CURSOR_COLOR_SET:
1315 g_value_set_boolean (value, priv->cursor_color_set);
1318 case PROP_CURSOR_SIZE:
1319 g_value_set_int (value, priv->cursor_size);
1323 g_value_set_int (value, priv->position);
1326 case PROP_SELECTION_BOUND:
1327 g_value_set_int (value, priv->selection_bound);
1331 g_value_set_boolean (value, priv->editable);
1334 case PROP_SELECTABLE:
1335 g_value_set_boolean (value, priv->selectable);
1338 case PROP_SELECTION_COLOR:
1339 clutter_value_set_color (value, &priv->selection_color);
1342 case PROP_SELECTION_COLOR_SET:
1343 g_value_set_boolean (value, priv->selection_color_set);
1346 case PROP_ACTIVATABLE:
1347 g_value_set_boolean (value, priv->activatable);
1350 case PROP_PASSWORD_CHAR:
1351 g_value_set_uint (value, priv->password_char);
1354 case PROP_MAX_LENGTH:
1355 g_value_set_int (value, clutter_text_buffer_get_max_length (get_buffer (self)));
1358 case PROP_SINGLE_LINE_MODE:
1359 g_value_set_boolean (value, priv->single_line_mode);
1362 case PROP_ELLIPSIZE:
1363 g_value_set_enum (value, priv->ellipsize);
1366 case PROP_LINE_WRAP:
1367 g_value_set_boolean (value, priv->wrap);
1370 case PROP_LINE_WRAP_MODE:
1371 g_value_set_enum (value, priv->wrap_mode);
1374 case PROP_LINE_ALIGNMENT:
1375 g_value_set_enum (value, priv->alignment);
1379 g_value_set_boolean (value, priv->justify);
1382 case PROP_ATTRIBUTES:
1383 g_value_set_boxed (value, priv->attrs);
1386 case PROP_SELECTED_TEXT_COLOR:
1387 clutter_value_set_color (value, &priv->selected_text_color);
1390 case PROP_SELECTED_TEXT_COLOR_SET:
1391 g_value_set_boolean (value, priv->selected_text_color_set);
1395 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1400 clutter_text_dispose (GObject *gobject)
1402 ClutterText *self = CLUTTER_TEXT (gobject);
1403 ClutterTextPrivate *priv = self->priv;
1405 /* get rid of the entire cache */
1406 clutter_text_dirty_cache (self);
1408 if (priv->direction_changed_id)
1410 g_signal_handler_disconnect (self, priv->direction_changed_id);
1411 priv->direction_changed_id = 0;
1414 if (priv->settings_changed_id)
1416 g_signal_handler_disconnect (clutter_get_default_backend (),
1417 priv->settings_changed_id);
1418 priv->settings_changed_id = 0;
1421 if (priv->password_hint_id)
1423 g_source_remove (priv->password_hint_id);
1424 priv->password_hint_id = 0;
1427 clutter_text_set_buffer (self, NULL);
1429 G_OBJECT_CLASS (clutter_text_parent_class)->dispose (gobject);
1433 clutter_text_finalize (GObject *gobject)
1435 ClutterText *self = CLUTTER_TEXT (gobject);
1436 ClutterTextPrivate *priv = self->priv;
1438 if (priv->font_desc)
1439 pango_font_description_free (priv->font_desc);
1442 pango_attr_list_unref (priv->attrs);
1443 if (priv->markup_attrs)
1444 pango_attr_list_unref (priv->markup_attrs);
1445 if (priv->effective_attrs)
1446 pango_attr_list_unref (priv->effective_attrs);
1447 if (priv->preedit_attrs)
1448 pango_attr_list_unref (priv->preedit_attrs);
1450 clutter_text_dirty_paint_volume (self);
1452 clutter_text_set_buffer (self, NULL);
1453 g_free (priv->font_name);
1455 G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject);
1458 typedef void (* ClutterTextSelectionFunc) (ClutterText *text,
1459 const ClutterActorBox *box,
1460 gpointer user_data);
1463 clutter_text_foreach_selection_rectangle (ClutterText *self,
1464 ClutterTextSelectionFunc func,
1467 ClutterTextPrivate *priv = self->priv;
1468 PangoLayout *layout = clutter_text_get_layout (self);
1469 gchar *utf8 = clutter_text_get_display_text (self);
1475 if (priv->position == 0)
1478 start_index = offset_to_bytes (utf8, priv->position);
1480 if (priv->selection_bound == 0)
1483 end_index = offset_to_bytes (utf8, priv->selection_bound);
1485 if (start_index > end_index)
1487 gint temp = start_index;
1488 start_index = end_index;
1492 lines = pango_layout_get_line_count (layout);
1494 for (line_no = 0; line_no < lines; line_no++)
1496 PangoLayoutLine *line;
1502 ClutterActorBox box;
1505 line = pango_layout_get_line_readonly (layout, line_no);
1506 pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL);
1507 if (maxindex < start_index)
1510 pango_layout_line_get_x_ranges (line, start_index, end_index,
1513 pango_layout_line_x_to_index (line, 0, &index_, NULL);
1515 clutter_text_position_to_coords (self,
1516 bytes_to_offset (utf8, index_),
1520 box.y2 = y + height;
1522 for (i = 0; i < n_ranges; i++)
1527 range_x = ranges[i * 2] / PANGO_SCALE;
1529 /* Account for any scrolling in single line mode */
1530 if (priv->single_line_mode)
1531 range_x += priv->text_x;
1534 range_width = ((gfloat) ranges[i * 2 + 1] - (gfloat) ranges[i * 2])
1538 box.x2 = ceilf (range_x + range_width + .5f);
1540 func (self, &box, user_data);
1550 add_selection_rectangle_to_path (ClutterText *text,
1551 const ClutterActorBox *box,
1554 cogl_path_rectangle (user_data, box->x1, box->y1, box->x2, box->y2);
1557 /* Draws the selected text, its background, and the cursor */
1559 selection_paint (ClutterText *self)
1561 ClutterTextPrivate *priv = self->priv;
1562 ClutterActor *actor = CLUTTER_ACTOR (self);
1563 guint8 paint_opacity = clutter_actor_get_paint_opacity (actor);
1565 if (!priv->has_focus)
1568 if (priv->editable && priv->cursor_visible)
1570 const ClutterColor *color;
1573 position = priv->position;
1575 if (position == priv->selection_bound)
1577 /* No selection, just draw the cursor */
1578 if (priv->cursor_color_set)
1579 color = &priv->cursor_color;
1581 color = &priv->text_color;
1583 cogl_set_source_color4ub (color->red,
1590 cogl_rectangle (priv->cursor_pos.x,
1592 priv->cursor_pos.x + priv->cursor_pos.width,
1593 priv->cursor_pos.y + priv->cursor_pos.height);
1597 /* Paint selection background first */
1598 PangoLayout *layout = clutter_text_get_layout (self);
1599 CoglPath *selection_path = cogl_path_new ();
1600 CoglColor cogl_color = { 0, };
1602 /* Paint selection background */
1603 if (priv->selection_color_set)
1604 color = &priv->selection_color;
1605 else if (priv->cursor_color_set)
1606 color = &priv->cursor_color;
1608 color = &priv->text_color;
1610 cogl_set_source_color4ub (color->red,
1613 paint_opacity * color->alpha / 255);
1615 clutter_text_foreach_selection_rectangle (self,
1616 add_selection_rectangle_to_path,
1619 cogl_path_fill (selection_path);
1621 /* Paint selected text */
1622 cogl_framebuffer_push_path_clip (cogl_get_draw_framebuffer (),
1624 cogl_object_unref (selection_path);
1626 if (priv->selected_text_color_set)
1627 color = &priv->selected_text_color;
1629 color = &priv->text_color;
1631 cogl_color_init_from_4ub (&cogl_color,
1635 paint_opacity * color->alpha / 255);
1637 cogl_pango_render_layout (layout, priv->text_x, 0, &cogl_color, 0);
1645 clutter_text_move_word_backward (ClutterText *self,
1648 gint retval = start;
1650 if (clutter_text_buffer_get_length (get_buffer (self)) > 0 && start > 0)
1652 PangoLayout *layout = clutter_text_get_layout (self);
1653 PangoLogAttr *log_attrs = NULL;
1656 pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
1659 while (retval > 0 && !log_attrs[retval].is_word_start)
1669 clutter_text_move_word_forward (ClutterText *self,
1672 gint retval = start;
1675 n_chars = clutter_text_buffer_get_length (get_buffer (self));
1676 if (n_chars > 0 && start < n_chars)
1678 PangoLayout *layout = clutter_text_get_layout (self);
1679 PangoLogAttr *log_attrs = NULL;
1682 pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
1685 while (retval < n_chars && !log_attrs[retval].is_word_end)
1695 clutter_text_move_line_start (ClutterText *self,
1698 PangoLayoutLine *layout_line;
1699 PangoLayout *layout;
1705 layout = clutter_text_get_layout (self);
1706 text = clutter_text_buffer_get_text (get_buffer (self));
1711 index_ = offset_to_bytes (text, start);
1713 pango_layout_index_to_line_x (layout, index_,
1717 layout_line = pango_layout_get_line_readonly (layout, line_no);
1721 pango_layout_line_x_to_index (layout_line, 0, &index_, NULL);
1723 position = bytes_to_offset (text, index_);
1729 clutter_text_move_line_end (ClutterText *self,
1732 ClutterTextPrivate *priv = self->priv;
1733 PangoLayoutLine *layout_line;
1734 PangoLayout *layout;
1741 layout = clutter_text_get_layout (self);
1742 text = clutter_text_buffer_get_text (get_buffer (self));
1747 index_ = offset_to_bytes (text, priv->position);
1749 pango_layout_index_to_line_x (layout, index_,
1753 layout_line = pango_layout_get_line_readonly (layout, line_no);
1757 pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing);
1760 position = bytes_to_offset (text, index_);
1766 clutter_text_select_word (ClutterText *self)
1768 gint cursor_pos = self->priv->position;
1769 gint start_pos, end_pos;
1771 start_pos = clutter_text_move_word_backward (self, cursor_pos);
1772 end_pos = clutter_text_move_word_forward (self, cursor_pos);
1774 clutter_text_set_selection (self, start_pos, end_pos);
1778 clutter_text_select_line (ClutterText *self)
1780 ClutterTextPrivate *priv = self->priv;
1781 gint cursor_pos = priv->position;
1782 gint start_pos, end_pos;
1784 if (priv->single_line_mode)
1791 start_pos = clutter_text_move_line_start (self, cursor_pos);
1792 end_pos = clutter_text_move_line_end (self, cursor_pos);
1795 clutter_text_set_selection (self, start_pos, end_pos);
1799 clutter_text_button_press (ClutterActor *actor,
1800 ClutterButtonEvent *event)
1802 ClutterText *self = CLUTTER_TEXT (actor);
1803 ClutterTextPrivate *priv = self->priv;
1804 gboolean res = FALSE;
1808 /* we'll steal keyfocus if we need it */
1809 if (priv->editable || priv->selectable)
1810 clutter_actor_grab_key_focus (actor);
1812 return CLUTTER_EVENT_PROPAGATE;
1814 /* if the actor is empty we just reset everything and not
1815 * set up the dragging of the selection since there's nothing
1818 if (clutter_text_buffer_get_length (get_buffer (self)) == 0)
1820 clutter_text_set_positions (self, -1, -1);
1822 return CLUTTER_EVENT_STOP;
1825 res = clutter_actor_transform_stage_point (actor,
1834 index_ = clutter_text_coords_to_position (self, x, y);
1835 text = clutter_text_buffer_get_text (get_buffer (self));
1836 offset = bytes_to_offset (text, index_);
1838 /* what we select depends on the number of button clicks we
1841 * 1: just position the cursor and the selection
1842 * 2: select the current word
1843 * 3: select the contents of the whole actor
1845 if (event->click_count == 1)
1847 clutter_text_set_positions (self, offset, offset);
1849 else if (event->click_count == 2)
1851 clutter_text_select_word (self);
1853 else if (event->click_count == 3)
1855 clutter_text_select_line (self);
1859 /* grab the pointer */
1860 priv->in_select_drag = TRUE;
1861 clutter_grab_pointer (actor);
1863 return CLUTTER_EVENT_STOP;
1867 clutter_text_motion (ClutterActor *actor,
1868 ClutterMotionEvent *mev)
1870 ClutterText *self = CLUTTER_TEXT (actor);
1871 ClutterTextPrivate *priv = self->priv;
1873 gint index_, offset;
1877 if (!priv->in_select_drag)
1878 return CLUTTER_EVENT_PROPAGATE;
1880 res = clutter_actor_transform_stage_point (actor,
1884 return CLUTTER_EVENT_PROPAGATE;
1886 index_ = clutter_text_coords_to_position (self, x, y);
1887 text = clutter_text_buffer_get_text (get_buffer (self));
1888 offset = bytes_to_offset (text, index_);
1890 if (priv->selectable)
1891 clutter_text_set_cursor_position (self, offset);
1893 clutter_text_set_positions (self, offset, offset);
1895 return CLUTTER_EVENT_STOP;
1899 clutter_text_button_release (ClutterActor *actor,
1900 ClutterButtonEvent *bev)
1902 ClutterText *self = CLUTTER_TEXT (actor);
1903 ClutterTextPrivate *priv = self->priv;
1905 if (priv->in_select_drag)
1907 clutter_ungrab_pointer ();
1908 priv->in_select_drag = FALSE;
1910 return CLUTTER_EVENT_STOP;
1913 return CLUTTER_EVENT_PROPAGATE;
1917 clutter_text_remove_password_hint (gpointer data)
1919 ClutterText *self = data;
1921 self->priv->password_hint_visible = FALSE;
1922 self->priv->password_hint_id = 0;
1924 clutter_text_dirty_cache (data);
1925 clutter_text_queue_redraw (data);
1927 return G_SOURCE_REMOVE;
1931 clutter_text_key_press (ClutterActor *actor,
1932 ClutterKeyEvent *event)
1934 ClutterText *self = CLUTTER_TEXT (actor);
1935 ClutterTextPrivate *priv = self->priv;
1936 ClutterBindingPool *pool;
1939 if (!priv->editable)
1940 return CLUTTER_EVENT_PROPAGATE;
1942 /* we need to use the ClutterText type name to find our own
1943 * key bindings; subclasses will override or chain up this
1944 * event handler, so they can do whatever they want there
1946 pool = clutter_binding_pool_find (g_type_name (CLUTTER_TYPE_TEXT));
1947 g_assert (pool != NULL);
1949 /* we allow passing synthetic events that only contain
1950 * the Unicode value and not the key symbol
1952 if (event->keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC))
1955 res = clutter_binding_pool_activate (pool, event->keyval,
1956 event->modifier_state,
1959 /* if the key binding has handled the event we bail out
1960 * as fast as we can; otherwise, we try to insert the
1961 * Unicode character inside the key event into the text
1965 return CLUTTER_EVENT_STOP;
1966 else if ((event->modifier_state & CLUTTER_CONTROL_MASK) == 0)
1968 gunichar key_unichar;
1970 /* Skip keys when control is pressed */
1971 key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) event);
1973 /* return is reported as CR, but we want LF */
1974 if (key_unichar == '\r')
1977 if (key_unichar == '\n' ||
1978 (g_unichar_validate (key_unichar) &&
1979 !g_unichar_iscntrl (key_unichar)))
1981 /* truncate the eventual selection so that the
1982 * Unicode character can replace it
1984 clutter_text_delete_selection (self);
1985 clutter_text_insert_unichar (self, key_unichar);
1987 if (priv->show_password_hint)
1989 if (priv->password_hint_id != 0)
1990 g_source_remove (priv->password_hint_id);
1992 priv->password_hint_visible = TRUE;
1993 priv->password_hint_id =
1994 clutter_threads_add_timeout (priv->password_hint_timeout,
1995 clutter_text_remove_password_hint,
1999 return CLUTTER_EVENT_STOP;
2003 return CLUTTER_EVENT_PROPAGATE;
2007 clutter_text_compute_layout_offsets (ClutterText *self,
2008 PangoLayout *layout,
2009 const ClutterActorBox *alloc,
2013 ClutterActor *actor = CLUTTER_ACTOR (self);
2014 ClutterActorAlign x_align, y_align;
2015 PangoRectangle logical_rect;
2016 float alloc_width, alloc_height;
2019 clutter_actor_box_get_size (alloc, &alloc_width, &alloc_height);
2020 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2022 if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_HORIZONTAL))
2023 x_align = _clutter_actor_get_effective_x_align (actor);
2025 x_align = CLUTTER_ACTOR_ALIGN_FILL;
2027 if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_VERTICAL))
2028 y_align = clutter_actor_get_y_align (actor);
2030 y_align = CLUTTER_ACTOR_ALIGN_FILL;
2035 case CLUTTER_ACTOR_ALIGN_FILL:
2036 case CLUTTER_ACTOR_ALIGN_START:
2039 case CLUTTER_ACTOR_ALIGN_END:
2040 if (alloc_width > logical_rect.width)
2041 x = alloc_width - logical_rect.width;
2044 case CLUTTER_ACTOR_ALIGN_CENTER:
2045 if (alloc_width > logical_rect.width)
2046 x = (alloc_width - logical_rect.width) / 2.f;
2053 case CLUTTER_ACTOR_ALIGN_FILL:
2054 case CLUTTER_ACTOR_ALIGN_START:
2057 case CLUTTER_ACTOR_ALIGN_END:
2058 if (alloc_height > logical_rect.height)
2059 y = alloc_height - logical_rect.height;
2062 case CLUTTER_ACTOR_ALIGN_CENTER:
2063 if (alloc_height > logical_rect.height)
2064 y = (alloc_height - logical_rect.height) / 2.f;
2069 *text_x = floorf (x);
2072 *text_y = floorf (y);
2075 #define TEXT_PADDING 2
2078 clutter_text_paint (ClutterActor *self)
2080 ClutterText *text = CLUTTER_TEXT (self);
2081 ClutterTextPrivate *priv = text->priv;
2082 PangoLayout *layout;
2083 ClutterActorBox alloc = { 0, };
2084 CoglColor color = { 0, };
2085 guint8 real_opacity;
2086 gint text_x = priv->text_x;
2087 gint text_y = priv->text_y;
2088 gboolean clip_set = FALSE;
2089 gboolean bg_color_set = FALSE;
2092 /* Note that if anything in this paint method changes it needs to be
2093 reflected in the get_paint_volume implementation which is tightly
2094 tied to the workings of this function */
2095 n_chars = clutter_text_buffer_get_length (get_buffer (text));
2097 /* don't bother painting an empty text actor, unless it's
2098 * editable, in which case we want to paint at least the
2101 if (n_chars == 0 && (!priv->editable || !priv->cursor_visible))
2104 clutter_actor_get_allocation_box (self, &alloc);
2106 g_object_get (self, "background-color-set", &bg_color_set, NULL);
2109 ClutterColor bg_color;
2111 clutter_actor_get_background_color (self, &bg_color);
2112 bg_color.alpha = clutter_actor_get_paint_opacity (self)
2116 cogl_set_source_color4ub (bg_color.red,
2120 cogl_rectangle (0, 0, alloc.x2 - alloc.x1, alloc.y2 - alloc.y1);
2123 if (priv->editable && priv->single_line_mode)
2124 layout = clutter_text_create_layout (text, -1, -1);
2127 /* the only time when we create the PangoLayout using the full
2128 * width and height of the allocation is when we can both wrap
2131 if (priv->wrap && priv->ellipsize)
2133 layout = clutter_text_create_layout (text,
2134 alloc.x2 - alloc.x1,
2135 alloc.y2 - alloc.y1);
2139 /* if we're not wrapping we cannot set the height of the
2140 * layout, otherwise Pango will happily wrap the text to
2141 * fit in the rectangle - thus making the :wrap property
2146 * http://bugzilla.clutter-project.org/show_bug.cgi?id=2339
2148 * in order to fix this, we create a layout that would fit
2149 * in the assigned width, then we clip the actor if the
2150 * logical rectangle overflows the allocation.
2152 layout = clutter_text_create_layout (text,
2153 alloc.x2 - alloc.x1,
2158 if (priv->editable && priv->cursor_visible)
2159 clutter_text_ensure_cursor_position (text);
2161 if (priv->editable && priv->single_line_mode)
2163 PangoRectangle logical_rect = { 0, };
2164 gint actor_width, text_width;
2166 pango_layout_get_extents (layout, NULL, &logical_rect);
2168 cogl_clip_push_rectangle (0, 0,
2169 (alloc.x2 - alloc.x1),
2170 (alloc.y2 - alloc.y1));
2173 actor_width = (alloc.x2 - alloc.x1)
2175 text_width = logical_rect.width / PANGO_SCALE;
2177 if (actor_width < text_width)
2179 gint cursor_x = priv->cursor_pos.x;
2181 if (priv->position == -1)
2183 text_x = actor_width - text_width;
2185 else if (priv->position == 0)
2187 text_x = TEXT_PADDING;
2193 text_x = text_x - cursor_x - TEXT_PADDING;
2195 else if (cursor_x > actor_width)
2197 text_x = text_x + (actor_width - cursor_x) - TEXT_PADDING;
2203 text_x = TEXT_PADDING;
2206 else if (!priv->editable && !(priv->wrap && priv->ellipsize))
2208 PangoRectangle logical_rect = { 0, };
2210 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2212 /* don't clip if the layout managed to fit inside our allocation */
2213 if (logical_rect.width > (alloc.x2 - alloc.x1) ||
2214 logical_rect.height > (alloc.y2 - alloc.y1))
2216 cogl_clip_push_rectangle (0, 0,
2217 alloc.x2 - alloc.x1,
2218 alloc.y2 - alloc.y1);
2222 clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y);
2225 clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y);
2227 if (priv->text_x != text_x ||
2228 priv->text_y != text_y)
2230 priv->text_x = text_x;
2231 priv->text_y = text_y;
2233 clutter_text_ensure_cursor_position (text);
2236 real_opacity = clutter_actor_get_paint_opacity (self)
2237 * priv->text_color.alpha
2240 CLUTTER_NOTE (PAINT, "painting text (text: '%s')",
2241 clutter_text_buffer_get_text (get_buffer (text)));
2243 cogl_color_init_from_4ub (&color,
2244 priv->text_color.red,
2245 priv->text_color.green,
2246 priv->text_color.blue,
2248 cogl_pango_render_layout (layout, priv->text_x, priv->text_y, &color, 0);
2250 selection_paint (text);
2257 add_selection_to_paint_volume (ClutterText *text,
2258 const ClutterActorBox *box,
2261 ClutterPaintVolume *total_volume = user_data;
2262 ClutterPaintVolume rect_volume;
2263 ClutterVertex vertex;
2265 _clutter_paint_volume_init_static (&rect_volume, CLUTTER_ACTOR (text));
2270 clutter_paint_volume_set_origin (&rect_volume, &vertex);
2271 clutter_paint_volume_set_width (&rect_volume, box->x2 - box->x1);
2272 clutter_paint_volume_set_height (&rect_volume, box->y2 - box->y1);
2274 clutter_paint_volume_union (total_volume, &rect_volume);
2276 clutter_paint_volume_free (&rect_volume);
2280 clutter_text_get_paint_volume_for_cursor (ClutterText *text,
2281 ClutterPaintVolume *volume)
2283 ClutterTextPrivate *priv = text->priv;
2284 ClutterVertex origin;
2286 clutter_text_ensure_cursor_position (text);
2288 if (priv->position == priv->selection_bound)
2290 origin.x = priv->cursor_pos.x;
2291 origin.y = priv->cursor_pos.y;
2293 clutter_paint_volume_set_origin (volume, &origin);
2294 clutter_paint_volume_set_width (volume, priv->cursor_pos.width);
2295 clutter_paint_volume_set_height (volume, priv->cursor_pos.height);
2299 clutter_text_foreach_selection_rectangle (text,
2300 add_selection_to_paint_volume,
2306 clutter_text_get_paint_volume (ClutterActor *self,
2307 ClutterPaintVolume *volume)
2309 ClutterText *text = CLUTTER_TEXT (self);
2310 ClutterTextPrivate *priv = text->priv;
2312 /* ClutterText uses the logical layout as the natural size of the
2313 actor. This means that it can sometimes paint outside of its
2314 allocation for example with italic fonts with serifs. Therefore
2315 we should use the ink rectangle of the layout instead */
2317 if (!priv->paint_volume_valid)
2319 PangoLayout *layout;
2320 PangoRectangle ink_rect;
2321 ClutterVertex origin;
2323 /* If the text is single line editable then it gets clipped to
2324 the allocation anyway so we can just use that */
2325 if (priv->editable && priv->single_line_mode)
2326 return _clutter_actor_set_default_paint_volume (self,
2330 if (G_OBJECT_TYPE (self) != CLUTTER_TYPE_TEXT)
2333 if (!clutter_actor_has_allocation (self))
2336 _clutter_paint_volume_init_static (&priv->paint_volume, self);
2338 layout = clutter_text_get_layout (text);
2339 pango_layout_get_extents (layout, &ink_rect, NULL);
2341 origin.x = ink_rect.x / (float) PANGO_SCALE;
2342 origin.y = ink_rect.y / (float) PANGO_SCALE;
2344 clutter_paint_volume_set_origin (&priv->paint_volume, &origin);
2345 clutter_paint_volume_set_width (&priv->paint_volume,
2346 ink_rect.width / (float) PANGO_SCALE);
2347 clutter_paint_volume_set_height (&priv->paint_volume,
2348 ink_rect.height / (float) PANGO_SCALE);
2350 /* If the cursor is visible then that will likely be drawn
2351 outside of the ink rectangle so we should merge that in */
2352 if (priv->editable && priv->cursor_visible && priv->has_focus)
2354 ClutterPaintVolume cursor_paint_volume;
2356 _clutter_paint_volume_init_static (&cursor_paint_volume,
2359 clutter_text_get_paint_volume_for_cursor (text, &cursor_paint_volume);
2361 clutter_paint_volume_union (&priv->paint_volume,
2362 &cursor_paint_volume);
2364 clutter_paint_volume_free (&cursor_paint_volume);
2367 priv->paint_volume_valid = TRUE;
2370 _clutter_paint_volume_copy_static (&priv->paint_volume, volume);
2376 clutter_text_get_preferred_width (ClutterActor *self,
2378 gfloat *min_width_p,
2379 gfloat *natural_width_p)
2381 ClutterText *text = CLUTTER_TEXT (self);
2382 ClutterTextPrivate *priv = text->priv;
2383 PangoRectangle logical_rect = { 0, };
2384 PangoLayout *layout;
2386 gfloat layout_width;
2388 layout = clutter_text_create_layout (text, -1, -1);
2390 pango_layout_get_extents (layout, NULL, &logical_rect);
2392 /* the X coordinate of the logical rectangle might be non-zero
2393 * according to the Pango documentation; hence, we need to offset
2394 * the width accordingly
2396 logical_width = logical_rect.x + logical_rect.width;
2398 layout_width = logical_width > 0
2399 ? ceilf (logical_width / 1024.0f)
2404 if (priv->wrap || priv->ellipsize || priv->editable)
2407 *min_width_p = layout_width;
2410 if (natural_width_p)
2412 if (priv->editable && priv->single_line_mode)
2413 *natural_width_p = layout_width + TEXT_PADDING * 2;
2415 *natural_width_p = layout_width;
2420 clutter_text_get_preferred_height (ClutterActor *self,
2422 gfloat *min_height_p,
2423 gfloat *natural_height_p)
2425 ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
2432 if (natural_height_p)
2433 *natural_height_p = 0;
2437 PangoLayout *layout;
2438 PangoRectangle logical_rect = { 0, };
2439 gint logical_height;
2440 gfloat layout_height;
2442 if (priv->single_line_mode)
2445 layout = clutter_text_create_layout (CLUTTER_TEXT (self),
2448 pango_layout_get_extents (layout, NULL, &logical_rect);
2450 /* the Y coordinate of the logical rectangle might be non-zero
2451 * according to the Pango documentation; hence, we need to offset
2452 * the height accordingly
2454 logical_height = logical_rect.y + logical_rect.height;
2455 layout_height = ceilf (logical_height / 1024.0f);
2459 /* if we wrap and ellipsize then the minimum height is
2460 * going to be at least the size of the first line
2462 if ((priv->ellipsize && priv->wrap) && !priv->single_line_mode)
2464 PangoLayoutLine *line;
2467 line = pango_layout_get_line_readonly (layout, 0);
2468 pango_layout_line_get_extents (line, NULL, &logical_rect);
2470 logical_height = logical_rect.y + logical_rect.height;
2471 line_height = ceilf (logical_height / 1024.0f);
2473 *min_height_p = line_height;
2476 *min_height_p = layout_height;
2479 if (natural_height_p)
2480 *natural_height_p = layout_height;
2485 clutter_text_allocate (ClutterActor *self,
2486 const ClutterActorBox *box,
2487 ClutterAllocationFlags flags)
2489 ClutterText *text = CLUTTER_TEXT (self);
2490 ClutterActorClass *parent_class;
2492 /* Ensure that there is a cached layout with the right width so
2493 * that we don't need to create the text during the paint run
2495 * if the Text is editable and in single line mode we don't want
2496 * to have any limit on the layout size, since the paint will clip
2497 * it to the allocation of the actor
2499 if (text->priv->editable && text->priv->single_line_mode)
2500 clutter_text_create_layout (text, -1, -1);
2502 clutter_text_create_layout (text,
2506 parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class);
2507 parent_class->allocate (self, box, flags);
2511 clutter_text_has_overlaps (ClutterActor *self)
2513 ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
2515 return priv->editable ||
2517 priv->cursor_visible;
2521 clutter_text_key_focus_in (ClutterActor *actor)
2523 ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
2525 priv->has_focus = TRUE;
2527 clutter_text_queue_redraw (actor);
2531 clutter_text_key_focus_out (ClutterActor *actor)
2533 ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
2535 priv->has_focus = FALSE;
2537 clutter_text_queue_redraw (actor);
2541 clutter_text_real_move_left (ClutterText *self,
2542 const gchar *action,
2544 ClutterModifierType modifiers)
2546 ClutterTextPrivate *priv = self->priv;
2547 gint pos = priv->position;
2551 len = clutter_text_buffer_get_length (get_buffer (self));
2553 g_object_freeze_notify (G_OBJECT (self));
2555 if (pos != 0 && len != 0)
2557 if (modifiers & CLUTTER_CONTROL_MASK)
2560 new_pos = clutter_text_move_word_backward (self, len);
2562 new_pos = clutter_text_move_word_backward (self, pos);
2572 clutter_text_set_cursor_position (self, new_pos);
2575 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2576 clutter_text_clear_selection (self);
2578 g_object_thaw_notify (G_OBJECT (self));
2584 clutter_text_real_move_right (ClutterText *self,
2585 const gchar *action,
2587 ClutterModifierType modifiers)
2589 ClutterTextPrivate *priv = self->priv;
2590 gint pos = priv->position;
2591 gint len = clutter_text_buffer_get_length (get_buffer (self));
2594 g_object_freeze_notify (G_OBJECT (self));
2596 if (pos != -1 && len !=0)
2598 if (modifiers & CLUTTER_CONTROL_MASK)
2601 new_pos = clutter_text_move_word_forward (self, pos);
2609 clutter_text_set_cursor_position (self, new_pos);
2612 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2613 clutter_text_clear_selection (self);
2615 g_object_thaw_notify (G_OBJECT (self));
2621 clutter_text_real_move_up (ClutterText *self,
2622 const gchar *action,
2624 ClutterModifierType modifiers)
2626 ClutterTextPrivate *priv = self->priv;
2627 PangoLayoutLine *layout_line;
2628 PangoLayout *layout;
2630 gint index_, trailing;
2635 layout = clutter_text_get_layout (self);
2636 text = clutter_text_buffer_get_text (get_buffer (self));
2638 if (priv->position == 0)
2641 index_ = offset_to_bytes (text, priv->position);
2643 pango_layout_index_to_line_x (layout, index_,
2651 if (priv->x_pos != -1)
2654 layout_line = pango_layout_get_line_readonly (layout, line_no);
2658 pango_layout_line_x_to_index (layout_line, x, &index_, &trailing);
2660 g_object_freeze_notify (G_OBJECT (self));
2662 pos = bytes_to_offset (text, index_);
2663 clutter_text_set_cursor_position (self, pos + trailing);
2665 /* Store the target x position to avoid drifting left and right when
2666 moving the cursor up and down */
2669 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2670 clutter_text_clear_selection (self);
2672 g_object_thaw_notify (G_OBJECT (self));
2678 clutter_text_real_move_down (ClutterText *self,
2679 const gchar *action,
2681 ClutterModifierType modifiers)
2683 ClutterTextPrivate *priv = self->priv;
2684 PangoLayoutLine *layout_line;
2685 PangoLayout *layout;
2687 gint index_, trailing;
2692 layout = clutter_text_get_layout (self);
2693 text = clutter_text_buffer_get_text (get_buffer (self));
2695 if (priv->position == 0)
2698 index_ = offset_to_bytes (text, priv->position);
2700 pango_layout_index_to_line_x (layout, index_,
2704 if (priv->x_pos != -1)
2707 layout_line = pango_layout_get_line_readonly (layout, line_no + 1);
2711 pango_layout_line_x_to_index (layout_line, x, &index_, &trailing);
2713 g_object_freeze_notify (G_OBJECT (self));
2715 pos = bytes_to_offset (text, index_);
2716 clutter_text_set_cursor_position (self, pos + trailing);
2718 /* Store the target x position to avoid drifting left and right when
2719 moving the cursor up and down */
2722 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2723 clutter_text_clear_selection (self);
2725 g_object_thaw_notify (G_OBJECT (self));
2731 clutter_text_real_line_start (ClutterText *self,
2732 const gchar *action,
2734 ClutterModifierType modifiers)
2736 ClutterTextPrivate *priv = self->priv;
2739 g_object_freeze_notify (G_OBJECT (self));
2741 position = clutter_text_move_line_start (self, priv->position);
2742 clutter_text_set_cursor_position (self, position);
2744 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2745 clutter_text_clear_selection (self);
2747 g_object_thaw_notify (G_OBJECT (self));
2753 clutter_text_real_line_end (ClutterText *self,
2754 const gchar *action,
2756 ClutterModifierType modifiers)
2758 ClutterTextPrivate *priv = self->priv;
2761 g_object_freeze_notify (G_OBJECT (self));
2763 position = clutter_text_move_line_end (self, priv->position);
2764 clutter_text_set_cursor_position (self, position);
2766 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2767 clutter_text_clear_selection (self);
2769 g_object_thaw_notify (G_OBJECT (self));
2775 clutter_text_real_select_all (ClutterText *self,
2776 const gchar *action,
2778 ClutterModifierType modifiers)
2780 guint n_chars = clutter_text_buffer_get_length (get_buffer (self));
2781 clutter_text_set_positions (self, 0, n_chars);
2787 clutter_text_real_del_next (ClutterText *self,
2788 const gchar *action,
2790 ClutterModifierType modifiers)
2792 ClutterTextPrivate *priv = self->priv;
2796 if (clutter_text_delete_selection (self))
2799 pos = priv->position;
2800 len = clutter_text_buffer_get_length (get_buffer (self));
2802 if (len && pos != -1 && pos < len)
2803 clutter_text_delete_text (self, pos, pos + 1);
2809 clutter_text_real_del_word_next (ClutterText *self,
2810 const gchar *action,
2812 ClutterModifierType modifiers)
2814 ClutterTextPrivate *priv = self->priv;
2818 pos = priv->position;
2819 len = clutter_text_buffer_get_length (get_buffer (self));
2821 if (len && pos != -1 && pos < len)
2825 end = clutter_text_move_word_forward (self, pos);
2826 clutter_text_delete_text (self, pos, end);
2828 if (priv->selection_bound >= end)
2832 new_bound = priv->selection_bound - (end - pos);
2833 clutter_text_set_selection_bound (self, new_bound);
2835 else if (priv->selection_bound > pos)
2837 clutter_text_set_selection_bound (self, pos);
2845 clutter_text_real_del_prev (ClutterText *self,
2846 const gchar *action,
2848 ClutterModifierType modifiers)
2850 ClutterTextPrivate *priv = self->priv;
2854 if (clutter_text_delete_selection (self))
2857 pos = priv->position;
2858 len = clutter_text_buffer_get_length (get_buffer (self));
2860 if (pos != 0 && len != 0)
2864 clutter_text_delete_text (self, len - 1, len);
2866 clutter_text_set_positions (self, -1, -1);
2870 clutter_text_delete_text (self, pos - 1, pos);
2872 clutter_text_set_positions (self, pos - 1, pos - 1);
2880 clutter_text_real_del_word_prev (ClutterText *self,
2881 const gchar *action,
2883 ClutterModifierType modifiers)
2885 ClutterTextPrivate *priv = self->priv;
2889 pos = priv->position;
2890 len = clutter_text_buffer_get_length (get_buffer (self));
2892 if (pos != 0 && len != 0)
2898 new_pos = clutter_text_move_word_backward (self, len);
2899 clutter_text_delete_text (self, new_pos, len);
2901 clutter_text_set_positions (self, -1, -1);
2905 new_pos = clutter_text_move_word_backward (self, pos);
2906 clutter_text_delete_text (self, new_pos, pos);
2908 clutter_text_set_cursor_position (self, new_pos);
2909 if (priv->selection_bound >= pos)
2913 new_bound = priv->selection_bound - (pos - new_pos);
2914 clutter_text_set_selection_bound (self, new_bound);
2916 else if (priv->selection_bound >= new_pos)
2918 clutter_text_set_selection_bound (self, new_pos);
2927 clutter_text_real_activate (ClutterText *self,
2928 const gchar *action,
2930 ClutterModifierType modifiers)
2932 return clutter_text_activate (self);
2936 clutter_text_add_move_binding (ClutterBindingPool *pool,
2937 const gchar *action,
2939 ClutterModifierType additional_modifiers,
2942 clutter_binding_pool_install_action (pool, action,
2947 clutter_binding_pool_install_action (pool, action,
2953 if (additional_modifiers != 0)
2955 clutter_binding_pool_install_action (pool, action,
2957 additional_modifiers,
2960 clutter_binding_pool_install_action (pool, action,
2962 CLUTTER_SHIFT_MASK |
2963 additional_modifiers,
2970 clutter_text_parse_custom_node (ClutterScriptable *scriptable,
2971 ClutterScript *script,
2976 if (strncmp (name, "font-description", 16) == 0)
2978 g_value_init (value, G_TYPE_STRING);
2979 g_value_set_string (value, json_node_get_string (node));
2984 return parent_scriptable_iface->parse_custom_node (scriptable, script,
2991 clutter_text_set_color_internal (ClutterText *self,
2993 const ClutterColor *color)
2995 ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
2996 GParamSpec *other = NULL;
2998 switch (pspec->param_id)
3001 priv->text_color = *color;
3004 case PROP_CURSOR_COLOR:
3007 priv->cursor_color = *color;
3008 priv->cursor_color_set = TRUE;
3011 priv->cursor_color_set = FALSE;
3013 other = obj_props[PROP_CURSOR_COLOR_SET];
3016 case PROP_SELECTION_COLOR:
3019 priv->selection_color = *color;
3020 priv->selection_color_set = TRUE;
3023 priv->selection_color_set = FALSE;
3025 other = obj_props[PROP_SELECTION_COLOR_SET];
3028 case PROP_SELECTED_TEXT_COLOR:
3031 priv->selected_text_color = *color;
3032 priv->selected_text_color_set = TRUE;
3035 priv->selected_text_color_set = FALSE;
3037 other = obj_props[PROP_SELECTED_TEXT_COLOR_SET];
3041 g_assert_not_reached ();
3045 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
3046 g_object_notify_by_pspec (G_OBJECT (self), pspec);
3048 g_object_notify_by_pspec (G_OBJECT (self), other);
3052 clutter_text_set_color_animated (ClutterText *self,
3054 const ClutterColor *color)
3056 ClutterActor *actor = CLUTTER_ACTOR (self);
3057 ClutterTextPrivate *priv = self->priv;
3058 const ClutterAnimationInfo *info;
3059 ClutterTransition *transition;
3061 info = _clutter_actor_get_animation_info (actor);
3062 transition = clutter_actor_get_transition (actor, pspec->name);
3064 /* jump to the end if there is no easing state, or if the easing
3065 * state has a duration of 0 msecs
3067 if (info->cur_state == NULL ||
3068 info->cur_state->easing_duration == 0)
3070 /* ensure that we remove any currently running transition */
3071 if (transition != NULL)
3073 clutter_actor_remove_transition (actor, pspec->name);
3077 clutter_text_set_color_internal (self, pspec, color);
3082 if (transition == NULL)
3084 transition = clutter_property_transition_new (pspec->name);
3085 clutter_transition_set_animatable (transition,
3086 CLUTTER_ANIMATABLE (self));
3087 clutter_transition_set_remove_on_complete (transition, TRUE);
3089 /* delay only makes sense if the transition has just been created */
3090 clutter_timeline_set_delay (CLUTTER_TIMELINE (transition),
3091 info->cur_state->easing_delay);
3093 clutter_actor_add_transition (actor, pspec->name, transition);
3095 /* the actor now owns the transition */
3096 g_object_unref (transition);
3099 /* if a transition already exist, update its bounds */
3100 switch (pspec->param_id)
3103 clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3107 case PROP_CURSOR_COLOR:
3108 clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3109 &priv->cursor_color);
3112 case PROP_SELECTION_COLOR:
3113 clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3114 &priv->selection_color);
3117 case PROP_SELECTED_TEXT_COLOR:
3118 clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3119 &priv->selected_text_color);
3123 g_assert_not_reached ();
3126 clutter_transition_set_to (transition, CLUTTER_TYPE_COLOR, color);
3128 /* always use the current easing state */
3129 clutter_timeline_set_duration (CLUTTER_TIMELINE (transition),
3130 info->cur_state->easing_duration);
3131 clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
3132 info->cur_state->easing_mode);
3134 /* ensure that we start from the beginning */
3135 clutter_timeline_rewind (CLUTTER_TIMELINE (transition));
3136 clutter_timeline_start (CLUTTER_TIMELINE (transition));
3140 clutter_text_set_custom_property (ClutterScriptable *scriptable,
3141 ClutterScript *script,
3143 const GValue *value)
3145 if (strncmp (name, "font-description", 16) == 0)
3147 g_assert (G_VALUE_HOLDS (value, G_TYPE_STRING));
3148 if (g_value_get_string (value) != NULL)
3149 clutter_text_set_font_name (CLUTTER_TEXT (scriptable),
3150 g_value_get_string (value));
3153 parent_scriptable_iface->set_custom_property (scriptable, script,
3159 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
3161 parent_scriptable_iface = g_type_interface_peek_parent (iface);
3163 iface->parse_custom_node = clutter_text_parse_custom_node;
3164 iface->set_custom_property = clutter_text_set_custom_property;
3168 clutter_text_set_final_state (ClutterAnimatable *animatable,
3169 const char *property_name,
3170 const GValue *value)
3172 if (strcmp (property_name, "color") == 0)
3174 const ClutterColor *color = clutter_value_get_color (value);
3175 clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3176 obj_props[PROP_COLOR], color);
3178 else if (strcmp (property_name, "cursor-color") == 0)
3180 const ClutterColor *color = clutter_value_get_color (value);
3181 clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3182 obj_props[PROP_CURSOR_COLOR],
3185 else if (strcmp (property_name, "selected-text-color") == 0)
3187 const ClutterColor *color = clutter_value_get_color (value);
3188 clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3189 obj_props[PROP_SELECTED_TEXT_COLOR],
3192 else if (strcmp (property_name, "selection-color") == 0)
3194 const ClutterColor *color = clutter_value_get_color (value);
3195 clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3196 obj_props[PROP_SELECTION_COLOR],
3200 parent_animatable_iface->set_final_state (animatable, property_name, value);
3204 clutter_animatable_iface_init (ClutterAnimatableIface *iface)
3206 parent_animatable_iface = g_type_interface_peek_parent (iface);
3208 iface->set_final_state = clutter_text_set_final_state;
3212 clutter_text_class_init (ClutterTextClass *klass)
3214 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3215 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
3216 ClutterBindingPool *binding_pool;
3219 g_type_class_add_private (klass, sizeof (ClutterTextPrivate));
3221 gobject_class->set_property = clutter_text_set_property;
3222 gobject_class->get_property = clutter_text_get_property;
3223 gobject_class->dispose = clutter_text_dispose;
3224 gobject_class->finalize = clutter_text_finalize;
3226 actor_class->paint = clutter_text_paint;
3227 actor_class->get_paint_volume = clutter_text_get_paint_volume;
3228 actor_class->get_preferred_width = clutter_text_get_preferred_width;
3229 actor_class->get_preferred_height = clutter_text_get_preferred_height;
3230 actor_class->allocate = clutter_text_allocate;
3231 actor_class->key_press_event = clutter_text_key_press;
3232 actor_class->button_press_event = clutter_text_button_press;
3233 actor_class->button_release_event = clutter_text_button_release;
3234 actor_class->motion_event = clutter_text_motion;
3235 actor_class->key_focus_in = clutter_text_key_focus_in;
3236 actor_class->key_focus_out = clutter_text_key_focus_out;
3237 actor_class->has_overlaps = clutter_text_has_overlaps;
3240 * ClutterText:buffer:
3242 * The buffer which stores the text for this #ClutterText.
3244 * If set to %NULL, a default buffer will be created.
3248 pspec = g_param_spec_object ("buffer",
3250 P_("The buffer for the text"),
3251 CLUTTER_TYPE_TEXT_BUFFER,
3252 CLUTTER_PARAM_READWRITE);
3253 obj_props[PROP_BUFFER] = pspec;
3254 g_object_class_install_property (gobject_class, PROP_BUFFER, pspec);
3257 * ClutterText:font-name:
3259 * The font to be used by the #ClutterText, as a string
3260 * that can be parsed by pango_font_description_from_string().
3262 * If set to %NULL, the default system font will be used instead.
3266 pspec = g_param_spec_string ("font-name",
3268 P_("The font to be used by the text"),
3270 CLUTTER_PARAM_READWRITE);
3271 obj_props[PROP_FONT_NAME] = pspec;
3272 g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec);
3275 * ClutterText:font-description:
3277 * The #PangoFontDescription that should be used by the #ClutterText
3279 * If you have a string describing the font then you should look at
3280 * #ClutterText:font-name instead
3284 pspec = g_param_spec_boxed ("font-description",
3285 P_("Font Description"),
3286 P_("The font description to be used"),
3287 PANGO_TYPE_FONT_DESCRIPTION,
3288 CLUTTER_PARAM_READWRITE);
3289 obj_props[PROP_FONT_DESCRIPTION] = pspec;
3290 g_object_class_install_property (gobject_class,
3291 PROP_FONT_DESCRIPTION,
3297 * The text to render inside the actor.
3301 pspec = g_param_spec_string ("text",
3303 P_("The text to render"),
3305 CLUTTER_PARAM_READWRITE);
3306 obj_props[PROP_TEXT] = pspec;
3307 g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
3310 * ClutterText:color:
3312 * The color used to render the text.
3316 pspec = clutter_param_spec_color ("color",
3318 P_("Color of the font used by the text"),
3319 &default_text_color,
3320 CLUTTER_PARAM_READWRITE |
3321 CLUTTER_PARAM_ANIMATABLE);
3322 obj_props[PROP_COLOR] = pspec;
3323 g_object_class_install_property (gobject_class, PROP_COLOR, pspec);
3326 * ClutterText:editable:
3328 * Whether key events delivered to the actor causes editing.
3332 pspec = g_param_spec_boolean ("editable",
3334 P_("Whether the text is editable"),
3337 obj_props[PROP_EDITABLE] = pspec;
3338 g_object_class_install_property (gobject_class, PROP_EDITABLE, pspec);
3341 * ClutterText:selectable:
3343 * Whether it is possible to select text, either using the pointer
3348 pspec = g_param_spec_boolean ("selectable",
3350 P_("Whether the text is selectable"),
3353 obj_props[PROP_SELECTABLE] = pspec;
3354 g_object_class_install_property (gobject_class, PROP_SELECTABLE, pspec);
3357 * ClutterText:activatable:
3359 * Toggles whether return invokes the activate signal or not.
3363 pspec = g_param_spec_boolean ("activatable",
3365 P_("Whether pressing return causes the activate signal to be emitted"),
3368 obj_props[PROP_ACTIVATABLE] = pspec;
3369 g_object_class_install_property (gobject_class, PROP_ACTIVATABLE, pspec);
3372 * ClutterText:cursor-visible:
3374 * Whether the input cursor is visible or not, it will only be visible
3375 * if both #ClutterText:cursor-visible and #ClutterText:editable are
3380 pspec = g_param_spec_boolean ("cursor-visible",
3381 P_("Cursor Visible"),
3382 P_("Whether the input cursor is visible"),
3384 CLUTTER_PARAM_READWRITE);
3385 obj_props[PROP_CURSOR_VISIBLE] = pspec;
3386 g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec);
3389 * ClutterText:cursor-color:
3391 * The color of the cursor.
3395 pspec = clutter_param_spec_color ("cursor-color",
3398 &default_cursor_color,
3399 CLUTTER_PARAM_READWRITE |
3400 CLUTTER_PARAM_ANIMATABLE);
3401 obj_props[PROP_CURSOR_COLOR] = pspec;
3402 g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec);
3405 * ClutterText:cursor-color-set:
3407 * Will be set to %TRUE if #ClutterText:cursor-color has been set.
3411 pspec = g_param_spec_boolean ("cursor-color-set",
3412 P_("Cursor Color Set"),
3413 P_("Whether the cursor color has been set"),
3415 CLUTTER_PARAM_READABLE);
3416 obj_props[PROP_CURSOR_COLOR_SET] = pspec;
3417 g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR_SET, pspec);
3420 * ClutterText:cursor-size:
3422 * The size of the cursor, in pixels. If set to -1 the size used will
3423 * be the default cursor size of 2 pixels.
3427 pspec = g_param_spec_int ("cursor-size",
3429 P_("The width of the cursor, in pixels"),
3430 -1, G_MAXINT, DEFAULT_CURSOR_SIZE,
3431 CLUTTER_PARAM_READWRITE);
3432 obj_props[PROP_CURSOR_SIZE] = pspec;
3433 g_object_class_install_property (gobject_class, PROP_CURSOR_SIZE, pspec);
3436 * ClutterText:position:
3438 * The current input cursor position. -1 is taken to be the end of the text
3442 pspec = g_param_spec_int ("position",
3443 P_("Cursor Position"),
3444 P_("The cursor position"),
3447 CLUTTER_PARAM_READWRITE);
3448 obj_props[PROP_POSITION] = pspec;
3449 g_object_class_install_property (gobject_class, PROP_POSITION, pspec);
3452 * ClutterText:selection-bound:
3454 * The current input cursor position. -1 is taken to be the end of the text
3458 pspec = g_param_spec_int ("selection-bound",
3459 P_("Selection-bound"),
3460 P_("The cursor position of the other end of the selection"),
3463 CLUTTER_PARAM_READWRITE);
3464 obj_props[PROP_SELECTION_BOUND] = pspec;
3465 g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec);
3468 * ClutterText:selection-color:
3470 * The color of the selection.
3474 pspec = clutter_param_spec_color ("selection-color",
3475 P_("Selection Color"),
3476 P_("Selection Color"),
3477 &default_selection_color,
3478 CLUTTER_PARAM_READWRITE |
3479 CLUTTER_PARAM_ANIMATABLE);
3480 obj_props[PROP_SELECTION_COLOR] = pspec;
3481 g_object_class_install_property (gobject_class, PROP_SELECTION_COLOR, pspec);
3484 * ClutterText:selection-color-set:
3486 * Will be set to %TRUE if #ClutterText:selection-color has been set.
3490 pspec = g_param_spec_boolean ("selection-color-set",
3491 P_("Selection Color Set"),
3492 P_("Whether the selection color has been set"),
3494 CLUTTER_PARAM_READABLE);
3495 obj_props[PROP_SELECTION_COLOR_SET] = pspec;
3496 g_object_class_install_property (gobject_class, PROP_SELECTION_COLOR_SET, pspec);
3499 * ClutterText:attributes:
3501 * A list of #PangoStyleAttribute<!-- -->s to be applied to the
3502 * contents of the #ClutterText actor.
3506 pspec = g_param_spec_boxed ("attributes",
3508 P_("A list of style attributes to apply to the contents of the actor"),
3509 PANGO_TYPE_ATTR_LIST,
3510 CLUTTER_PARAM_READWRITE);
3511 obj_props[PROP_ATTRIBUTES] = pspec;
3512 g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec);
3515 * ClutterText:use-markup:
3517 * Whether the text includes Pango markup.
3519 * For more informations about the Pango markup format, see
3520 * pango_layout_set_markup() in the Pango documentation.
3522 * <note>It is not possible to round-trip this property between
3523 * %TRUE and %FALSE. Once a string with markup has been set on
3524 * a #ClutterText actor with :use-markup set to %TRUE, the markup
3525 * is stripped from the string.</note>
3529 pspec = g_param_spec_boolean ("use-markup",
3531 P_("Whether or not the text includes Pango markup"),
3533 CLUTTER_PARAM_READWRITE);
3534 obj_props[PROP_USE_MARKUP] = pspec;
3535 g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec);
3538 * ClutterText:line-wrap:
3540 * Whether to wrap the lines of #ClutterText:text if the contents
3541 * exceed the available allocation. The wrapping strategy is
3542 * controlled by the #ClutterText:line-wrap-mode property.
3546 pspec = g_param_spec_boolean ("line-wrap",
3548 P_("If set, wrap the lines if the text becomes too wide"),
3550 CLUTTER_PARAM_READWRITE);
3551 obj_props[PROP_LINE_WRAP] = pspec;
3552 g_object_class_install_property (gobject_class, PROP_LINE_WRAP, pspec);
3555 * ClutterText:line-wrap-mode:
3557 * If #ClutterText:line-wrap is set to %TRUE, this property will
3558 * control how the text is wrapped.
3562 pspec = g_param_spec_enum ("line-wrap-mode",
3563 P_("Line wrap mode"),
3564 P_("Control how line-wrapping is done"),
3565 PANGO_TYPE_WRAP_MODE,
3567 CLUTTER_PARAM_READWRITE);
3568 obj_props[PROP_LINE_WRAP_MODE] = pspec;
3569 g_object_class_install_property (gobject_class, PROP_LINE_WRAP_MODE, pspec);
3572 * ClutterText:ellipsize:
3574 * The preferred place to ellipsize the contents of the #ClutterText actor
3578 pspec = g_param_spec_enum ("ellipsize",
3580 P_("The preferred place to ellipsize the string"),
3581 PANGO_TYPE_ELLIPSIZE_MODE,
3582 PANGO_ELLIPSIZE_NONE,
3583 CLUTTER_PARAM_READWRITE);
3584 obj_props[PROP_ELLIPSIZE] = pspec;
3585 g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec);
3588 * ClutterText:line-alignment:
3590 * The preferred alignment for the text. This property controls
3591 * the alignment of multi-line paragraphs.
3595 pspec = g_param_spec_enum ("line-alignment",
3596 P_("Line Alignment"),
3597 P_("The preferred alignment for the string, for multi-line text"),
3598 PANGO_TYPE_ALIGNMENT,
3600 CLUTTER_PARAM_READWRITE);
3601 obj_props[PROP_LINE_ALIGNMENT] = pspec;
3602 g_object_class_install_property (gobject_class, PROP_LINE_ALIGNMENT, pspec);
3605 * ClutterText:justify:
3607 * Whether the contents of the #ClutterText should be justified
3612 pspec = g_param_spec_boolean ("justify",
3614 P_("Whether the text should be justified"),
3616 CLUTTER_PARAM_READWRITE);
3617 obj_props[PROP_JUSTIFY] = pspec;
3618 g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec);
3621 * ClutterText:password-char:
3623 * If non-zero, the character that should be used in place of
3624 * the actual text in a password text actor.
3628 pspec = g_param_spec_unichar ("password-char",
3629 P_("Password Character"),
3630 P_("If non-zero, use this character to display the actor's contents"),
3632 CLUTTER_PARAM_READWRITE);
3633 obj_props[PROP_PASSWORD_CHAR] = pspec;
3634 g_object_class_install_property (gobject_class, PROP_PASSWORD_CHAR, pspec);
3637 * ClutterText:max-length:
3639 * The maximum length of the contents of the #ClutterText actor.
3643 pspec = g_param_spec_int ("max-length",
3645 P_("Maximum length of the text inside the actor"),
3647 CLUTTER_PARAM_READWRITE);
3648 obj_props[PROP_MAX_LENGTH] = pspec;
3649 g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec);
3652 * ClutterText:single-line-mode:
3654 * Whether the #ClutterText actor should be in single line mode
3655 * or not. A single line #ClutterText actor will only contain a
3656 * single line of text, scrolling it in case its length is bigger
3657 * than the allocated size.
3659 * Setting this property will also set the #ClutterText:activatable
3660 * property as a side-effect.
3662 * The #ClutterText:single-line-mode property is used only if the
3663 * #ClutterText:editable property is set to %TRUE.
3667 pspec = g_param_spec_boolean ("single-line-mode",
3668 P_("Single Line Mode"),
3669 P_("Whether the text should be a single line"),
3671 CLUTTER_PARAM_READWRITE);
3672 obj_props[PROP_SINGLE_LINE_MODE] = pspec;
3673 g_object_class_install_property (gobject_class, PROP_SINGLE_LINE_MODE, pspec);
3676 * ClutterText:selected-text-color:
3678 * The color of selected text.
3682 pspec = clutter_param_spec_color ("selected-text-color",
3683 P_("Selected Text Color"),
3684 P_("Selected Text Color"),
3685 &default_selected_text_color,
3686 CLUTTER_PARAM_READWRITE |
3687 CLUTTER_PARAM_ANIMATABLE);
3688 obj_props[PROP_SELECTED_TEXT_COLOR] = pspec;
3689 g_object_class_install_property (gobject_class, PROP_SELECTED_TEXT_COLOR, pspec);
3692 * ClutterText:selected-text-color-set:
3694 * Will be set to %TRUE if #ClutterText:selected-text-color has been set.
3698 pspec = g_param_spec_boolean ("selected-text-color-set",
3699 P_("Selected Text Color Set"),
3700 P_("Whether the selected text color has been set"),
3702 CLUTTER_PARAM_READABLE);
3703 obj_props[PROP_SELECTED_TEXT_COLOR_SET] = pspec;
3704 g_object_class_install_property (gobject_class, PROP_SELECTED_TEXT_COLOR_SET, pspec);
3707 * ClutterText::text-changed:
3708 * @self: the #ClutterText that emitted the signal
3710 * The ::text-changed signal is emitted after @actor's text changes
3714 text_signals[TEXT_CHANGED] =
3715 g_signal_new (I_("text-changed"),
3716 G_TYPE_FROM_CLASS (gobject_class),
3718 G_STRUCT_OFFSET (ClutterTextClass, text_changed),
3720 _clutter_marshal_VOID__VOID,
3724 * ClutterText::insert-text:
3725 * @self: the #ClutterText that emitted the signal
3726 * @new_text: the new text to insert
3727 * @new_text_length: the length of the new text, in bytes, or -1 if
3728 * new_text is nul-terminated
3729 * @position: the position, in characters, at which to insert the
3730 * new text. this is an in-out parameter. After the signal
3731 * emission is finished, it should point after the newly
3734 * This signal is emitted when text is inserted into the actor by
3735 * the user. It is emitted before @self text changes.
3739 text_signals[INSERT_TEXT] =
3740 g_signal_new (I_("insert-text"),
3741 G_TYPE_FROM_CLASS (gobject_class),
3742 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3745 _clutter_marshal_VOID__STRING_INT_POINTER,
3752 * ClutterText::delete-text:
3753 * @self: the #ClutterText that emitted the signal
3754 * @start_pos: the starting position
3755 * @end_pos: the end position
3757 * This signal is emitted when text is deleted from the actor by
3758 * the user. It is emitted before @self text changes.
3762 text_signals[DELETE_TEXT] =
3763 g_signal_new (I_("delete-text"),
3764 G_TYPE_FROM_CLASS (gobject_class),
3765 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3768 _clutter_marshal_VOID__INT_INT,
3774 * ClutterText::cursor-event:
3775 * @self: the #ClutterText that emitted the signal
3776 * @geometry: the coordinates of the cursor
3778 * The ::cursor-event signal is emitted whenever the cursor position
3779 * changes inside a #ClutterText actor. Inside @geometry it is stored
3780 * the current position and size of the cursor, relative to the actor
3785 text_signals[CURSOR_EVENT] =
3786 g_signal_new (I_("cursor-event"),
3787 G_TYPE_FROM_CLASS (gobject_class),
3789 G_STRUCT_OFFSET (ClutterTextClass, cursor_event),
3791 _clutter_marshal_VOID__BOXED,
3793 CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE);
3796 * ClutterText::activate:
3797 * @self: the #ClutterText that emitted the signal
3799 * The ::activate signal is emitted each time the actor is 'activated'
3800 * by the user, normally by pressing the 'Enter' key. The signal is
3801 * emitted only if #ClutterText:activatable is set to %TRUE.
3805 text_signals[ACTIVATE] =
3806 g_signal_new (I_("activate"),
3807 G_TYPE_FROM_CLASS (gobject_class),
3809 G_STRUCT_OFFSET (ClutterTextClass, activate),
3811 _clutter_marshal_VOID__VOID,
3814 binding_pool = clutter_binding_pool_get_for_class (klass);
3816 clutter_text_add_move_binding (binding_pool, "move-left",
3817 CLUTTER_KEY_Left, CLUTTER_CONTROL_MASK,
3818 G_CALLBACK (clutter_text_real_move_left));
3819 clutter_text_add_move_binding (binding_pool, "move-left",
3820 CLUTTER_KEY_KP_Left, CLUTTER_CONTROL_MASK,
3821 G_CALLBACK (clutter_text_real_move_left));
3822 clutter_text_add_move_binding (binding_pool, "move-right",
3823 CLUTTER_KEY_Right, CLUTTER_CONTROL_MASK,
3824 G_CALLBACK (clutter_text_real_move_right));
3825 clutter_text_add_move_binding (binding_pool, "move-right",
3826 CLUTTER_KEY_KP_Right, CLUTTER_CONTROL_MASK,
3827 G_CALLBACK (clutter_text_real_move_right));
3828 clutter_text_add_move_binding (binding_pool, "move-up",
3830 G_CALLBACK (clutter_text_real_move_up));
3831 clutter_text_add_move_binding (binding_pool, "move-up",
3832 CLUTTER_KEY_KP_Up, 0,
3833 G_CALLBACK (clutter_text_real_move_up));
3834 clutter_text_add_move_binding (binding_pool, "move-down",
3835 CLUTTER_KEY_Down, 0,
3836 G_CALLBACK (clutter_text_real_move_down));
3837 clutter_text_add_move_binding (binding_pool, "move-down",
3838 CLUTTER_KEY_KP_Down, 0,
3839 G_CALLBACK (clutter_text_real_move_down));
3841 clutter_text_add_move_binding (binding_pool, "line-start",
3842 CLUTTER_KEY_Home, 0,
3843 G_CALLBACK (clutter_text_real_line_start));
3844 clutter_text_add_move_binding (binding_pool, "line-start",
3845 CLUTTER_KEY_KP_Home, 0,
3846 G_CALLBACK (clutter_text_real_line_start));
3847 clutter_text_add_move_binding (binding_pool, "line-start",
3848 CLUTTER_KEY_Begin, 0,
3849 G_CALLBACK (clutter_text_real_line_start));
3850 clutter_text_add_move_binding (binding_pool, "line-end",
3852 G_CALLBACK (clutter_text_real_line_end));
3853 clutter_text_add_move_binding (binding_pool, "line-end",
3854 CLUTTER_KEY_KP_End, 0,
3855 G_CALLBACK (clutter_text_real_line_end));
3857 clutter_binding_pool_install_action (binding_pool, "select-all",
3858 CLUTTER_KEY_a, CLUTTER_CONTROL_MASK,
3859 G_CALLBACK (clutter_text_real_select_all),
3862 clutter_binding_pool_install_action (binding_pool, "delete-next",
3863 CLUTTER_KEY_Delete, 0,
3864 G_CALLBACK (clutter_text_real_del_next),
3866 clutter_binding_pool_install_action (binding_pool, "delete-next",
3867 CLUTTER_KEY_Delete, CLUTTER_CONTROL_MASK,
3868 G_CALLBACK (clutter_text_real_del_word_next),
3870 clutter_binding_pool_install_action (binding_pool, "delete-next",
3871 CLUTTER_KEY_KP_Delete, 0,
3872 G_CALLBACK (clutter_text_real_del_next),
3874 clutter_binding_pool_install_action (binding_pool, "delete-next",
3875 CLUTTER_KEY_KP_Delete, CLUTTER_CONTROL_MASK,
3876 G_CALLBACK (clutter_text_real_del_word_next),
3878 clutter_binding_pool_install_action (binding_pool, "delete-prev",
3879 CLUTTER_KEY_BackSpace, 0,
3880 G_CALLBACK (clutter_text_real_del_prev),
3882 clutter_binding_pool_install_action (binding_pool, "delete-prev",
3883 CLUTTER_KEY_BackSpace, CLUTTER_SHIFT_MASK,
3884 G_CALLBACK (clutter_text_real_del_prev),
3886 clutter_binding_pool_install_action (binding_pool, "delete-prev",
3887 CLUTTER_KEY_BackSpace, CLUTTER_CONTROL_MASK,
3888 G_CALLBACK (clutter_text_real_del_word_prev),
3891 clutter_binding_pool_install_action (binding_pool, "activate",
3892 CLUTTER_KEY_Return, 0,
3893 G_CALLBACK (clutter_text_real_activate),
3895 clutter_binding_pool_install_action (binding_pool, "activate",
3896 CLUTTER_KEY_KP_Enter, 0,
3897 G_CALLBACK (clutter_text_real_activate),
3899 clutter_binding_pool_install_action (binding_pool, "activate",
3900 CLUTTER_KEY_ISO_Enter, 0,
3901 G_CALLBACK (clutter_text_real_activate),
3906 clutter_text_init (ClutterText *self)
3908 ClutterSettings *settings;
3909 ClutterTextPrivate *priv;
3911 int i, password_hint_time;
3913 self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self);
3915 priv->alignment = PANGO_ALIGN_LEFT;
3917 priv->wrap_mode = PANGO_WRAP_WORD;
3918 priv->ellipsize = PANGO_ELLIPSIZE_NONE;
3919 priv->use_underline = FALSE;
3920 priv->use_markup = FALSE;
3921 priv->justify = FALSE;
3923 for (i = 0; i < N_CACHED_LAYOUTS; i++)
3924 priv->cached_layouts[i].layout = NULL;
3926 /* default to "" so that clutter_text_get_text() will
3927 * return a valid string and we can safely call strlen()
3930 priv->buffer = NULL;
3932 priv->text_color = default_text_color;
3933 priv->cursor_color = default_cursor_color;
3934 priv->selection_color = default_selection_color;
3935 priv->selected_text_color = default_selected_text_color;
3937 /* get the default font name from the context; we don't use
3938 * set_font_description() here because we are initializing
3939 * the Text and we don't need notifications and sanity checks
3941 settings = clutter_settings_get_default ();
3942 g_object_get (settings,
3943 "font-name", &font_name,
3944 "password-hint-time", &password_hint_time,
3947 priv->font_name = font_name; /* font_name is allocated */
3948 priv->font_desc = pango_font_description_from_string (font_name);
3949 priv->is_default_font = TRUE;
3951 priv->position = -1;
3952 priv->selection_bound = -1;
3955 priv->cursor_visible = TRUE;
3956 priv->editable = FALSE;
3957 priv->selectable = TRUE;
3959 priv->selection_color_set = FALSE;
3960 priv->cursor_color_set = FALSE;
3961 priv->selected_text_color_set = FALSE;
3962 priv->preedit_set = FALSE;
3964 priv->password_char = 0;
3965 priv->show_password_hint = password_hint_time > 0;
3966 priv->password_hint_timeout = password_hint_time;
3970 priv->cursor_size = DEFAULT_CURSOR_SIZE;
3971 memset (&priv->cursor_pos, 0, sizeof (ClutterGeometry));
3973 priv->settings_changed_id =
3974 g_signal_connect_swapped (clutter_get_default_backend (),
3976 G_CALLBACK (clutter_text_settings_changed_cb),
3979 priv->direction_changed_id =
3980 g_signal_connect (self, "notify::text-direction",
3981 G_CALLBACK (clutter_text_direction_changed_cb),
3988 * Creates a new #ClutterText actor. This actor can be used to
3989 * display and edit text.
3991 * Return value: the newly created #ClutterText actor
3996 clutter_text_new (void)
3998 return g_object_new (CLUTTER_TYPE_TEXT, NULL);
4002 * clutter_text_new_full:
4003 * @font_name: a string with a font description
4004 * @text: the contents of the actor
4005 * @color: the color to be used to render @text
4007 * Creates a new #ClutterText actor, using @font_name as the font
4008 * description; @text will be used to set the contents of the actor;
4009 * and @color will be used as the color to render @text.
4011 * This function is equivalent to calling clutter_text_new(),
4012 * clutter_text_set_font_name(), clutter_text_set_text() and
4013 * clutter_text_set_color().
4015 * Return value: the newly created #ClutterText actor
4020 clutter_text_new_full (const gchar *font_name,
4022 const ClutterColor *color)
4024 return g_object_new (CLUTTER_TYPE_TEXT,
4025 "font-name", font_name,
4032 * clutter_text_new_with_text:
4033 * @font_name: (allow-none): a string with a font description
4034 * @text: the contents of the actor
4036 * Creates a new #ClutterText actor, using @font_name as the font
4037 * description; @text will be used to set the contents of the actor.
4039 * This function is equivalent to calling clutter_text_new(),
4040 * clutter_text_set_font_name(), and clutter_text_set_text().
4042 * Return value: the newly created #ClutterText actor
4047 clutter_text_new_with_text (const gchar *font_name,
4050 return g_object_new (CLUTTER_TYPE_TEXT,
4051 "font-name", font_name,
4056 static ClutterTextBuffer*
4057 get_buffer (ClutterText *self)
4059 ClutterTextPrivate *priv = self->priv;
4061 if (priv->buffer == NULL)
4063 ClutterTextBuffer *buffer;
4064 buffer = clutter_text_buffer_new ();
4065 clutter_text_set_buffer (self, buffer);
4066 g_object_unref (buffer);
4069 return priv->buffer;
4072 /* GtkEntryBuffer signal handlers
4075 buffer_inserted_text (ClutterTextBuffer *buffer,
4081 ClutterTextPrivate *priv;
4083 gint new_selection_bound;
4087 if (priv->position >= 0 || priv->selection_bound >= 0)
4089 new_position = priv->position;
4090 new_selection_bound = priv->selection_bound;
4092 if (position <= new_position)
4093 new_position += n_chars;
4094 if (position <= new_selection_bound)
4095 new_selection_bound += n_chars;
4097 if (priv->position != new_position || priv->selection_bound != new_selection_bound)
4098 clutter_text_set_positions (self, new_position, new_selection_bound);
4101 n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars;
4102 g_signal_emit (self, text_signals[INSERT_TEXT], 0, chars,
4103 n_bytes, &position);
4105 /* TODO: What are we supposed to with the out value of position? */
4109 buffer_deleted_text (ClutterTextBuffer *buffer,
4114 ClutterTextPrivate *priv;
4116 gint new_selection_bound;
4119 if (priv->position >= 0 || priv->selection_bound >= 0)
4121 new_position = priv->position;
4122 new_selection_bound = priv->selection_bound;
4124 if (position < new_position)
4125 new_position -= n_chars;
4126 if (position < new_selection_bound)
4127 new_selection_bound -= n_chars;
4129 if (priv->position != new_position || priv->selection_bound != new_selection_bound)
4130 clutter_text_set_positions (self, new_position, new_selection_bound);
4133 g_signal_emit (self, text_signals[DELETE_TEXT], 0, position, position + n_chars);
4137 buffer_notify_text (ClutterTextBuffer *buffer,
4141 g_object_freeze_notify (G_OBJECT (self));
4143 clutter_text_dirty_cache (self);
4145 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
4147 g_signal_emit (self, text_signals[TEXT_CHANGED], 0);
4148 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT]);
4150 g_object_thaw_notify (G_OBJECT (self));
4154 buffer_notify_max_length (ClutterTextBuffer *buffer,
4158 g_object_notify (G_OBJECT (self), "max-length");
4162 buffer_connect_signals (ClutterText *self)
4164 ClutterTextPrivate *priv = self->priv;
4165 g_signal_connect (priv->buffer, "inserted-text", G_CALLBACK (buffer_inserted_text), self);
4166 g_signal_connect (priv->buffer, "deleted-text", G_CALLBACK (buffer_deleted_text), self);
4167 g_signal_connect (priv->buffer, "notify::text", G_CALLBACK (buffer_notify_text), self);
4168 g_signal_connect (priv->buffer, "notify::max-length", G_CALLBACK (buffer_notify_max_length), self);
4172 buffer_disconnect_signals (ClutterText *self)
4174 ClutterTextPrivate *priv = self->priv;
4175 g_signal_handlers_disconnect_by_func (priv->buffer, buffer_inserted_text, self);
4176 g_signal_handlers_disconnect_by_func (priv->buffer, buffer_deleted_text, self);
4177 g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_text, self);
4178 g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_max_length, self);
4182 * clutter_text_new_with_buffer:
4183 * @buffer: The buffer to use for the new #ClutterText.
4185 * Creates a new entry with the specified text buffer.
4187 * Return value: a new #ClutterText
4192 clutter_text_new_with_buffer (ClutterTextBuffer *buffer)
4194 g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), NULL);
4195 return g_object_new (CLUTTER_TYPE_TEXT, "buffer", buffer, NULL);
4199 * clutter_text_get_buffer:
4200 * @self: a #ClutterText
4202 * Get the #ClutterTextBuffer object which holds the text for
4205 * Returns: (transfer none): A #GtkEntryBuffer object.
4210 clutter_text_get_buffer (ClutterText *self)
4212 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4214 return get_buffer (self);
4218 * clutter_text_set_buffer:
4219 * @self: a #ClutterText
4220 * @buffer: a #ClutterTextBuffer
4222 * Set the #ClutterTextBuffer object which holds the text for
4228 clutter_text_set_buffer (ClutterText *self,
4229 ClutterTextBuffer *buffer)
4231 ClutterTextPrivate *priv;
4234 g_return_if_fail (CLUTTER_IS_TEXT (self));
4240 g_return_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer));
4241 g_object_ref (buffer);
4246 buffer_disconnect_signals (self);
4247 g_object_unref (priv->buffer);
4250 priv->buffer = buffer;
4253 buffer_connect_signals (self);
4255 obj = G_OBJECT (self);
4256 g_object_freeze_notify (obj);
4257 g_object_notify (obj, "buffer");
4258 g_object_notify (obj, "text");
4259 g_object_notify (obj, "max-length");
4260 g_object_thaw_notify (obj);
4264 * clutter_text_set_editable:
4265 * @self: a #ClutterText
4266 * @editable: whether the #ClutterText should be editable
4268 * Sets whether the #ClutterText actor should be editable.
4270 * An editable #ClutterText with key focus set using
4271 * clutter_actor_grab_key_focus() or clutter_stage_set_key_focus()
4272 * will receive key events and will update its contents accordingly.
4277 clutter_text_set_editable (ClutterText *self,
4280 ClutterTextPrivate *priv;
4282 g_return_if_fail (CLUTTER_IS_TEXT (self));
4286 if (priv->editable != editable)
4288 priv->editable = editable;
4290 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4292 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EDITABLE]);
4297 * clutter_text_get_editable:
4298 * @self: a #ClutterText
4300 * Retrieves whether a #ClutterText is editable or not.
4302 * Return value: %TRUE if the actor is editable
4307 clutter_text_get_editable (ClutterText *self)
4309 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
4311 return self->priv->editable;
4315 * clutter_text_set_selectable:
4316 * @self: a #ClutterText
4317 * @selectable: whether the #ClutterText actor should be selectable
4319 * Sets whether a #ClutterText actor should be selectable.
4321 * A selectable #ClutterText will allow selecting its contents using
4322 * the pointer or the keyboard.
4327 clutter_text_set_selectable (ClutterText *self,
4328 gboolean selectable)
4330 ClutterTextPrivate *priv;
4332 g_return_if_fail (CLUTTER_IS_TEXT (self));
4336 if (priv->selectable != selectable)
4338 priv->selectable = selectable;
4340 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4342 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTABLE]);
4347 * clutter_text_get_selectable:
4348 * @self: a #ClutterText
4350 * Retrieves whether a #ClutterText is selectable or not.
4352 * Return value: %TRUE if the actor is selectable
4357 clutter_text_get_selectable (ClutterText *self)
4359 g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
4361 return self->priv->selectable;
4365 * clutter_text_set_activatable:
4366 * @self: a #ClutterText
4367 * @activatable: whether the #ClutterText actor should be activatable
4369 * Sets whether a #ClutterText actor should be activatable.
4371 * An activatable #ClutterText actor will emit the #ClutterText::activate
4372 * signal whenever the 'Enter' (or 'Return') key is pressed; if it is not
4373 * activatable, a new line will be appended to the current content.
4375 * An activatable #ClutterText must also be set as editable using
4376 * clutter_text_set_editable().
4381 clutter_text_set_activatable (ClutterText *self,
4382 gboolean activatable)
4384 ClutterTextPrivate *priv;
4386 g_return_if_fail (CLUTTER_IS_TEXT (self));
4390 if (priv->activatable != activatable)
4392 priv->activatable = activatable;
4394 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4396 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]);
4401 * clutter_text_get_activatable:
4402 * @self: a #ClutterText
4404 * Retrieves whether a #ClutterText is activatable or not.
4406 * Return value: %TRUE if the actor is activatable
4411 clutter_text_get_activatable (ClutterText *self)
4413 g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
4415 return self->priv->activatable;
4419 * clutter_text_activate:
4420 * @self: a #ClutterText
4422 * Emits the #ClutterText::activate signal, if @self has been set
4423 * as activatable using clutter_text_set_activatable().
4425 * This function can be used to emit the ::activate signal inside
4426 * a #ClutterActor::captured-event or #ClutterActor::key-press-event
4427 * signal handlers before the default signal handler for the
4428 * #ClutterText is invoked.
4430 * Return value: %TRUE if the ::activate signal has been emitted,
4431 * and %FALSE otherwise
4436 clutter_text_activate (ClutterText *self)
4438 ClutterTextPrivate *priv;
4440 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
4444 if (priv->activatable)
4446 g_signal_emit (self, text_signals[ACTIVATE], 0);
4454 * clutter_text_set_cursor_visible:
4455 * @self: a #ClutterText
4456 * @cursor_visible: whether the cursor should be visible
4458 * Sets whether the cursor of a #ClutterText actor should be
4461 * The color of the cursor will be the same as the text color
4462 * unless clutter_text_set_cursor_color() has been called.
4464 * The size of the cursor can be set using clutter_text_set_cursor_size().
4466 * The position of the cursor can be changed programmatically using
4467 * clutter_text_set_cursor_position().
4472 clutter_text_set_cursor_visible (ClutterText *self,
4473 gboolean cursor_visible)
4475 ClutterTextPrivate *priv;
4477 g_return_if_fail (CLUTTER_IS_TEXT (self));
4481 if (priv->cursor_visible != cursor_visible)
4483 priv->cursor_visible = cursor_visible;
4485 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4487 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_VISIBLE]);
4492 * clutter_text_get_cursor_visible:
4493 * @self: a #ClutterText
4495 * Retrieves whether the cursor of a #ClutterText actor is visible.
4497 * Return value: %TRUE if the cursor is visible
4502 clutter_text_get_cursor_visible (ClutterText *self)
4504 g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
4506 return self->priv->cursor_visible;
4510 * clutter_text_set_cursor_color:
4511 * @self: a #ClutterText
4512 * @color: (allow-none): the color of the cursor, or %NULL to unset it
4514 * Sets the color of the cursor of a #ClutterText actor.
4516 * If @color is %NULL, the cursor color will be the same as the
4522 clutter_text_set_cursor_color (ClutterText *self,
4523 const ClutterColor *color)
4525 g_return_if_fail (CLUTTER_IS_TEXT (self));
4527 clutter_text_set_color_animated (self, obj_props[PROP_CURSOR_COLOR], color);
4531 * clutter_text_get_cursor_color:
4532 * @self: a #ClutterText
4533 * @color: (out): return location for a #ClutterColor
4535 * Retrieves the color of the cursor of a #ClutterText actor.
4540 clutter_text_get_cursor_color (ClutterText *self,
4541 ClutterColor *color)
4543 ClutterTextPrivate *priv;
4545 g_return_if_fail (CLUTTER_IS_TEXT (self));
4546 g_return_if_fail (color != NULL);
4550 *color = priv->cursor_color;
4554 * clutter_text_set_selection:
4555 * @self: a #ClutterText
4556 * @start_pos: start of the selection, in characters
4557 * @end_pos: end of the selection, in characters
4559 * Selects the region of text between @start_pos and @end_pos.
4561 * This function changes the position of the cursor to match
4562 * @start_pos and the selection bound to match @end_pos.
4567 clutter_text_set_selection (ClutterText *self,
4573 g_return_if_fail (CLUTTER_IS_TEXT (self));
4575 n_chars = clutter_text_buffer_get_length (get_buffer (self));
4579 start_pos = MIN (n_chars, start_pos);
4580 end_pos = MIN (n_chars, end_pos);
4582 clutter_text_set_positions (self, start_pos, end_pos);
4586 * clutter_text_get_selection:
4587 * @self: a #ClutterText
4589 * Retrieves the currently selected text.
4591 * Return value: a newly allocated string containing the currently
4592 * selected text, or %NULL. Use g_free() to free the returned
4598 clutter_text_get_selection (ClutterText *self)
4600 ClutterTextPrivate *priv;
4603 gint start_index, end_index;
4604 gint start_offset, end_offset;
4607 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4611 start_index = priv->position;
4612 end_index = priv->selection_bound;
4614 if (end_index == start_index)
4615 return g_strdup ("");
4617 if ((end_index != -1 && end_index < start_index) ||
4620 gint temp = start_index;
4621 start_index = end_index;
4625 text = clutter_text_buffer_get_text (get_buffer (self));
4626 start_offset = offset_to_bytes (text, start_index);
4627 end_offset = offset_to_bytes (text, end_index);
4628 len = end_offset - start_offset;
4630 str = g_malloc (len + 1);
4631 g_utf8_strncpy (str, text + start_offset, end_index - start_index);
4637 * clutter_text_set_selection_bound:
4638 * @self: a #ClutterText
4639 * @selection_bound: the position of the end of the selection, in characters
4641 * Sets the other end of the selection, starting from the current
4644 * If @selection_bound is -1, the selection unset.
4649 clutter_text_set_selection_bound (ClutterText *self,
4650 gint selection_bound)
4652 ClutterTextPrivate *priv;
4654 g_return_if_fail (CLUTTER_IS_TEXT (self));
4658 if (priv->selection_bound != selection_bound)
4660 gint len = clutter_text_buffer_get_length (get_buffer (self));;
4662 if (selection_bound < 0 || selection_bound >= len)
4663 priv->selection_bound = -1;
4665 priv->selection_bound = selection_bound;
4667 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4669 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
4674 * clutter_text_get_selection_bound:
4675 * @self: a #ClutterText
4677 * Retrieves the other end of the selection of a #ClutterText actor,
4678 * in characters from the current cursor position.
4680 * Return value: the position of the other end of the selection
4685 clutter_text_get_selection_bound (ClutterText *self)
4687 g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1);
4689 return self->priv->selection_bound;
4693 * clutter_text_set_selection_color:
4694 * @self: a #ClutterText
4695 * @color: (allow-none): the color of the selection, or %NULL to unset it
4697 * Sets the color of the selection of a #ClutterText actor.
4699 * If @color is %NULL, the selection color will be the same as the
4700 * cursor color, or if no cursor color is set either then it will be
4701 * the same as the text color.
4706 clutter_text_set_selection_color (ClutterText *self,
4707 const ClutterColor *color)
4709 g_return_if_fail (CLUTTER_IS_TEXT (self));
4711 clutter_text_set_color_animated (self, obj_props[PROP_SELECTION_COLOR],
4716 * clutter_text_get_selection_color:
4717 * @self: a #ClutterText
4718 * @color: (out caller-allocates): return location for a #ClutterColor
4720 * Retrieves the color of the selection of a #ClutterText actor.
4725 clutter_text_get_selection_color (ClutterText *self,
4726 ClutterColor *color)
4728 ClutterTextPrivate *priv;
4730 g_return_if_fail (CLUTTER_IS_TEXT (self));
4731 g_return_if_fail (color != NULL);
4735 *color = priv->selection_color;
4739 * clutter_text_set_selected_text_color:
4740 * @self: a #ClutterText
4741 * @color: (allow-none): the selected text color, or %NULL to unset it
4743 * Sets the selected text color of a #ClutterText actor.
4745 * If @color is %NULL, the selected text color will be the same as the
4746 * selection color, which then falls back to cursor, and then text color.
4751 clutter_text_set_selected_text_color (ClutterText *self,
4752 const ClutterColor *color)
4754 g_return_if_fail (CLUTTER_IS_TEXT (self));
4756 clutter_text_set_color_animated (self, obj_props[PROP_SELECTED_TEXT_COLOR],
4761 * clutter_text_get_selected_text_color:
4762 * @self: a #ClutterText
4763 * @color: (out caller-allocates): return location for a #ClutterColor
4765 * Retrieves the color of selected text of a #ClutterText actor.
4770 clutter_text_get_selected_text_color (ClutterText *self,
4771 ClutterColor *color)
4773 ClutterTextPrivate *priv;
4775 g_return_if_fail (CLUTTER_IS_TEXT (self));
4776 g_return_if_fail (color != NULL);
4780 *color = priv->selected_text_color;
4784 * clutter_text_set_font_description:
4785 * @self: a #ClutterText
4786 * @font_desc: a #PangoFontDescription
4788 * Sets @font_desc as the font description for a #ClutterText
4790 * The #PangoFontDescription is copied by the #ClutterText actor
4791 * so you can safely call pango_font_description_free() on it after
4792 * calling this function.
4797 clutter_text_set_font_description (ClutterText *self,
4798 PangoFontDescription *font_desc)
4800 PangoFontDescription *copy;
4802 g_return_if_fail (CLUTTER_IS_TEXT (self));
4804 copy = pango_font_description_copy (font_desc);
4805 clutter_text_set_font_description_internal (self, copy);
4809 * clutter_text_get_font_description:
4810 * @self: a #ClutterText
4812 * Retrieves the #PangoFontDescription used by @self
4814 * Return value: a #PangoFontDescription. The returned value is owned
4815 * by the #ClutterText actor and it should not be modified or freed
4819 PangoFontDescription *
4820 clutter_text_get_font_description (ClutterText *self)
4822 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4824 return self->priv->font_desc;
4828 * clutter_text_get_font_name:
4829 * @self: a #ClutterText
4831 * Retrieves the font name as set by clutter_text_set_font_name().
4833 * Return value: a string containing the font name. The returned
4834 * string is owned by the #ClutterText actor and should not be
4840 clutter_text_get_font_name (ClutterText *text)
4842 g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL);
4844 return text->priv->font_name;
4848 * clutter_text_set_font_name:
4849 * @self: a #ClutterText
4850 * @font_name: (allow-none): a font name, or %NULL to set the default font name
4852 * Sets the font used by a #ClutterText. The @font_name string
4853 * must either be %NULL, which means that the font name from the
4854 * default #ClutterBackend will be used; or be something that can
4855 * be parsed by the pango_font_description_from_string() function,
4859 * clutter_text_set_font_name (text, "Sans 10pt");
4860 * clutter_text_set_font_name (text, "Serif 16px");
4861 * clutter_text_set_font_name (text, "Helvetica 10");
4867 clutter_text_set_font_name (ClutterText *self,
4868 const gchar *font_name)
4870 ClutterTextPrivate *priv;
4871 PangoFontDescription *desc;
4872 gboolean is_default_font;
4874 g_return_if_fail (CLUTTER_IS_TEXT (self));
4876 /* get the default font name from the backend */
4877 if (font_name == NULL || font_name[0] == '\0')
4879 ClutterSettings *settings = clutter_settings_get_default ();
4880 gchar *default_font_name = NULL;
4882 g_object_get (settings, "font-name", &default_font_name, NULL);
4884 if (default_font_name != NULL)
4885 font_name = default_font_name;
4889 default_font_name = g_strdup ("Sans 12");
4892 is_default_font = TRUE;
4895 is_default_font = FALSE;
4899 if (g_strcmp0 (priv->font_name, font_name) == 0)
4902 desc = pango_font_description_from_string (font_name);
4905 g_warning ("Attempting to create a PangoFontDescription for "
4906 "font name '%s', but failed.",
4911 /* this will set the font_name field as well */
4912 clutter_text_set_font_description_internal (self, desc);
4913 priv->is_default_font = is_default_font;
4915 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_NAME]);
4918 if (is_default_font)
4919 g_free ((gchar *) font_name);
4923 * clutter_text_get_text:
4924 * @self: a #ClutterText
4926 * Retrieves a pointer to the current contents of a #ClutterText
4929 * If you need a copy of the contents for manipulating, either
4930 * use g_strdup() on the returned string, or use:
4933 * copy = clutter_text_get_chars (text, 0, -1);
4936 * Which will return a newly allocated string.
4938 * If the #ClutterText actor is empty, this function will return
4939 * an empty string, and not %NULL.
4941 * Return value: (transfer none): the contents of the actor. The returned
4942 * string is owned by the #ClutterText actor and should never be modified
4948 clutter_text_get_text (ClutterText *self)
4950 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4952 return clutter_text_buffer_get_text (get_buffer (self));
4956 clutter_text_set_use_markup_internal (ClutterText *self,
4957 gboolean use_markup)
4959 ClutterTextPrivate *priv = self->priv;
4961 if (priv->use_markup != use_markup)
4963 priv->use_markup = use_markup;
4965 /* reset the attributes lists so that they can be
4968 if (priv->effective_attrs != NULL)
4970 pango_attr_list_unref (priv->effective_attrs);
4971 priv->effective_attrs = NULL;
4974 if (priv->markup_attrs)
4976 pango_attr_list_unref (priv->markup_attrs);
4977 priv->markup_attrs = NULL;
4980 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_USE_MARKUP]);
4985 * clutter_text_set_text:
4986 * @self: a #ClutterText
4987 * @text: (allow-none): the text to set. Passing %NULL is the same
4988 * as passing "" (the empty string)
4990 * Sets the contents of a #ClutterText actor.
4992 * If the #ClutterText:use-markup property was set to %TRUE it
4993 * will be reset to %FALSE as a side effect. If you want to
4994 * maintain the #ClutterText:use-markup you should use the
4995 * clutter_text_set_markup() function instead
5000 clutter_text_set_text (ClutterText *self,
5003 g_return_if_fail (CLUTTER_IS_TEXT (self));
5005 /* if the text is editable (i.e. there is not markup flag to reset) then
5006 * changing the contents will result in selection and cursor changes that
5009 if (self->priv->editable)
5011 if (g_strcmp0 (clutter_text_buffer_get_text (get_buffer (self)), text) == 0)
5015 clutter_text_set_use_markup_internal (self, FALSE);
5016 clutter_text_buffer_set_text (get_buffer (self), text ? text : "", -1);
5020 * clutter_text_set_markup:
5021 * @self: a #ClutterText
5022 * @markup: (allow-none): a string containing Pango markup.
5023 * Passing %NULL is the same as passing "" (the empty string)
5025 * Sets @markup as the contents of a #ClutterText.
5027 * This is a convenience function for setting a string containing
5028 * Pango markup, and it is logically equivalent to:
5031 * /* the order is important */
5032 * clutter_text_set_text (CLUTTER_TEXT (actor), markup);
5033 * clutter_text_set_use_markup (CLUTTER_TEXT (actor), TRUE);
5039 clutter_text_set_markup (ClutterText *self,
5040 const gchar *markup)
5042 g_return_if_fail (CLUTTER_IS_TEXT (self));
5044 clutter_text_set_use_markup_internal (self, TRUE);
5045 if (markup != NULL && *markup != '\0')
5046 clutter_text_set_markup_internal (self, markup);
5048 clutter_text_buffer_set_text (get_buffer (self), "", 0);
5052 * clutter_text_get_layout:
5053 * @self: a #ClutterText
5055 * Retrieves the current #PangoLayout used by a #ClutterText actor.
5057 * Return value: (transfer none): a #PangoLayout. The returned object is owned by
5058 * the #ClutterText actor and should not be modified or freed
5063 clutter_text_get_layout (ClutterText *self)
5065 gfloat width, height;
5067 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
5069 if (self->priv->editable && self->priv->single_line_mode)
5070 return clutter_text_create_layout (self, -1, -1);
5072 clutter_actor_get_size (CLUTTER_ACTOR (self), &width, &height);
5074 return clutter_text_create_layout (self, width, height);
5078 * clutter_text_set_color:
5079 * @self: a #ClutterText
5080 * @color: a #ClutterColor
5082 * Sets the color of the contents of a #ClutterText actor.
5084 * The overall opacity of the #ClutterText actor will be the
5085 * result of the alpha value of @color and the composited
5086 * opacity of the actor itself on the scenegraph, as returned
5087 * by clutter_actor_get_paint_opacity().
5092 clutter_text_set_color (ClutterText *self,
5093 const ClutterColor *color)
5095 g_return_if_fail (CLUTTER_IS_TEXT (self));
5096 g_return_if_fail (color != NULL);
5098 clutter_text_set_color_animated (self, obj_props[PROP_COLOR], color);
5102 * clutter_text_get_color:
5103 * @self: a #ClutterText
5104 * @color: (out caller-allocates): return location for a #ClutterColor
5106 * Retrieves the text color as set by clutter_text_set_color().
5111 clutter_text_get_color (ClutterText *self,
5112 ClutterColor *color)
5114 ClutterTextPrivate *priv;
5116 g_return_if_fail (CLUTTER_IS_TEXT (self));
5117 g_return_if_fail (color != NULL);
5121 *color = priv->text_color;
5125 * clutter_text_set_ellipsize:
5126 * @self: a #ClutterText
5127 * @mode: a #PangoEllipsizeMode
5129 * Sets the mode used to ellipsize (add an ellipsis: "...") to the
5130 * text if there is not enough space to render the entire contents
5131 * of a #ClutterText actor
5136 clutter_text_set_ellipsize (ClutterText *self,
5137 PangoEllipsizeMode mode)
5139 ClutterTextPrivate *priv;
5141 g_return_if_fail (CLUTTER_IS_TEXT (self));
5142 g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE &&
5143 mode <= PANGO_ELLIPSIZE_END);
5147 if ((PangoEllipsizeMode) priv->ellipsize != mode)
5149 priv->ellipsize = mode;
5151 clutter_text_dirty_cache (self);
5153 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5155 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ELLIPSIZE]);
5160 * clutter_text_get_ellipsize:
5161 * @self: a #ClutterText
5163 * Returns the ellipsizing position of a #ClutterText actor, as
5164 * set by clutter_text_set_ellipsize().
5166 * Return value: #PangoEllipsizeMode
5171 clutter_text_get_ellipsize (ClutterText *self)
5173 g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ELLIPSIZE_NONE);
5175 return self->priv->ellipsize;
5179 * clutter_text_get_line_wrap:
5180 * @self: a #ClutterText
5182 * Retrieves the value set using clutter_text_set_line_wrap().
5184 * Return value: %TRUE if the #ClutterText actor should wrap
5190 clutter_text_get_line_wrap (ClutterText *self)
5192 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5194 return self->priv->wrap;
5198 * clutter_text_set_line_wrap:
5199 * @self: a #ClutterText
5200 * @line_wrap: whether the contents should wrap
5202 * Sets whether the contents of a #ClutterText actor should wrap,
5203 * if they don't fit the size assigned to the actor.
5208 clutter_text_set_line_wrap (ClutterText *self,
5211 ClutterTextPrivate *priv;
5213 g_return_if_fail (CLUTTER_IS_TEXT (self));
5217 if (priv->wrap != line_wrap)
5219 priv->wrap = line_wrap;
5221 clutter_text_dirty_cache (self);
5223 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5225 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_WRAP]);
5230 * clutter_text_set_line_wrap_mode:
5231 * @self: a #ClutterText
5232 * @wrap_mode: the line wrapping mode
5234 * If line wrapping is enabled (see clutter_text_set_line_wrap()) this
5235 * function controls how the line wrapping is performed. The default is
5236 * %PANGO_WRAP_WORD which means wrap on word boundaries.
5241 clutter_text_set_line_wrap_mode (ClutterText *self,
5242 PangoWrapMode wrap_mode)
5244 ClutterTextPrivate *priv;
5246 g_return_if_fail (CLUTTER_IS_TEXT (self));
5250 if (priv->wrap_mode != wrap_mode)
5252 priv->wrap_mode = wrap_mode;
5254 clutter_text_dirty_cache (self);
5256 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5258 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_WRAP_MODE]);
5263 * clutter_text_get_line_wrap_mode:
5264 * @self: a #ClutterText
5266 * Retrieves the line wrap mode used by the #ClutterText actor.
5268 * See clutter_text_set_line_wrap_mode ().
5270 * Return value: the wrap mode used by the #ClutterText
5275 clutter_text_get_line_wrap_mode (ClutterText *self)
5277 g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_WRAP_WORD);
5279 return self->priv->wrap_mode;
5283 * clutter_text_set_attributes:
5284 * @self: a #ClutterText
5285 * @attrs: (allow-none): a #PangoAttrList or %NULL to unset the attributes
5287 * Sets the attributes list that are going to be applied to the
5288 * #ClutterText contents.
5290 * The #ClutterText actor will take a reference on the #PangoAttrList
5291 * passed to this function.
5296 clutter_text_set_attributes (ClutterText *self,
5297 PangoAttrList *attrs)
5299 ClutterTextPrivate *priv;
5301 g_return_if_fail (CLUTTER_IS_TEXT (self));
5306 pango_attr_list_ref (attrs);
5309 pango_attr_list_unref (priv->attrs);
5311 priv->attrs = attrs;
5313 /* Clear the effective attributes so they will be regenerated when a
5314 layout is created */
5315 if (priv->effective_attrs)
5317 pango_attr_list_unref (priv->effective_attrs);
5318 priv->effective_attrs = NULL;
5321 clutter_text_dirty_cache (self);
5323 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ATTRIBUTES]);
5325 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5329 * clutter_text_get_attributes:
5330 * @self: a #ClutterText
5332 * Gets the attribute list that was set on the #ClutterText actor
5333 * clutter_text_set_attributes(), if any.
5335 * Return value: (transfer none): the attribute list, or %NULL if none was set. The
5336 * returned value is owned by the #ClutterText and should not be unreferenced.
5341 clutter_text_get_attributes (ClutterText *self)
5343 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
5345 return self->priv->attrs;
5349 * clutter_text_set_line_alignment:
5350 * @self: a #ClutterText
5351 * @alignment: A #PangoAlignment
5353 * Sets the way that the lines of a wrapped label are aligned with
5354 * respect to each other. This does not affect the overall alignment
5355 * of the label within its allocated or specified width.
5357 * To align a #ClutterText actor you should add it to a container
5358 * that supports alignment, or use the anchor point.
5363 clutter_text_set_line_alignment (ClutterText *self,
5364 PangoAlignment alignment)
5366 ClutterTextPrivate *priv;
5368 g_return_if_fail (CLUTTER_IS_TEXT (self));
5372 if (priv->alignment != alignment)
5374 priv->alignment = alignment;
5376 clutter_text_dirty_cache (self);
5378 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5380 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_ALIGNMENT]);
5385 * clutter_text_get_line_alignment:
5386 * @self: a #ClutterText
5388 * Retrieves the alignment of a #ClutterText, as set by
5389 * clutter_text_set_line_alignment().
5391 * Return value: a #PangoAlignment
5396 clutter_text_get_line_alignment (ClutterText *self)
5398 g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ALIGN_LEFT);
5400 return self->priv->alignment;
5404 * clutter_text_set_use_markup:
5405 * @self: a #ClutterText
5406 * @setting: %TRUE if the text should be parsed for markup.
5408 * Sets whether the contents of the #ClutterText actor contains markup
5409 * in <link linkend="PangoMarkupFormat">Pango's text markup language</link>.
5411 * Setting #ClutterText:use-markup on an editable #ClutterText will
5412 * not have any effect except hiding the markup.
5414 * See also #ClutterText:use-markup.
5419 clutter_text_set_use_markup (ClutterText *self,
5424 g_return_if_fail (CLUTTER_IS_TEXT (self));
5426 text = clutter_text_buffer_get_text (get_buffer (self));
5428 clutter_text_set_use_markup_internal (self, setting);
5431 clutter_text_set_markup_internal (self, text);
5433 clutter_text_dirty_cache (self);
5435 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5439 * clutter_text_get_use_markup:
5440 * @self: a #ClutterText
5442 * Retrieves whether the contents of the #ClutterText actor should be
5443 * parsed for the Pango text markup.
5445 * Return value: %TRUE if the contents will be parsed for markup
5450 clutter_text_get_use_markup (ClutterText *self)
5452 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5454 return self->priv->use_markup;
5458 * clutter_text_set_justify:
5459 * @self: a #ClutterText
5460 * @justify: whether the text should be justified
5462 * Sets whether the text of the #ClutterText actor should be justified
5463 * on both margins. This setting is ignored if Clutter is compiled
5464 * against Pango < 1.18.
5469 clutter_text_set_justify (ClutterText *self,
5472 ClutterTextPrivate *priv;
5474 g_return_if_fail (CLUTTER_IS_TEXT (self));
5478 if (priv->justify != justify)
5480 priv->justify = justify;
5482 clutter_text_dirty_cache (self);
5484 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5486 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_JUSTIFY]);
5491 * clutter_text_get_justify:
5492 * @self: a #ClutterText
5494 * Retrieves whether the #ClutterText actor should justify its contents
5497 * Return value: %TRUE if the text should be justified
5502 clutter_text_get_justify (ClutterText *self)
5504 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5506 return self->priv->justify;
5510 * clutter_text_get_cursor_position:
5511 * @self: a #ClutterText
5513 * Retrieves the cursor position.
5515 * Return value: the cursor position, in characters
5520 clutter_text_get_cursor_position (ClutterText *self)
5522 g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1);
5524 return self->priv->position;
5528 * clutter_text_set_cursor_position:
5529 * @self: a #ClutterText
5530 * @position: the new cursor position, in characters
5532 * Sets the cursor of a #ClutterText actor at @position.
5534 * The position is expressed in characters, not in bytes.
5539 clutter_text_set_cursor_position (ClutterText *self,
5542 ClutterTextPrivate *priv;
5545 g_return_if_fail (CLUTTER_IS_TEXT (self));
5549 if (priv->position == position)
5552 len = clutter_text_buffer_get_length (get_buffer (self));
5554 if (position < 0 || position >= len)
5555 priv->position = -1;
5557 priv->position = position;
5559 /* Forget the target x position so that it will be recalculated next
5560 time the cursor is moved up or down */
5563 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
5565 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]);
5569 * clutter_text_set_cursor_size:
5570 * @self: a #ClutterText
5571 * @size: the size of the cursor, in pixels, or -1 to use the
5574 * Sets the size of the cursor of a #ClutterText. The cursor
5575 * will only be visible if the #ClutterText:cursor-visible property
5581 clutter_text_set_cursor_size (ClutterText *self,
5584 ClutterTextPrivate *priv;
5586 g_return_if_fail (CLUTTER_IS_TEXT (self));
5590 if (priv->cursor_size != size)
5593 size = DEFAULT_CURSOR_SIZE;
5595 priv->cursor_size = size;
5597 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
5599 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_SIZE]);
5604 * clutter_text_get_cursor_size:
5605 * @self: a #ClutterText
5607 * Retrieves the size of the cursor of a #ClutterText actor.
5609 * Return value: the size of the cursor, in pixels
5614 clutter_text_get_cursor_size (ClutterText *self)
5616 g_return_val_if_fail (CLUTTER_IS_TEXT (self), DEFAULT_CURSOR_SIZE);
5618 return self->priv->cursor_size;
5622 * clutter_text_set_password_char:
5623 * @self: a #ClutterText
5624 * @wc: a Unicode character, or 0 to unset the password character
5626 * Sets the character to use in place of the actual text in a
5627 * password text actor.
5629 * If @wc is 0 the text will be displayed as it is entered in the
5630 * #ClutterText actor.
5635 clutter_text_set_password_char (ClutterText *self,
5638 ClutterTextPrivate *priv;
5640 g_return_if_fail (CLUTTER_IS_TEXT (self));
5644 if (priv->password_char != wc)
5646 priv->password_char = wc;
5648 clutter_text_dirty_cache (self);
5649 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5651 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PASSWORD_CHAR]);
5656 * clutter_text_get_password_char:
5657 * @self: a #ClutterText
5659 * Retrieves the character to use in place of the actual text
5660 * as set by clutter_text_set_password_char().
5662 * Return value: a Unicode character or 0 if the password
5663 * character is not set
5668 clutter_text_get_password_char (ClutterText *self)
5670 g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
5672 return self->priv->password_char;
5676 * clutter_text_set_max_length:
5677 * @self: a #ClutterText
5678 * @max: the maximum number of characters allowed in the text actor; 0
5679 * to disable or -1 to set the length of the current string
5681 * Sets the maximum allowed length of the contents of the actor. If the
5682 * current contents are longer than the given length, then they will be
5688 clutter_text_set_max_length (ClutterText *self,
5691 g_return_if_fail (CLUTTER_IS_TEXT (self));
5692 clutter_text_buffer_set_max_length (get_buffer (self), max);
5696 * clutter_text_get_max_length:
5697 * @self: a #ClutterText
5699 * Gets the maximum length of text that can be set into a text actor.
5701 * See clutter_text_set_max_length().
5703 * Return value: the maximum number of characters.
5708 clutter_text_get_max_length (ClutterText *self)
5710 g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
5712 return clutter_text_buffer_get_max_length (get_buffer (self));
5716 * clutter_text_insert_unichar:
5717 * @self: a #ClutterText
5718 * @wc: a Unicode character
5720 * Inserts @wc at the current cursor position of a
5721 * #ClutterText actor.
5726 clutter_text_insert_unichar (ClutterText *self,
5729 ClutterTextPrivate *priv;
5734 new = g_string_new ("");
5735 g_string_append_unichar (new, wc);
5737 clutter_text_buffer_insert_text (get_buffer (self), priv->position, new->str, 1);
5739 g_string_free (new, TRUE);
5743 * clutter_text_insert_text:
5744 * @self: a #ClutterText
5745 * @text: the text to be inserted
5746 * @position: the position of the insertion, or -1
5748 * Inserts @text into a #ClutterActor at the given position.
5750 * If @position is a negative number, the text will be appended
5751 * at the end of the current contents of the #ClutterText.
5753 * The position is expressed in characters, not in bytes.
5758 clutter_text_insert_text (ClutterText *self,
5762 g_return_if_fail (CLUTTER_IS_TEXT (self));
5763 g_return_if_fail (text != NULL);
5765 clutter_text_buffer_insert_text (get_buffer (self), position, text,
5766 g_utf8_strlen (text, -1));
5770 * clutter_text_delete_text:
5771 * @self: a #ClutterText
5772 * @start_pos: starting position
5773 * @end_pos: ending position
5775 * Deletes the text inside a #ClutterText actor between @start_pos
5778 * The starting and ending positions are expressed in characters,
5784 clutter_text_delete_text (ClutterText *self,
5788 g_return_if_fail (CLUTTER_IS_TEXT (self));
5790 clutter_text_buffer_delete_text (get_buffer (self), start_pos, end_pos - start_pos);
5794 * clutter_text_delete_chars:
5795 * @self: a #ClutterText
5796 * @n_chars: the number of characters to delete
5798 * Deletes @n_chars inside a #ClutterText actor, starting from the
5799 * current cursor position.
5801 * Somewhat awkwardly, the cursor position is decremented by the same
5802 * number of characters you've deleted.
5807 clutter_text_delete_chars (ClutterText *self,
5810 ClutterTextPrivate *priv;
5812 g_return_if_fail (CLUTTER_IS_TEXT (self));
5816 clutter_text_buffer_delete_text (get_buffer (self), priv->position, n_chars);
5818 if (priv->position > 0)
5819 clutter_text_set_cursor_position (self, priv->position - n_chars);
5823 * clutter_text_get_chars:
5824 * @self: a #ClutterText
5825 * @start_pos: start of text, in characters
5826 * @end_pos: end of text, in characters
5828 * Retrieves the contents of the #ClutterText actor between
5829 * @start_pos and @end_pos, but not including @end_pos.
5831 * The positions are specified in characters, not in bytes.
5833 * Return value: a newly allocated string with the contents of
5834 * the text actor between the specified positions. Use g_free()
5835 * to free the resources when done
5840 clutter_text_get_chars (ClutterText *self,
5844 gint start_index, end_index;
5848 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
5850 n_chars = clutter_text_buffer_get_length (get_buffer (self));
5851 text = clutter_text_buffer_get_text (get_buffer (self));
5856 start_pos = MIN (n_chars, start_pos);
5857 end_pos = MIN (n_chars, end_pos);
5859 start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
5860 end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
5862 return g_strndup (text + start_index, end_index - start_index);
5866 * clutter_text_set_single_line_mode:
5867 * @self: a #ClutterText
5868 * @single_line: whether to enable single line mode
5870 * Sets whether a #ClutterText actor should be in single line mode
5871 * or not. Only editable #ClutterText<!-- -->s can be in single line
5874 * A text actor in single line mode will not wrap text and will clip
5875 * the visible area to the predefined size. The contents of the
5876 * text actor will scroll to display the end of the text if its length
5877 * is bigger than the allocated width.
5879 * When setting the single line mode the #ClutterText:activatable
5880 * property is also set as a side effect. Instead of entering a new
5881 * line character, the text actor will emit the #ClutterText::activate
5887 clutter_text_set_single_line_mode (ClutterText *self,
5888 gboolean single_line)
5890 ClutterTextPrivate *priv;
5892 g_return_if_fail (CLUTTER_IS_TEXT (self));
5896 if (priv->single_line_mode != single_line)
5898 g_object_freeze_notify (G_OBJECT (self));
5900 priv->single_line_mode = single_line;
5902 if (priv->single_line_mode)
5904 priv->activatable = TRUE;
5906 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]);
5909 clutter_text_dirty_cache (self);
5910 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5912 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SINGLE_LINE_MODE]);
5914 g_object_thaw_notify (G_OBJECT (self));
5919 * clutter_text_get_single_line_mode:
5920 * @self: a #ClutterText
5922 * Retrieves whether the #ClutterText actor is in single line mode.
5924 * Return value: %TRUE if the #ClutterText actor is in single line mode
5929 clutter_text_get_single_line_mode (ClutterText *self)
5931 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5933 return self->priv->single_line_mode;
5937 * clutter_text_set_preedit_string:
5938 * @self: a #ClutterText
5939 * @preedit_str: (allow-none): the pre-edit string, or %NULL to unset it
5940 * @preedit_attrs: (allow-none): the pre-edit string attributes
5941 * @cursor_pos: the cursor position for the pre-edit string
5943 * Sets, or unsets, the pre-edit string. This function is useful
5944 * for input methods to display a string (with eventual specific
5945 * Pango attributes) before it is entered inside the #ClutterText
5948 * The preedit string and attributes are ignored if the #ClutterText
5949 * actor is not editable.
5951 * This function should not be used by applications
5956 clutter_text_set_preedit_string (ClutterText *self,
5957 const gchar *preedit_str,
5958 PangoAttrList *preedit_attrs,
5961 ClutterTextPrivate *priv;
5963 g_return_if_fail (CLUTTER_IS_TEXT (self));
5967 g_free (priv->preedit_str);
5968 priv->preedit_str = NULL;
5970 if (priv->preedit_attrs != NULL)
5972 pango_attr_list_unref (priv->preedit_attrs);
5973 priv->preedit_attrs = NULL;
5976 priv->preedit_n_chars = 0;
5977 priv->preedit_cursor_pos = 0;
5979 if (preedit_str == NULL || *preedit_str == '\0')
5980 priv->preedit_set = FALSE;
5983 priv->preedit_str = g_strdup (preedit_str);
5985 if (priv->preedit_str != NULL)
5986 priv->preedit_n_chars = g_utf8_strlen (priv->preedit_str, -1);
5988 priv->preedit_n_chars = 0;
5990 if (preedit_attrs != NULL)
5991 priv->preedit_attrs = pango_attr_list_ref (preedit_attrs);
5993 priv->preedit_cursor_pos =
5994 CLAMP (cursor_pos, 0, priv->preedit_n_chars);
5996 priv->preedit_set = TRUE;
5999 clutter_text_dirty_cache (self);
6000 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
6005 * clutter_text_get_layout_offsets:
6006 * @self: a #ClutterText
6007 * @x: (out): location to store X offset of layout, or %NULL
6008 * @y: (out): location to store Y offset of layout, or %NULL
6010 * Obtains the coordinates where the #ClutterText will draw the #PangoLayout
6011 * representing the text.
6016 clutter_text_get_layout_offsets (ClutterText *self,
6020 ClutterTextPrivate *priv;
6022 g_return_if_fail (CLUTTER_IS_TEXT (self));