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;
93 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
94 static void clutter_animatable_iface_init (ClutterAnimatableIface *iface);
96 G_DEFINE_TYPE_WITH_CODE (ClutterText,
99 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
100 clutter_scriptable_iface_init)
101 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
102 clutter_animatable_iface_init));
106 /* Cached layout. Pango internally caches the computed extents
107 * when they are requested so there is no need to cache that as
112 /* A number representing the age of this cache (so that when a
113 * new layout is needed the last used cache is replaced)
118 struct _ClutterTextPrivate
120 PangoFontDescription *font_desc;
122 /* the displayed text */
123 ClutterTextBuffer *buffer;
129 ClutterColor text_color;
131 LayoutCache cached_layouts[N_CACHED_LAYOUTS];
134 /* These are the attributes set by the attributes property */
135 PangoAttrList *attrs;
136 /* These are the attributes derived from the text when the
137 use-markup property is set */
138 PangoAttrList *markup_attrs;
139 /* This is the combination of the above two lists. It is set to NULL
140 whenever either of them changes and then regenerated by merging
141 the two lists whenever a layout is needed */
142 PangoAttrList *effective_attrs;
143 /* These are the attributes for the preedit string. These are merged
144 with the effective attributes into a temporary list before
146 PangoAttrList *preedit_attrs;
148 /* current cursor position */
151 /* current 'other end of selection' position */
152 gint selection_bound;
154 /* the x position in the PangoLayout, used to
155 * avoid drifting when repeatedly moving up|down
159 /* the x position of the PangoLayout when in
160 * single line mode, to scroll the contents of the
165 /* the y position of the PangoLayout, fixed to 0 by
169 /* Where to draw the cursor */
170 ClutterGeometry cursor_pos;
171 ClutterColor cursor_color;
174 /* Box representing the paint volume. The box is lazily calculated
176 ClutterPaintVolume paint_volume;
178 guint preedit_cursor_pos;
179 gint preedit_n_chars;
181 ClutterColor selection_color;
183 ClutterColor selected_text_color;
185 gunichar password_char;
187 guint password_hint_id;
188 guint password_hint_timeout;
190 /* Signal handler for when the backend changes its font settings */
191 guint settings_changed_id;
193 /* Signal handler for when the :text-direction changes */
194 guint direction_changed_id;
199 guint use_underline : 1;
200 guint use_markup : 1;
202 guint single_line_mode : 1;
206 guint cursor_visible : 1;
207 guint activatable : 1;
208 guint selectable : 1;
209 guint selection_color_set : 1;
210 guint in_select_drag : 1;
211 guint cursor_color_set : 1;
212 guint preedit_set : 1;
213 guint is_default_font : 1;
215 guint selected_text_color_set : 1;
216 guint paint_volume_valid : 1;
217 guint show_password_hint : 1;
218 guint password_hint_visible : 1;
227 PROP_FONT_DESCRIPTION,
238 PROP_SELECTION_BOUND,
239 PROP_SELECTION_COLOR,
240 PROP_SELECTION_COLOR_SET,
243 PROP_CURSOR_COLOR_SET,
250 PROP_SINGLE_LINE_MODE,
251 PROP_SELECTED_TEXT_COLOR,
252 PROP_SELECTED_TEXT_COLOR_SET,
257 static GParamSpec *obj_props[PROP_LAST];
270 static guint text_signals[LAST_SIGNAL] = { 0, };
272 static void clutter_text_settings_changed_cb (ClutterText *text);
273 static void buffer_connect_signals (ClutterText *self);
274 static void buffer_disconnect_signals (ClutterText *self);
275 static ClutterTextBuffer *get_buffer (ClutterText *self);
278 clutter_text_dirty_paint_volume (ClutterText *text)
280 ClutterTextPrivate *priv = text->priv;
282 if (priv->paint_volume_valid)
284 clutter_paint_volume_free (&priv->paint_volume);
285 priv->paint_volume_valid = FALSE;
290 clutter_text_queue_redraw (ClutterActor *self)
292 /* This is a wrapper for clutter_actor_queue_redraw that also
293 dirties the cached paint volume. It would be nice if we could
294 just override the default implementation of the queue redraw
295 signal to do this instead but that doesn't work because the
296 signal isn't immediately emitted when queue_redraw is called.
297 Clutter will however immediately call get_paint_volume when
298 queue_redraw is called so we do need to dirty it immediately. */
300 clutter_text_dirty_paint_volume (CLUTTER_TEXT (self));
302 clutter_actor_queue_redraw (self);
305 #define clutter_actor_queue_redraw \
306 Please_use_clutter_text_queue_redraw_instead
308 #define offset_real(t,p) ((p) == -1 ? g_utf8_strlen ((t), -1) : (p))
311 offset_to_bytes (const gchar *text,
317 return strlen (text);
319 /* Loop over each character in the string until we either reach the
320 end or the requested position */
321 for (ptr = text; *ptr && pos-- > 0; ptr = g_utf8_next_char (ptr));
326 #define bytes_to_offset(t,p) (g_utf8_pointer_to_offset ((t), (t) + (p)))
329 clutter_text_clear_selection (ClutterText *self)
331 ClutterTextPrivate *priv = self->priv;
333 if (priv->selection_bound != priv->position)
335 priv->selection_bound = priv->position;
336 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
337 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
342 clutter_text_get_display_text (ClutterText *self)
344 ClutterTextPrivate *priv = self->priv;
345 ClutterTextBuffer *buffer;
348 buffer = get_buffer (self);
349 text = clutter_text_buffer_get_text (buffer);
351 /* simple short-circuit to avoid going through GString
352 * with an empty text and a password char set
355 return g_strdup ("");
357 if (G_LIKELY (priv->password_char == 0))
358 return g_strdup (text);
362 gunichar invisible_char;
367 n_chars = clutter_text_buffer_get_length (buffer);
368 str = g_string_sized_new (clutter_text_buffer_get_bytes (buffer));
369 invisible_char = priv->password_char;
371 /* we need to convert the string built of invisible
372 * characters into UTF-8 for it to be fed to the Pango
375 memset (buf, 0, sizeof (buf));
376 char_len = g_unichar_to_utf8 (invisible_char, buf);
378 if (priv->show_password_hint && priv->password_hint_visible)
382 for (i = 0; i < n_chars - 1; i++)
383 g_string_append_len (str, buf, char_len);
385 last_char = g_utf8_offset_to_pointer (text, n_chars - 1);
386 g_string_append (str, last_char);
390 for (i = 0; i < n_chars; i++)
391 g_string_append_len (str, buf, char_len);
394 return g_string_free (str, FALSE);
399 clutter_text_ensure_effective_attributes (ClutterText *self)
401 ClutterTextPrivate *priv = self->priv;
403 /* If we already have the effective attributes then we don't need to
405 if (priv->effective_attrs != NULL)
408 /* same as if we don't have any attribute at all */
409 if (priv->attrs == NULL && priv->markup_attrs == NULL)
412 if (priv->attrs != NULL)
414 /* If there are no markup attributes then we can just use
415 these attributes directly */
416 if (priv->markup_attrs == NULL)
417 priv->effective_attrs = pango_attr_list_ref (priv->attrs);
420 /* Otherwise we need to merge the two lists */
421 PangoAttrIterator *iter;
422 GSList *attributes, *l;
424 priv->effective_attrs = pango_attr_list_copy (priv->markup_attrs);
426 iter = pango_attr_list_get_iterator (priv->attrs);
429 attributes = pango_attr_iterator_get_attrs (iter);
431 for (l = attributes; l != NULL; l = l->next)
433 PangoAttribute *attr = l->data;
435 pango_attr_list_insert (priv->effective_attrs, attr);
438 g_slist_free (attributes);
440 while (pango_attr_iterator_next (iter));
442 pango_attr_iterator_destroy (iter);
445 else if (priv->markup_attrs != NULL)
447 /* We can just use the markup attributes directly */
448 priv->effective_attrs = pango_attr_list_ref (priv->markup_attrs);
453 clutter_text_create_layout_no_cache (ClutterText *text,
456 PangoEllipsizeMode ellipsize)
458 ClutterTextPrivate *priv = text->priv;
463 CLUTTER_STATIC_TIMER (text_layout_timer,
469 CLUTTER_TIMER_START (_clutter_uprof_context, text_layout_timer);
471 layout = clutter_actor_create_pango_layout (CLUTTER_ACTOR (text), NULL);
472 pango_layout_set_font_description (layout, priv->font_desc);
474 contents = clutter_text_get_display_text (text);
475 contents_len = strlen (contents);
477 if (priv->editable && priv->preedit_set)
479 GString *tmp = g_string_new (contents);
480 PangoAttrList *tmp_attrs = pango_attr_list_new ();
483 if (priv->position == 0)
486 cursor_index = offset_to_bytes (contents, priv->position);
488 g_string_insert (tmp, cursor_index, priv->preedit_str);
490 pango_layout_set_text (layout, tmp->str, tmp->len);
492 if (priv->preedit_attrs != NULL)
494 pango_attr_list_splice (tmp_attrs, priv->preedit_attrs,
496 strlen (priv->preedit_str));
498 pango_layout_set_attributes (layout, tmp_attrs);
501 g_string_free (tmp, TRUE);
502 pango_attr_list_unref (tmp_attrs);
505 pango_layout_set_text (layout, contents, contents_len);
509 /* This will merge the markup attributes and the attributes
510 property if needed */
511 clutter_text_ensure_effective_attributes (text);
513 if (priv->effective_attrs != NULL)
514 pango_layout_set_attributes (layout, priv->effective_attrs);
517 pango_layout_set_alignment (layout, priv->alignment);
518 pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode);
519 pango_layout_set_justify (layout, priv->justify);
520 pango_layout_set_wrap (layout, priv->wrap_mode);
522 pango_layout_set_ellipsize (layout, ellipsize);
523 pango_layout_set_width (layout, width);
524 pango_layout_set_height (layout, height);
528 CLUTTER_TIMER_STOP (_clutter_uprof_context, text_layout_timer);
534 clutter_text_dirty_cache (ClutterText *text)
536 ClutterTextPrivate *priv = text->priv;
539 /* Delete the cached layouts so they will be recreated the next time
541 for (i = 0; i < N_CACHED_LAYOUTS; i++)
542 if (priv->cached_layouts[i].layout)
544 g_object_unref (priv->cached_layouts[i].layout);
545 priv->cached_layouts[i].layout = NULL;
548 clutter_text_dirty_paint_volume (text);
552 * clutter_text_set_font_description_internal:
553 * @self: a #ClutterText
554 * @desc: a #PangoFontDescription
556 * Sets @desc as the font description to be used by the #ClutterText
557 * actor. The font description ownership is transferred to @self so
558 * the #PangoFontDescription must not be freed after this function
560 * This function will also set the :font-name field as a side-effect
562 * This function will evict the layout cache, and queue a relayout if
563 * the #ClutterText actor has contents.
566 clutter_text_set_font_description_internal (ClutterText *self,
567 PangoFontDescription *desc)
569 ClutterTextPrivate *priv = self->priv;
571 if (priv->font_desc == desc)
574 if (priv->font_desc != NULL)
575 pango_font_description_free (priv->font_desc);
577 priv->font_desc = desc;
579 /* update the font name string we use */
580 g_free (priv->font_name);
581 priv->font_name = pango_font_description_to_string (priv->font_desc);
583 clutter_text_dirty_cache (self);
585 if (clutter_text_buffer_get_length (get_buffer (self)) != 0)
586 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
588 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_DESCRIPTION]);
592 clutter_text_settings_changed_cb (ClutterText *text)
594 ClutterTextPrivate *priv = text->priv;
595 guint password_hint_time = 0;
596 ClutterSettings *settings;
598 settings = clutter_settings_get_default ();
600 g_object_get (settings, "password-hint-time", &password_hint_time, NULL);
602 priv->show_password_hint = password_hint_time > 0;
603 priv->password_hint_timeout = password_hint_time;
605 if (priv->is_default_font)
607 PangoFontDescription *font_desc;
608 gchar *font_name = NULL;
610 g_object_get (settings, "font-name", &font_name, NULL);
612 CLUTTER_NOTE (ACTOR, "Text[%p]: default font changed to '%s'",
616 font_desc = pango_font_description_from_string (font_name);
617 clutter_text_set_font_description_internal (text, font_desc);
622 clutter_text_dirty_cache (text);
623 clutter_actor_queue_relayout (CLUTTER_ACTOR (text));
627 clutter_text_direction_changed_cb (GObject *gobject,
630 clutter_text_dirty_cache (CLUTTER_TEXT (gobject));
632 /* no need to queue a relayout: set_text_direction() will do that for us */
636 * clutter_text_create_layout:
637 * @text: a #ClutterText
638 * @allocation_width: the allocation width
639 * @allocation_height: the allocation height
641 * Like clutter_text_create_layout_no_cache(), but will also ensure
642 * the glyphs cache. If a previously cached layout generated using the
643 * same width is available then that will be used instead of
644 * generating a new one.
647 clutter_text_create_layout (ClutterText *text,
648 gfloat allocation_width,
649 gfloat allocation_height)
651 ClutterTextPrivate *priv = text->priv;
652 LayoutCache *oldest_cache = priv->cached_layouts;
653 gboolean found_free_cache = FALSE;
656 PangoEllipsizeMode ellipsize = PANGO_ELLIPSIZE_NONE;
659 CLUTTER_STATIC_COUNTER (text_cache_hit_counter,
660 "Text layout cache hit counter",
661 "Increments for each layout cache hit",
663 CLUTTER_STATIC_COUNTER (text_cache_miss_counter,
664 "Text layout cache miss counter",
665 "Increments for each layout cache miss",
668 /* First determine the width, height, and ellipsize mode that
669 * we need for the layout. The ellipsize mode depends on
670 * allocation_width/allocation_size as follows:
672 * Cases, assuming ellipsize != NONE on actor:
674 * Width request: ellipsization can be set or not on layout,
677 * Height request: ellipsization must never be set on layout
678 * if wrap=true, because we need to measure the wrapped
679 * height. It must always be set if wrap=false.
681 * Allocate: ellipsization must always be set.
683 * See http://bugzilla.gnome.org/show_bug.cgi?id=560931
686 if (priv->ellipsize != PANGO_ELLIPSIZE_NONE)
688 if (allocation_height < 0 && priv->wrap)
689 ; /* must not set ellipsization on wrap=true height request */
693 ellipsize = priv->ellipsize;
697 /* When painting, we always need to set the width, since
698 * we might need to align to the right. When getting the
699 * height, however, there are some cases where we know that
700 * the width won't affect the width.
702 * - editable, single-line text actors, since those can
704 * - non-wrapping, non-ellipsizing actors.
706 if (allocation_width >= 0 &&
707 (allocation_height >= 0 ||
708 !((priv->editable && priv->single_line_mode) ||
709 (priv->ellipsize == PANGO_ELLIPSIZE_NONE && !priv->wrap))))
711 width = allocation_width * 1024 + 0.5f;
714 /* Pango only uses height if ellipsization is enabled, so don't set
715 * height if ellipsize isn't set. Pango implicitly enables wrapping
716 * if height is set, so don't set height if wrapping is disabled.
717 * In other words, only set height if we want to both wrap then
718 * ellipsize and we're not in single line mode.
720 * See http://bugzilla.gnome.org/show_bug.cgi?id=560931 if this
723 if (allocation_height >= 0 &&
725 priv->ellipsize != PANGO_ELLIPSIZE_NONE &&
726 !priv->single_line_mode)
728 height = allocation_height * 1024 + 0.5f;
731 /* Search for a cached layout with the same width and keep
732 * track of the oldest one
734 for (i = 0; i < N_CACHED_LAYOUTS; i++)
736 if (priv->cached_layouts[i].layout == NULL)
738 /* Always prefer free cache spaces */
739 found_free_cache = TRUE;
740 oldest_cache = priv->cached_layouts + i;
744 PangoLayout *cached = priv->cached_layouts[i].layout;
745 gint cached_width = pango_layout_get_width (cached);
746 gint cached_height = pango_layout_get_height (cached);
747 gint cached_ellipsize = pango_layout_get_ellipsize (cached);
749 if (cached_width == width &&
750 cached_height == height &&
751 cached_ellipsize == ellipsize)
753 /* If this cached layout is using the same size then we can
754 * just return that directly
757 "ClutterText: %p: cache hit for size %.2fx%.2f",
762 CLUTTER_COUNTER_INC (_clutter_uprof_context,
763 text_cache_hit_counter);
765 return priv->cached_layouts[i].layout;
768 /* When getting the preferred height for a specific width,
769 * we might be able to reuse the layout from getting the
770 * preferred width. If the width that the layout gives
771 * unconstrained is less than the width that we are using
772 * than the height will be unaffected by that width.
774 if (allocation_height < 0 &&
775 cached_width == -1 &&
776 cached_ellipsize == ellipsize)
778 PangoRectangle logical_rect;
780 pango_layout_get_extents (priv->cached_layouts[i].layout,
784 if (logical_rect.width <= width)
786 /* We've been asked for our height for the width we gave as a result
787 * of a _get_preferred_width call
790 "ClutterText: %p: cache hit for size %.2fx%.2f "
791 "(unwrapped width narrower than given width)",
796 CLUTTER_COUNTER_INC (_clutter_uprof_context,
797 text_cache_hit_counter);
799 return priv->cached_layouts[i].layout;
803 if (!found_free_cache &&
804 (priv->cached_layouts[i].age < oldest_cache->age))
806 oldest_cache = priv->cached_layouts + i;
811 CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache miss for size %.2fx%.2f",
816 CLUTTER_COUNTER_INC (_clutter_uprof_context, text_cache_miss_counter);
818 /* If we make it here then we didn't have a cached version so we
819 need to recreate the layout */
820 if (oldest_cache->layout)
821 g_object_unref (oldest_cache->layout);
823 oldest_cache->layout =
824 clutter_text_create_layout_no_cache (text, width, height, ellipsize);
826 cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout);
828 /* Mark the 'time' this cache was created and advance the time */
829 oldest_cache->age = priv->cache_age++;
830 return oldest_cache->layout;
834 * clutter_text_coords_to_position:
835 * @self: a #ClutterText
836 * @x: the X coordinate, relative to the actor
837 * @y: the Y coordinate, relative to the actor
839 * Retrieves the position of the character at the given coordinates.
841 * Return: the position of the character
846 clutter_text_coords_to_position (ClutterText *self,
854 g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
856 /* Take any offset due to scrolling into account, and normalize
857 * the coordinates to PangoScale units
859 px = (x - self->priv->text_x) * PANGO_SCALE;
860 py = (y - self->priv->text_y) * PANGO_SCALE;
862 pango_layout_xy_to_index (clutter_text_get_layout (self),
866 return index_ + trailing;
870 * clutter_text_position_to_coords:
871 * @self: a #ClutterText
872 * @position: position in characters
873 * @x: (out): return location for the X coordinate, or %NULL
874 * @y: (out): return location for the Y coordinate, or %NULL
875 * @line_height: (out): return location for the line height, or %NULL
877 * Retrieves the coordinates of the given @position.
879 * Return value: %TRUE if the conversion was successful
884 clutter_text_position_to_coords (ClutterText *self,
890 ClutterTextPrivate *priv;
893 gint password_char_bytes = 1;
897 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
901 n_chars = clutter_text_buffer_get_length (get_buffer (self));
902 if (priv->preedit_set)
903 n_chars += priv->preedit_n_chars;
905 if (position < -1 || position > n_chars)
908 if (priv->password_char != 0)
909 password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL);
913 if (priv->password_char == 0)
915 n_bytes = clutter_text_buffer_get_bytes (get_buffer (self));
916 if (priv->editable && priv->preedit_set)
917 index_ = n_bytes + strlen (priv->preedit_str);
922 index_ = n_chars * password_char_bytes;
924 else if (position == 0)
930 gchar *text = clutter_text_get_display_text (self);
931 GString *tmp = g_string_new (text);
934 cursor_index = offset_to_bytes (text, priv->position);
936 if (priv->preedit_str != NULL)
937 g_string_insert (tmp, cursor_index, priv->preedit_str);
939 if (priv->password_char == 0)
940 index_ = offset_to_bytes (tmp->str, position);
942 index_ = position * password_char_bytes;
945 g_string_free (tmp, TRUE);
948 pango_layout_get_cursor_pos (clutter_text_get_layout (self),
954 *x = (gfloat) rect.x / 1024.0f;
956 /* Take any offset due to scrolling into account */
957 if (priv->single_line_mode)
962 *y = (gfloat) rect.y / 1024.0f;
965 *line_height = (gfloat) rect.height / 1024.0f;
971 clutter_text_ensure_cursor_position (ClutterText *self)
973 ClutterTextPrivate *priv = self->priv;
974 gfloat x, y, cursor_height;
975 ClutterGeometry cursor_pos = { 0, };
976 gboolean x_changed, y_changed;
977 gboolean width_changed, height_changed;
980 position = priv->position;
982 if (priv->editable && priv->preedit_set)
985 position = clutter_text_buffer_get_length (get_buffer (self));
986 position += priv->preedit_cursor_pos;
989 CLUTTER_NOTE (MISC, "Cursor at %d (preedit %s at pos: %d)",
991 priv->preedit_set ? "set" : "unset",
992 priv->preedit_set ? priv->preedit_cursor_pos : 0);
994 x = y = cursor_height = 0;
995 clutter_text_position_to_coords (self, position,
1000 cursor_pos.y = y + 2;
1001 cursor_pos.width = priv->cursor_size;
1002 cursor_pos.height = cursor_height - 4;
1004 x_changed = priv->cursor_pos.x != cursor_pos.x;
1005 y_changed = priv->cursor_pos.y != cursor_pos.y;
1006 width_changed = priv->cursor_pos.width != cursor_pos.width;
1007 height_changed = priv->cursor_pos.height != cursor_pos.height;
1009 if (x_changed || y_changed || width_changed || height_changed)
1011 priv->cursor_pos = cursor_pos;
1013 g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos);
1018 * clutter_text_delete_selection:
1019 * @self: a #ClutterText
1021 * Deletes the currently selected text
1023 * This function is only useful in subclasses of #ClutterText
1025 * Return value: %TRUE if text was deleted or if the text actor
1026 * is empty, and %FALSE otherwise
1031 clutter_text_delete_selection (ClutterText *self)
1033 ClutterTextPrivate *priv;
1036 gint old_position, old_selection;
1039 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
1043 n_chars = clutter_text_buffer_get_length (get_buffer (self));
1047 start_index = priv->position == -1 ? n_chars : priv->position;
1048 end_index = priv->selection_bound == -1 ? n_chars : priv->selection_bound;
1050 if (end_index == start_index)
1053 if (end_index < start_index)
1055 gint temp = start_index;
1056 start_index = end_index;
1060 old_position = priv->position;
1061 old_selection = priv->selection_bound;
1063 clutter_text_delete_text (self, start_index, end_index);
1065 priv->position = start_index;
1066 priv->selection_bound = start_index;
1068 /* Not required to be guarded by g_object_freeze/thaw_notify */
1069 if (priv->position != old_position)
1070 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]);
1072 if (priv->selection_bound != old_selection)
1073 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
1079 * Utility function to update both cursor position and selection bound
1083 clutter_text_set_positions (ClutterText *self,
1087 g_object_freeze_notify (G_OBJECT (self));
1088 clutter_text_set_cursor_position (self, new_pos);
1089 clutter_text_set_selection_bound (self, new_bound);
1090 g_object_thaw_notify (G_OBJECT (self));
1094 clutter_text_set_markup_internal (ClutterText *self,
1097 ClutterTextPrivate *priv = self->priv;
1100 PangoAttrList *attrs = NULL;
1103 g_assert (str != NULL);
1106 res = pango_parse_markup (str, -1, 0,
1114 if (G_LIKELY (error != NULL))
1116 g_warning ("Failed to set the markup of the actor '%s': %s",
1117 _clutter_actor_get_debug_name (CLUTTER_ACTOR (self)),
1119 g_error_free (error);
1122 g_warning ("Failed to set the markup of the actor '%s'",
1123 _clutter_actor_get_debug_name (CLUTTER_ACTOR (self)));
1130 clutter_text_buffer_set_text (get_buffer (self), text, -1);
1134 /* Store the new markup attributes */
1135 if (priv->markup_attrs != NULL)
1136 pango_attr_list_unref (priv->markup_attrs);
1138 priv->markup_attrs = attrs;
1140 /* Clear the effective attributes so they will be regenerated when a
1141 layout is created */
1142 if (priv->effective_attrs != NULL)
1144 pango_attr_list_unref (priv->effective_attrs);
1145 priv->effective_attrs = NULL;
1150 clutter_text_set_property (GObject *gobject,
1152 const GValue *value,
1155 ClutterText *self = CLUTTER_TEXT (gobject);
1160 clutter_text_set_buffer (self, g_value_get_object (value));
1165 const char *str = g_value_get_string (value);
1166 if (self->priv->use_markup)
1167 clutter_text_set_markup_internal (self, str ? str : "");
1169 clutter_text_buffer_set_text (get_buffer (self), str ? str : "", -1);
1174 clutter_text_set_color (self, clutter_value_get_color (value));
1177 case PROP_FONT_NAME:
1178 clutter_text_set_font_name (self, g_value_get_string (value));
1181 case PROP_FONT_DESCRIPTION:
1182 clutter_text_set_font_description (self, g_value_get_boxed (value));
1185 case PROP_USE_MARKUP:
1186 clutter_text_set_use_markup (self, g_value_get_boolean (value));
1189 case PROP_ATTRIBUTES:
1190 clutter_text_set_attributes (self, g_value_get_boxed (value));
1193 case PROP_LINE_ALIGNMENT:
1194 clutter_text_set_line_alignment (self, g_value_get_enum (value));
1197 case PROP_LINE_WRAP:
1198 clutter_text_set_line_wrap (self, g_value_get_boolean (value));
1201 case PROP_LINE_WRAP_MODE:
1202 clutter_text_set_line_wrap_mode (self, g_value_get_enum (value));
1206 clutter_text_set_justify (self, g_value_get_boolean (value));
1209 case PROP_ELLIPSIZE:
1210 clutter_text_set_ellipsize (self, g_value_get_enum (value));
1214 clutter_text_set_cursor_position (self, g_value_get_int (value));
1217 case PROP_SELECTION_BOUND:
1218 clutter_text_set_selection_bound (self, g_value_get_int (value));
1221 case PROP_SELECTION_COLOR:
1222 clutter_text_set_selection_color (self, g_value_get_boxed (value));
1225 case PROP_CURSOR_VISIBLE:
1226 clutter_text_set_cursor_visible (self, g_value_get_boolean (value));
1229 case PROP_CURSOR_COLOR:
1230 clutter_text_set_cursor_color (self, g_value_get_boxed (value));
1233 case PROP_CURSOR_SIZE:
1234 clutter_text_set_cursor_size (self, g_value_get_int (value));
1238 clutter_text_set_editable (self, g_value_get_boolean (value));
1241 case PROP_ACTIVATABLE:
1242 clutter_text_set_activatable (self, g_value_get_boolean (value));
1245 case PROP_SELECTABLE:
1246 clutter_text_set_selectable (self, g_value_get_boolean (value));
1249 case PROP_PASSWORD_CHAR:
1250 clutter_text_set_password_char (self, g_value_get_uint (value));
1253 case PROP_MAX_LENGTH:
1254 clutter_text_set_max_length (self, g_value_get_int (value));
1257 case PROP_SINGLE_LINE_MODE:
1258 clutter_text_set_single_line_mode (self, g_value_get_boolean (value));
1261 case PROP_SELECTED_TEXT_COLOR:
1262 clutter_text_set_selected_text_color (self, clutter_value_get_color (value));
1266 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1271 clutter_text_get_property (GObject *gobject,
1276 ClutterText *self = CLUTTER_TEXT (gobject);
1277 ClutterTextPrivate *priv = self->priv;
1282 g_value_set_object (value, clutter_text_get_buffer (self));
1286 g_value_set_string (value, clutter_text_buffer_get_text (get_buffer (self)));
1289 case PROP_FONT_NAME:
1290 g_value_set_string (value, priv->font_name);
1293 case PROP_FONT_DESCRIPTION:
1294 g_value_set_boxed (value, priv->font_desc);
1297 case PROP_USE_MARKUP:
1298 g_value_set_boolean (value, priv->use_markup);
1302 clutter_value_set_color (value, &priv->text_color);
1305 case PROP_CURSOR_VISIBLE:
1306 g_value_set_boolean (value, priv->cursor_visible);
1309 case PROP_CURSOR_COLOR:
1310 clutter_value_set_color (value, &priv->cursor_color);
1313 case PROP_CURSOR_COLOR_SET:
1314 g_value_set_boolean (value, priv->cursor_color_set);
1317 case PROP_CURSOR_SIZE:
1318 g_value_set_int (value, priv->cursor_size);
1322 g_value_set_int (value, priv->position);
1325 case PROP_SELECTION_BOUND:
1326 g_value_set_int (value, priv->selection_bound);
1330 g_value_set_boolean (value, priv->editable);
1333 case PROP_SELECTABLE:
1334 g_value_set_boolean (value, priv->selectable);
1337 case PROP_SELECTION_COLOR:
1338 clutter_value_set_color (value, &priv->selection_color);
1341 case PROP_SELECTION_COLOR_SET:
1342 g_value_set_boolean (value, priv->selection_color_set);
1345 case PROP_ACTIVATABLE:
1346 g_value_set_boolean (value, priv->activatable);
1349 case PROP_PASSWORD_CHAR:
1350 g_value_set_uint (value, priv->password_char);
1353 case PROP_MAX_LENGTH:
1354 g_value_set_int (value, clutter_text_buffer_get_max_length (get_buffer (self)));
1357 case PROP_SINGLE_LINE_MODE:
1358 g_value_set_boolean (value, priv->single_line_mode);
1361 case PROP_ELLIPSIZE:
1362 g_value_set_enum (value, priv->ellipsize);
1365 case PROP_LINE_WRAP:
1366 g_value_set_boolean (value, priv->wrap);
1369 case PROP_LINE_WRAP_MODE:
1370 g_value_set_enum (value, priv->wrap_mode);
1373 case PROP_LINE_ALIGNMENT:
1374 g_value_set_enum (value, priv->alignment);
1378 g_value_set_boolean (value, priv->justify);
1381 case PROP_ATTRIBUTES:
1382 g_value_set_boxed (value, priv->attrs);
1385 case PROP_SELECTED_TEXT_COLOR:
1386 clutter_value_set_color (value, &priv->selected_text_color);
1389 case PROP_SELECTED_TEXT_COLOR_SET:
1390 g_value_set_boolean (value, priv->selected_text_color_set);
1394 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1399 clutter_text_dispose (GObject *gobject)
1401 ClutterText *self = CLUTTER_TEXT (gobject);
1402 ClutterTextPrivate *priv = self->priv;
1404 /* get rid of the entire cache */
1405 clutter_text_dirty_cache (self);
1407 if (priv->direction_changed_id)
1409 g_signal_handler_disconnect (self, priv->direction_changed_id);
1410 priv->direction_changed_id = 0;
1413 if (priv->settings_changed_id)
1415 g_signal_handler_disconnect (clutter_get_default_backend (),
1416 priv->settings_changed_id);
1417 priv->settings_changed_id = 0;
1420 if (priv->password_hint_id)
1422 g_source_remove (priv->password_hint_id);
1423 priv->password_hint_id = 0;
1426 clutter_text_set_buffer (self, NULL);
1428 G_OBJECT_CLASS (clutter_text_parent_class)->dispose (gobject);
1432 clutter_text_finalize (GObject *gobject)
1434 ClutterText *self = CLUTTER_TEXT (gobject);
1435 ClutterTextPrivate *priv = self->priv;
1437 if (priv->font_desc)
1438 pango_font_description_free (priv->font_desc);
1441 pango_attr_list_unref (priv->attrs);
1442 if (priv->markup_attrs)
1443 pango_attr_list_unref (priv->markup_attrs);
1444 if (priv->effective_attrs)
1445 pango_attr_list_unref (priv->effective_attrs);
1446 if (priv->preedit_attrs)
1447 pango_attr_list_unref (priv->preedit_attrs);
1449 clutter_text_dirty_paint_volume (self);
1451 clutter_text_set_buffer (self, NULL);
1452 g_free (priv->font_name);
1454 G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject);
1457 typedef void (* ClutterTextSelectionFunc) (ClutterText *text,
1458 const ClutterActorBox *box,
1459 gpointer user_data);
1462 clutter_text_foreach_selection_rectangle (ClutterText *self,
1463 ClutterTextSelectionFunc func,
1466 ClutterTextPrivate *priv = self->priv;
1467 PangoLayout *layout = clutter_text_get_layout (self);
1468 gchar *utf8 = clutter_text_get_display_text (self);
1474 if (priv->position == 0)
1477 start_index = offset_to_bytes (utf8, priv->position);
1479 if (priv->selection_bound == 0)
1482 end_index = offset_to_bytes (utf8, priv->selection_bound);
1484 if (start_index > end_index)
1486 gint temp = start_index;
1487 start_index = end_index;
1491 lines = pango_layout_get_line_count (layout);
1493 for (line_no = 0; line_no < lines; line_no++)
1495 PangoLayoutLine *line;
1501 ClutterActorBox box;
1504 line = pango_layout_get_line_readonly (layout, line_no);
1505 pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL);
1506 if (maxindex < start_index)
1509 pango_layout_line_get_x_ranges (line, start_index, end_index,
1512 pango_layout_line_x_to_index (line, 0, &index_, NULL);
1514 clutter_text_position_to_coords (self,
1515 bytes_to_offset (utf8, index_),
1519 box.y2 = y + height;
1521 for (i = 0; i < n_ranges; i++)
1526 range_x = ranges[i * 2] / PANGO_SCALE;
1528 /* Account for any scrolling in single line mode */
1529 if (priv->single_line_mode)
1530 range_x += priv->text_x;
1533 range_width = ((gfloat) ranges[i * 2 + 1] - (gfloat) ranges[i * 2])
1537 box.x2 = ceilf (range_x + range_width + .5f);
1539 func (self, &box, user_data);
1549 add_selection_rectangle_to_path (ClutterText *text,
1550 const ClutterActorBox *box,
1553 cogl_path_rectangle (user_data, box->x1, box->y1, box->x2, box->y2);
1556 /* Draws the selected text, its background, and the cursor */
1558 selection_paint (ClutterText *self)
1560 ClutterTextPrivate *priv = self->priv;
1561 ClutterActor *actor = CLUTTER_ACTOR (self);
1562 guint8 paint_opacity = clutter_actor_get_paint_opacity (actor);
1564 if (!priv->has_focus)
1567 if (priv->editable && priv->cursor_visible)
1569 const ClutterColor *color;
1572 position = priv->position;
1574 if (position == priv->selection_bound)
1576 /* No selection, just draw the cursor */
1577 if (priv->cursor_color_set)
1578 color = &priv->cursor_color;
1580 color = &priv->text_color;
1582 cogl_set_source_color4ub (color->red,
1589 cogl_rectangle (priv->cursor_pos.x,
1591 priv->cursor_pos.x + priv->cursor_pos.width,
1592 priv->cursor_pos.y + priv->cursor_pos.height);
1596 /* Paint selection background first */
1597 PangoLayout *layout = clutter_text_get_layout (self);
1598 CoglPath *selection_path = cogl_path_new ();
1599 CoglColor cogl_color = { 0, };
1601 /* Paint selection background */
1602 if (priv->selection_color_set)
1603 color = &priv->selection_color;
1604 else if (priv->cursor_color_set)
1605 color = &priv->cursor_color;
1607 color = &priv->text_color;
1609 cogl_set_source_color4ub (color->red,
1612 paint_opacity * color->alpha / 255);
1614 clutter_text_foreach_selection_rectangle (self,
1615 add_selection_rectangle_to_path,
1618 cogl_path_fill (selection_path);
1620 /* Paint selected text */
1621 cogl_framebuffer_push_path_clip (cogl_get_draw_framebuffer (),
1623 cogl_object_unref (selection_path);
1625 if (priv->selected_text_color_set)
1626 color = &priv->selected_text_color;
1628 color = &priv->text_color;
1630 cogl_color_init_from_4ub (&cogl_color,
1634 paint_opacity * color->alpha / 255);
1636 cogl_pango_render_layout (layout, priv->text_x, 0, &cogl_color, 0);
1644 clutter_text_move_word_backward (ClutterText *self,
1647 gint retval = start;
1649 if (clutter_text_buffer_get_length (get_buffer (self)) > 0 && start > 0)
1651 PangoLayout *layout = clutter_text_get_layout (self);
1652 PangoLogAttr *log_attrs = NULL;
1655 pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
1658 while (retval > 0 && !log_attrs[retval].is_word_start)
1668 clutter_text_move_word_forward (ClutterText *self,
1671 gint retval = start;
1674 n_chars = clutter_text_buffer_get_length (get_buffer (self));
1675 if (n_chars > 0 && start < n_chars)
1677 PangoLayout *layout = clutter_text_get_layout (self);
1678 PangoLogAttr *log_attrs = NULL;
1681 pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
1684 while (retval < n_chars && !log_attrs[retval].is_word_end)
1694 clutter_text_move_line_start (ClutterText *self,
1697 PangoLayoutLine *layout_line;
1698 PangoLayout *layout;
1704 layout = clutter_text_get_layout (self);
1705 text = clutter_text_buffer_get_text (get_buffer (self));
1710 index_ = offset_to_bytes (text, start);
1712 pango_layout_index_to_line_x (layout, index_,
1716 layout_line = pango_layout_get_line_readonly (layout, line_no);
1720 pango_layout_line_x_to_index (layout_line, 0, &index_, NULL);
1722 position = bytes_to_offset (text, index_);
1728 clutter_text_move_line_end (ClutterText *self,
1731 ClutterTextPrivate *priv = self->priv;
1732 PangoLayoutLine *layout_line;
1733 PangoLayout *layout;
1740 layout = clutter_text_get_layout (self);
1741 text = clutter_text_buffer_get_text (get_buffer (self));
1746 index_ = offset_to_bytes (text, priv->position);
1748 pango_layout_index_to_line_x (layout, index_,
1752 layout_line = pango_layout_get_line_readonly (layout, line_no);
1756 pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing);
1759 position = bytes_to_offset (text, index_);
1765 clutter_text_select_word (ClutterText *self)
1767 gint cursor_pos = self->priv->position;
1768 gint start_pos, end_pos;
1770 start_pos = clutter_text_move_word_backward (self, cursor_pos);
1771 end_pos = clutter_text_move_word_forward (self, cursor_pos);
1773 clutter_text_set_selection (self, start_pos, end_pos);
1777 clutter_text_select_line (ClutterText *self)
1779 ClutterTextPrivate *priv = self->priv;
1780 gint cursor_pos = priv->position;
1781 gint start_pos, end_pos;
1783 if (priv->single_line_mode)
1790 start_pos = clutter_text_move_line_start (self, cursor_pos);
1791 end_pos = clutter_text_move_line_end (self, cursor_pos);
1794 clutter_text_set_selection (self, start_pos, end_pos);
1798 clutter_text_button_press (ClutterActor *actor,
1799 ClutterButtonEvent *event)
1801 ClutterText *self = CLUTTER_TEXT (actor);
1802 ClutterTextPrivate *priv = self->priv;
1803 gboolean res = FALSE;
1807 /* we'll steal keyfocus if we need it */
1808 if (priv->editable || priv->selectable)
1809 clutter_actor_grab_key_focus (actor);
1811 return CLUTTER_EVENT_PROPAGATE;
1813 /* if the actor is empty we just reset everything and not
1814 * set up the dragging of the selection since there's nothing
1817 if (clutter_text_buffer_get_length (get_buffer (self)) == 0)
1819 clutter_text_set_positions (self, -1, -1);
1821 return CLUTTER_EVENT_STOP;
1824 res = clutter_actor_transform_stage_point (actor,
1833 index_ = clutter_text_coords_to_position (self, x, y);
1834 text = clutter_text_buffer_get_text (get_buffer (self));
1835 offset = bytes_to_offset (text, index_);
1837 /* what we select depends on the number of button clicks we
1840 * 1: just position the cursor and the selection
1841 * 2: select the current word
1842 * 3: select the contents of the whole actor
1844 if (event->click_count == 1)
1846 clutter_text_set_positions (self, offset, offset);
1848 else if (event->click_count == 2)
1850 clutter_text_select_word (self);
1852 else if (event->click_count == 3)
1854 clutter_text_select_line (self);
1858 /* grab the pointer */
1859 priv->in_select_drag = TRUE;
1860 clutter_grab_pointer (actor);
1862 return CLUTTER_EVENT_STOP;
1866 clutter_text_motion (ClutterActor *actor,
1867 ClutterMotionEvent *mev)
1869 ClutterText *self = CLUTTER_TEXT (actor);
1870 ClutterTextPrivate *priv = self->priv;
1872 gint index_, offset;
1876 if (!priv->in_select_drag)
1877 return CLUTTER_EVENT_PROPAGATE;
1879 res = clutter_actor_transform_stage_point (actor,
1883 return CLUTTER_EVENT_PROPAGATE;
1885 index_ = clutter_text_coords_to_position (self, x, y);
1886 text = clutter_text_buffer_get_text (get_buffer (self));
1887 offset = bytes_to_offset (text, index_);
1889 if (priv->selectable)
1890 clutter_text_set_cursor_position (self, offset);
1892 clutter_text_set_positions (self, offset, offset);
1894 return CLUTTER_EVENT_STOP;
1898 clutter_text_button_release (ClutterActor *actor,
1899 ClutterButtonEvent *bev)
1901 ClutterText *self = CLUTTER_TEXT (actor);
1902 ClutterTextPrivate *priv = self->priv;
1904 if (priv->in_select_drag)
1906 clutter_ungrab_pointer ();
1907 priv->in_select_drag = FALSE;
1909 return CLUTTER_EVENT_STOP;
1912 return CLUTTER_EVENT_PROPAGATE;
1916 clutter_text_remove_password_hint (gpointer data)
1918 ClutterText *self = data;
1920 self->priv->password_hint_visible = FALSE;
1921 self->priv->password_hint_id = 0;
1923 clutter_text_dirty_cache (data);
1924 clutter_text_queue_redraw (data);
1926 return G_SOURCE_REMOVE;
1930 clutter_text_key_press (ClutterActor *actor,
1931 ClutterKeyEvent *event)
1933 ClutterText *self = CLUTTER_TEXT (actor);
1934 ClutterTextPrivate *priv = self->priv;
1935 ClutterBindingPool *pool;
1938 if (!priv->editable)
1939 return CLUTTER_EVENT_PROPAGATE;
1941 /* we need to use the ClutterText type name to find our own
1942 * key bindings; subclasses will override or chain up this
1943 * event handler, so they can do whatever they want there
1945 pool = clutter_binding_pool_find (g_type_name (CLUTTER_TYPE_TEXT));
1946 g_assert (pool != NULL);
1948 /* we allow passing synthetic events that only contain
1949 * the Unicode value and not the key symbol
1951 if (event->keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC))
1954 res = clutter_binding_pool_activate (pool, event->keyval,
1955 event->modifier_state,
1958 /* if the key binding has handled the event we bail out
1959 * as fast as we can; otherwise, we try to insert the
1960 * Unicode character inside the key event into the text
1964 return CLUTTER_EVENT_STOP;
1965 else if ((event->modifier_state & CLUTTER_CONTROL_MASK) == 0)
1967 gunichar key_unichar;
1969 /* Skip keys when control is pressed */
1970 key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) event);
1972 /* return is reported as CR, but we want LF */
1973 if (key_unichar == '\r')
1976 if (key_unichar == '\n' ||
1977 (g_unichar_validate (key_unichar) &&
1978 !g_unichar_iscntrl (key_unichar)))
1980 /* truncate the eventual selection so that the
1981 * Unicode character can replace it
1983 clutter_text_delete_selection (self);
1984 clutter_text_insert_unichar (self, key_unichar);
1986 if (priv->show_password_hint)
1988 if (priv->password_hint_id != 0)
1989 g_source_remove (priv->password_hint_id);
1991 priv->password_hint_visible = TRUE;
1992 priv->password_hint_id =
1993 clutter_threads_add_timeout (priv->password_hint_timeout,
1994 clutter_text_remove_password_hint,
1998 return CLUTTER_EVENT_STOP;
2002 return CLUTTER_EVENT_PROPAGATE;
2006 clutter_text_compute_layout_offsets (ClutterText *self,
2007 PangoLayout *layout,
2008 const ClutterActorBox *alloc,
2012 ClutterActor *actor = CLUTTER_ACTOR (self);
2013 ClutterActorAlign x_align, y_align;
2014 PangoRectangle logical_rect;
2015 float alloc_width, alloc_height;
2018 clutter_actor_box_get_size (alloc, &alloc_width, &alloc_height);
2019 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2021 if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_HORIZONTAL))
2022 x_align = _clutter_actor_get_effective_x_align (actor);
2024 x_align = CLUTTER_ACTOR_ALIGN_FILL;
2026 if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_VERTICAL))
2027 y_align = clutter_actor_get_y_align (actor);
2029 y_align = CLUTTER_ACTOR_ALIGN_FILL;
2034 case CLUTTER_ACTOR_ALIGN_FILL:
2035 case CLUTTER_ACTOR_ALIGN_START:
2038 case CLUTTER_ACTOR_ALIGN_END:
2039 if (alloc_width > logical_rect.width)
2040 x = alloc_width - logical_rect.width;
2043 case CLUTTER_ACTOR_ALIGN_CENTER:
2044 if (alloc_width > logical_rect.width)
2045 x = (alloc_width - logical_rect.width) / 2.f;
2052 case CLUTTER_ACTOR_ALIGN_FILL:
2053 case CLUTTER_ACTOR_ALIGN_START:
2056 case CLUTTER_ACTOR_ALIGN_END:
2057 if (alloc_height > logical_rect.height)
2058 y = alloc_height - logical_rect.height;
2061 case CLUTTER_ACTOR_ALIGN_CENTER:
2062 if (alloc_height > logical_rect.height)
2063 y = (alloc_height - logical_rect.height) / 2.f;
2068 *text_x = floorf (x);
2071 *text_y = floorf (y);
2074 #define TEXT_PADDING 2
2077 clutter_text_paint (ClutterActor *self)
2079 ClutterText *text = CLUTTER_TEXT (self);
2080 ClutterTextPrivate *priv = text->priv;
2081 PangoLayout *layout;
2082 ClutterActorBox alloc = { 0, };
2083 CoglColor color = { 0, };
2084 guint8 real_opacity;
2085 gint text_x = priv->text_x;
2086 gint text_y = priv->text_y;
2087 gboolean clip_set = FALSE;
2088 gboolean bg_color_set = FALSE;
2091 /* Note that if anything in this paint method changes it needs to be
2092 reflected in the get_paint_volume implementation which is tightly
2093 tied to the workings of this function */
2094 n_chars = clutter_text_buffer_get_length (get_buffer (text));
2096 /* don't bother painting an empty text actor, unless it's
2097 * editable, in which case we want to paint at least the
2100 if (n_chars == 0 && (!priv->editable || !priv->cursor_visible))
2103 clutter_actor_get_allocation_box (self, &alloc);
2105 g_object_get (self, "background-color-set", &bg_color_set, NULL);
2108 ClutterColor bg_color;
2110 clutter_actor_get_background_color (self, &bg_color);
2111 bg_color.alpha = clutter_actor_get_paint_opacity (self)
2115 cogl_set_source_color4ub (bg_color.red,
2119 cogl_rectangle (0, 0, alloc.x2 - alloc.x1, alloc.y2 - alloc.y1);
2122 if (priv->editable && priv->single_line_mode)
2123 layout = clutter_text_create_layout (text, -1, -1);
2126 /* the only time when we create the PangoLayout using the full
2127 * width and height of the allocation is when we can both wrap
2130 if (priv->wrap && priv->ellipsize)
2132 layout = clutter_text_create_layout (text,
2133 alloc.x2 - alloc.x1,
2134 alloc.y2 - alloc.y1);
2138 /* if we're not wrapping we cannot set the height of the
2139 * layout, otherwise Pango will happily wrap the text to
2140 * fit in the rectangle - thus making the :wrap property
2145 * http://bugzilla.clutter-project.org/show_bug.cgi?id=2339
2147 * in order to fix this, we create a layout that would fit
2148 * in the assigned width, then we clip the actor if the
2149 * logical rectangle overflows the allocation.
2151 layout = clutter_text_create_layout (text,
2152 alloc.x2 - alloc.x1,
2157 if (priv->editable && priv->cursor_visible)
2158 clutter_text_ensure_cursor_position (text);
2160 if (priv->editable && priv->single_line_mode)
2162 PangoRectangle logical_rect = { 0, };
2163 gint actor_width, text_width;
2165 pango_layout_get_extents (layout, NULL, &logical_rect);
2167 cogl_clip_push_rectangle (0, 0,
2168 (alloc.x2 - alloc.x1),
2169 (alloc.y2 - alloc.y1));
2172 actor_width = (alloc.x2 - alloc.x1)
2174 text_width = logical_rect.width / PANGO_SCALE;
2176 if (actor_width < text_width)
2178 gint cursor_x = priv->cursor_pos.x;
2180 if (priv->position == -1)
2182 text_x = actor_width - text_width;
2184 else if (priv->position == 0)
2186 text_x = TEXT_PADDING;
2192 text_x = text_x - cursor_x - TEXT_PADDING;
2194 else if (cursor_x > actor_width)
2196 text_x = text_x + (actor_width - cursor_x) - TEXT_PADDING;
2202 text_x = TEXT_PADDING;
2205 else if (!priv->editable && !(priv->wrap && priv->ellipsize))
2207 PangoRectangle logical_rect = { 0, };
2209 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2211 /* don't clip if the layout managed to fit inside our allocation */
2212 if (logical_rect.width > (alloc.x2 - alloc.x1) ||
2213 logical_rect.height > (alloc.y2 - alloc.y1))
2215 cogl_clip_push_rectangle (0, 0,
2216 alloc.x2 - alloc.x1,
2217 alloc.y2 - alloc.y1);
2221 clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y);
2224 clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y);
2226 if (priv->text_x != text_x ||
2227 priv->text_y != text_y)
2229 priv->text_x = text_x;
2230 priv->text_y = text_y;
2232 clutter_text_ensure_cursor_position (text);
2235 real_opacity = clutter_actor_get_paint_opacity (self)
2236 * priv->text_color.alpha
2239 CLUTTER_NOTE (PAINT, "painting text (text: '%s')",
2240 clutter_text_buffer_get_text (get_buffer (text)));
2242 cogl_color_init_from_4ub (&color,
2243 priv->text_color.red,
2244 priv->text_color.green,
2245 priv->text_color.blue,
2247 cogl_pango_render_layout (layout, priv->text_x, priv->text_y, &color, 0);
2249 selection_paint (text);
2256 add_selection_to_paint_volume (ClutterText *text,
2257 const ClutterActorBox *box,
2260 ClutterPaintVolume *total_volume = user_data;
2261 ClutterPaintVolume rect_volume;
2262 ClutterVertex vertex;
2264 _clutter_paint_volume_init_static (&rect_volume, CLUTTER_ACTOR (text));
2269 clutter_paint_volume_set_origin (&rect_volume, &vertex);
2270 clutter_paint_volume_set_width (&rect_volume, box->x2 - box->x1);
2271 clutter_paint_volume_set_height (&rect_volume, box->y2 - box->y1);
2273 clutter_paint_volume_union (total_volume, &rect_volume);
2275 clutter_paint_volume_free (&rect_volume);
2279 clutter_text_get_paint_volume_for_cursor (ClutterText *text,
2280 ClutterPaintVolume *volume)
2282 ClutterTextPrivate *priv = text->priv;
2283 ClutterVertex origin;
2285 clutter_text_ensure_cursor_position (text);
2287 if (priv->position == priv->selection_bound)
2289 origin.x = priv->cursor_pos.x;
2290 origin.y = priv->cursor_pos.y;
2292 clutter_paint_volume_set_origin (volume, &origin);
2293 clutter_paint_volume_set_width (volume, priv->cursor_pos.width);
2294 clutter_paint_volume_set_height (volume, priv->cursor_pos.height);
2298 clutter_text_foreach_selection_rectangle (text,
2299 add_selection_to_paint_volume,
2305 clutter_text_get_paint_volume (ClutterActor *self,
2306 ClutterPaintVolume *volume)
2308 ClutterText *text = CLUTTER_TEXT (self);
2309 ClutterTextPrivate *priv = text->priv;
2311 /* ClutterText uses the logical layout as the natural size of the
2312 actor. This means that it can sometimes paint outside of its
2313 allocation for example with italic fonts with serifs. Therefore
2314 we should use the ink rectangle of the layout instead */
2316 if (!priv->paint_volume_valid)
2318 PangoLayout *layout;
2319 PangoRectangle ink_rect;
2320 ClutterVertex origin;
2322 /* If the text is single line editable then it gets clipped to
2323 the allocation anyway so we can just use that */
2324 if (priv->editable && priv->single_line_mode)
2325 return _clutter_actor_set_default_paint_volume (self,
2329 if (G_OBJECT_TYPE (self) != CLUTTER_TYPE_TEXT)
2332 if (!clutter_actor_has_allocation (self))
2335 _clutter_paint_volume_init_static (&priv->paint_volume, self);
2337 layout = clutter_text_get_layout (text);
2338 pango_layout_get_extents (layout, &ink_rect, NULL);
2340 origin.x = ink_rect.x / (float) PANGO_SCALE;
2341 origin.y = ink_rect.y / (float) PANGO_SCALE;
2343 clutter_paint_volume_set_origin (&priv->paint_volume, &origin);
2344 clutter_paint_volume_set_width (&priv->paint_volume,
2345 ink_rect.width / (float) PANGO_SCALE);
2346 clutter_paint_volume_set_height (&priv->paint_volume,
2347 ink_rect.height / (float) PANGO_SCALE);
2349 /* If the cursor is visible then that will likely be drawn
2350 outside of the ink rectangle so we should merge that in */
2351 if (priv->editable && priv->cursor_visible && priv->has_focus)
2353 ClutterPaintVolume cursor_paint_volume;
2355 _clutter_paint_volume_init_static (&cursor_paint_volume,
2358 clutter_text_get_paint_volume_for_cursor (text, &cursor_paint_volume);
2360 clutter_paint_volume_union (&priv->paint_volume,
2361 &cursor_paint_volume);
2363 clutter_paint_volume_free (&cursor_paint_volume);
2366 priv->paint_volume_valid = TRUE;
2369 _clutter_paint_volume_copy_static (&priv->paint_volume, volume);
2375 clutter_text_get_preferred_width (ClutterActor *self,
2377 gfloat *min_width_p,
2378 gfloat *natural_width_p)
2380 ClutterText *text = CLUTTER_TEXT (self);
2381 ClutterTextPrivate *priv = text->priv;
2382 PangoRectangle logical_rect = { 0, };
2383 PangoLayout *layout;
2385 gfloat layout_width;
2387 layout = clutter_text_create_layout (text, -1, -1);
2389 pango_layout_get_extents (layout, NULL, &logical_rect);
2391 /* the X coordinate of the logical rectangle might be non-zero
2392 * according to the Pango documentation; hence, we need to offset
2393 * the width accordingly
2395 logical_width = logical_rect.x + logical_rect.width;
2397 layout_width = logical_width > 0
2398 ? ceilf (logical_width / 1024.0f)
2403 if (priv->wrap || priv->ellipsize || priv->editable)
2406 *min_width_p = layout_width;
2409 if (natural_width_p)
2411 if (priv->editable && priv->single_line_mode)
2412 *natural_width_p = layout_width + TEXT_PADDING * 2;
2414 *natural_width_p = layout_width;
2419 clutter_text_get_preferred_height (ClutterActor *self,
2421 gfloat *min_height_p,
2422 gfloat *natural_height_p)
2424 ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
2431 if (natural_height_p)
2432 *natural_height_p = 0;
2436 PangoLayout *layout;
2437 PangoRectangle logical_rect = { 0, };
2438 gint logical_height;
2439 gfloat layout_height;
2441 if (priv->single_line_mode)
2444 layout = clutter_text_create_layout (CLUTTER_TEXT (self),
2447 pango_layout_get_extents (layout, NULL, &logical_rect);
2449 /* the Y coordinate of the logical rectangle might be non-zero
2450 * according to the Pango documentation; hence, we need to offset
2451 * the height accordingly
2453 logical_height = logical_rect.y + logical_rect.height;
2454 layout_height = ceilf (logical_height / 1024.0f);
2458 /* if we wrap and ellipsize then the minimum height is
2459 * going to be at least the size of the first line
2461 if ((priv->ellipsize && priv->wrap) && !priv->single_line_mode)
2463 PangoLayoutLine *line;
2466 line = pango_layout_get_line_readonly (layout, 0);
2467 pango_layout_line_get_extents (line, NULL, &logical_rect);
2469 logical_height = logical_rect.y + logical_rect.height;
2470 line_height = ceilf (logical_height / 1024.0f);
2472 *min_height_p = line_height;
2475 *min_height_p = layout_height;
2478 if (natural_height_p)
2479 *natural_height_p = layout_height;
2484 clutter_text_allocate (ClutterActor *self,
2485 const ClutterActorBox *box,
2486 ClutterAllocationFlags flags)
2488 ClutterText *text = CLUTTER_TEXT (self);
2489 ClutterActorClass *parent_class;
2491 /* Ensure that there is a cached layout with the right width so
2492 * that we don't need to create the text during the paint run
2494 * if the Text is editable and in single line mode we don't want
2495 * to have any limit on the layout size, since the paint will clip
2496 * it to the allocation of the actor
2498 if (text->priv->editable && text->priv->single_line_mode)
2499 clutter_text_create_layout (text, -1, -1);
2501 clutter_text_create_layout (text,
2505 parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class);
2506 parent_class->allocate (self, box, flags);
2510 clutter_text_has_overlaps (ClutterActor *self)
2512 ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
2514 return priv->editable ||
2516 priv->cursor_visible;
2520 clutter_text_key_focus_in (ClutterActor *actor)
2522 ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
2524 priv->has_focus = TRUE;
2526 clutter_text_queue_redraw (actor);
2530 clutter_text_key_focus_out (ClutterActor *actor)
2532 ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
2534 priv->has_focus = FALSE;
2536 clutter_text_queue_redraw (actor);
2540 clutter_text_real_move_left (ClutterText *self,
2541 const gchar *action,
2543 ClutterModifierType modifiers)
2545 ClutterTextPrivate *priv = self->priv;
2546 gint pos = priv->position;
2550 len = clutter_text_buffer_get_length (get_buffer (self));
2552 g_object_freeze_notify (G_OBJECT (self));
2554 if (pos != 0 && len != 0)
2556 if (modifiers & CLUTTER_CONTROL_MASK)
2559 new_pos = clutter_text_move_word_backward (self, len);
2561 new_pos = clutter_text_move_word_backward (self, pos);
2571 clutter_text_set_cursor_position (self, new_pos);
2574 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2575 clutter_text_clear_selection (self);
2577 g_object_thaw_notify (G_OBJECT (self));
2583 clutter_text_real_move_right (ClutterText *self,
2584 const gchar *action,
2586 ClutterModifierType modifiers)
2588 ClutterTextPrivate *priv = self->priv;
2589 gint pos = priv->position;
2590 gint len = clutter_text_buffer_get_length (get_buffer (self));
2593 g_object_freeze_notify (G_OBJECT (self));
2595 if (pos != -1 && len !=0)
2597 if (modifiers & CLUTTER_CONTROL_MASK)
2600 new_pos = clutter_text_move_word_forward (self, pos);
2608 clutter_text_set_cursor_position (self, new_pos);
2611 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2612 clutter_text_clear_selection (self);
2614 g_object_thaw_notify (G_OBJECT (self));
2620 clutter_text_real_move_up (ClutterText *self,
2621 const gchar *action,
2623 ClutterModifierType modifiers)
2625 ClutterTextPrivate *priv = self->priv;
2626 PangoLayoutLine *layout_line;
2627 PangoLayout *layout;
2629 gint index_, trailing;
2634 layout = clutter_text_get_layout (self);
2635 text = clutter_text_buffer_get_text (get_buffer (self));
2637 if (priv->position == 0)
2640 index_ = offset_to_bytes (text, priv->position);
2642 pango_layout_index_to_line_x (layout, index_,
2650 if (priv->x_pos != -1)
2653 layout_line = pango_layout_get_line_readonly (layout, line_no);
2657 pango_layout_line_x_to_index (layout_line, x, &index_, &trailing);
2659 g_object_freeze_notify (G_OBJECT (self));
2661 pos = bytes_to_offset (text, index_);
2662 clutter_text_set_cursor_position (self, pos + trailing);
2664 /* Store the target x position to avoid drifting left and right when
2665 moving the cursor up and down */
2668 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2669 clutter_text_clear_selection (self);
2671 g_object_thaw_notify (G_OBJECT (self));
2677 clutter_text_real_move_down (ClutterText *self,
2678 const gchar *action,
2680 ClutterModifierType modifiers)
2682 ClutterTextPrivate *priv = self->priv;
2683 PangoLayoutLine *layout_line;
2684 PangoLayout *layout;
2686 gint index_, trailing;
2691 layout = clutter_text_get_layout (self);
2692 text = clutter_text_buffer_get_text (get_buffer (self));
2694 if (priv->position == 0)
2697 index_ = offset_to_bytes (text, priv->position);
2699 pango_layout_index_to_line_x (layout, index_,
2703 if (priv->x_pos != -1)
2706 layout_line = pango_layout_get_line_readonly (layout, line_no + 1);
2710 pango_layout_line_x_to_index (layout_line, x, &index_, &trailing);
2712 g_object_freeze_notify (G_OBJECT (self));
2714 pos = bytes_to_offset (text, index_);
2715 clutter_text_set_cursor_position (self, pos + trailing);
2717 /* Store the target x position to avoid drifting left and right when
2718 moving the cursor up and down */
2721 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2722 clutter_text_clear_selection (self);
2724 g_object_thaw_notify (G_OBJECT (self));
2730 clutter_text_real_line_start (ClutterText *self,
2731 const gchar *action,
2733 ClutterModifierType modifiers)
2735 ClutterTextPrivate *priv = self->priv;
2738 g_object_freeze_notify (G_OBJECT (self));
2740 position = clutter_text_move_line_start (self, priv->position);
2741 clutter_text_set_cursor_position (self, position);
2743 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2744 clutter_text_clear_selection (self);
2746 g_object_thaw_notify (G_OBJECT (self));
2752 clutter_text_real_line_end (ClutterText *self,
2753 const gchar *action,
2755 ClutterModifierType modifiers)
2757 ClutterTextPrivate *priv = self->priv;
2760 g_object_freeze_notify (G_OBJECT (self));
2762 position = clutter_text_move_line_end (self, priv->position);
2763 clutter_text_set_cursor_position (self, position);
2765 if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2766 clutter_text_clear_selection (self);
2768 g_object_thaw_notify (G_OBJECT (self));
2774 clutter_text_real_select_all (ClutterText *self,
2775 const gchar *action,
2777 ClutterModifierType modifiers)
2779 guint n_chars = clutter_text_buffer_get_length (get_buffer (self));
2780 clutter_text_set_positions (self, 0, n_chars);
2786 clutter_text_real_del_next (ClutterText *self,
2787 const gchar *action,
2789 ClutterModifierType modifiers)
2791 ClutterTextPrivate *priv = self->priv;
2795 if (clutter_text_delete_selection (self))
2798 pos = priv->position;
2799 len = clutter_text_buffer_get_length (get_buffer (self));
2801 if (len && pos != -1 && pos < len)
2802 clutter_text_delete_text (self, pos, pos + 1);
2808 clutter_text_real_del_word_next (ClutterText *self,
2809 const gchar *action,
2811 ClutterModifierType modifiers)
2813 ClutterTextPrivate *priv = self->priv;
2817 pos = priv->position;
2818 len = clutter_text_buffer_get_length (get_buffer (self));
2820 if (len && pos != -1 && pos < len)
2824 end = clutter_text_move_word_forward (self, pos);
2825 clutter_text_delete_text (self, pos, end);
2827 if (priv->selection_bound >= end)
2831 new_bound = priv->selection_bound - (end - pos);
2832 clutter_text_set_selection_bound (self, new_bound);
2834 else if (priv->selection_bound > pos)
2836 clutter_text_set_selection_bound (self, pos);
2844 clutter_text_real_del_prev (ClutterText *self,
2845 const gchar *action,
2847 ClutterModifierType modifiers)
2849 ClutterTextPrivate *priv = self->priv;
2853 if (clutter_text_delete_selection (self))
2856 pos = priv->position;
2857 len = clutter_text_buffer_get_length (get_buffer (self));
2859 if (pos != 0 && len != 0)
2863 clutter_text_delete_text (self, len - 1, len);
2865 clutter_text_set_positions (self, -1, -1);
2869 clutter_text_delete_text (self, pos - 1, pos);
2871 clutter_text_set_positions (self, pos - 1, pos - 1);
2879 clutter_text_real_del_word_prev (ClutterText *self,
2880 const gchar *action,
2882 ClutterModifierType modifiers)
2884 ClutterTextPrivate *priv = self->priv;
2888 pos = priv->position;
2889 len = clutter_text_buffer_get_length (get_buffer (self));
2891 if (pos != 0 && len != 0)
2897 new_pos = clutter_text_move_word_backward (self, len);
2898 clutter_text_delete_text (self, new_pos, len);
2900 clutter_text_set_positions (self, -1, -1);
2904 new_pos = clutter_text_move_word_backward (self, pos);
2905 clutter_text_delete_text (self, new_pos, pos);
2907 clutter_text_set_cursor_position (self, new_pos);
2908 if (priv->selection_bound >= pos)
2912 new_bound = priv->selection_bound - (pos - new_pos);
2913 clutter_text_set_selection_bound (self, new_bound);
2915 else if (priv->selection_bound >= new_pos)
2917 clutter_text_set_selection_bound (self, new_pos);
2926 clutter_text_real_activate (ClutterText *self,
2927 const gchar *action,
2929 ClutterModifierType modifiers)
2931 return clutter_text_activate (self);
2935 clutter_text_add_move_binding (ClutterBindingPool *pool,
2936 const gchar *action,
2938 ClutterModifierType additional_modifiers,
2941 clutter_binding_pool_install_action (pool, action,
2946 clutter_binding_pool_install_action (pool, action,
2952 if (additional_modifiers != 0)
2954 clutter_binding_pool_install_action (pool, action,
2956 additional_modifiers,
2959 clutter_binding_pool_install_action (pool, action,
2961 CLUTTER_SHIFT_MASK |
2962 additional_modifiers,
2969 clutter_text_parse_custom_node (ClutterScriptable *scriptable,
2970 ClutterScript *script,
2975 if (strncmp (name, "font-description", 16) == 0)
2977 g_value_init (value, G_TYPE_STRING);
2978 g_value_set_string (value, json_node_get_string (node));
2987 clutter_text_set_color_internal (ClutterText *self,
2989 const ClutterColor *color)
2991 ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
2992 GParamSpec *other = NULL;
2994 switch (pspec->param_id)
2997 priv->text_color = *color;
3000 case PROP_CURSOR_COLOR:
3003 priv->cursor_color = *color;
3004 priv->cursor_color_set = TRUE;
3007 priv->cursor_color_set = FALSE;
3009 other = obj_props[PROP_CURSOR_COLOR_SET];
3012 case PROP_SELECTION_COLOR:
3015 priv->selection_color = *color;
3016 priv->selection_color_set = TRUE;
3019 priv->selection_color_set = FALSE;
3021 other = obj_props[PROP_SELECTION_COLOR_SET];
3024 case PROP_SELECTED_TEXT_COLOR:
3027 priv->selected_text_color = *color;
3028 priv->selected_text_color_set = TRUE;
3031 priv->selected_text_color_set = FALSE;
3033 other = obj_props[PROP_SELECTED_TEXT_COLOR_SET];
3037 g_assert_not_reached ();
3041 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
3042 g_object_notify_by_pspec (G_OBJECT (self), pspec);
3044 g_object_notify_by_pspec (G_OBJECT (self), other);
3048 clutter_text_set_color_animated (ClutterText *self,
3050 const ClutterColor *color)
3052 ClutterActor *actor = CLUTTER_ACTOR (self);
3053 ClutterTextPrivate *priv = self->priv;
3054 const ClutterAnimationInfo *info;
3055 ClutterTransition *transition;
3057 info = _clutter_actor_get_animation_info (actor);
3058 transition = clutter_actor_get_transition (actor, pspec->name);
3060 /* jump to the end if there is no easing state, or if the easing
3061 * state has a duration of 0 msecs
3063 if (info->cur_state == NULL ||
3064 info->cur_state->easing_duration == 0)
3066 /* ensure that we remove any currently running transition */
3067 if (transition != NULL)
3069 clutter_actor_remove_transition (actor, pspec->name);
3073 clutter_text_set_color_internal (self, pspec, color);
3078 if (transition == NULL)
3080 transition = clutter_property_transition_new (pspec->name);
3081 clutter_transition_set_animatable (transition,
3082 CLUTTER_ANIMATABLE (self));
3083 clutter_transition_set_remove_on_complete (transition, TRUE);
3085 /* delay only makes sense if the transition has just been created */
3086 clutter_timeline_set_delay (CLUTTER_TIMELINE (transition),
3087 info->cur_state->easing_delay);
3089 clutter_actor_add_transition (actor, pspec->name, transition);
3091 /* the actor now owns the transition */
3092 g_object_unref (transition);
3095 /* if a transition already exist, update its bounds */
3096 switch (pspec->param_id)
3099 clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3103 case PROP_CURSOR_COLOR:
3104 clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3105 &priv->cursor_color);
3108 case PROP_SELECTION_COLOR:
3109 clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3110 &priv->selection_color);
3113 case PROP_SELECTED_TEXT_COLOR:
3114 clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3115 &priv->selected_text_color);
3119 g_assert_not_reached ();
3122 clutter_transition_set_to (transition, CLUTTER_TYPE_COLOR, color);
3124 /* always use the current easing state */
3125 clutter_timeline_set_duration (CLUTTER_TIMELINE (transition),
3126 info->cur_state->easing_duration);
3127 clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
3128 info->cur_state->easing_mode);
3130 /* ensure that we start from the beginning */
3131 clutter_timeline_rewind (CLUTTER_TIMELINE (transition));
3132 clutter_timeline_start (CLUTTER_TIMELINE (transition));
3136 clutter_text_set_custom_property (ClutterScriptable *scriptable,
3137 ClutterScript *script,
3139 const GValue *value)
3141 if (strncmp (name, "font-description", 16) == 0)
3143 g_assert (G_VALUE_HOLDS (value, G_TYPE_STRING));
3144 if (g_value_get_string (value) != NULL)
3145 clutter_text_set_font_name (CLUTTER_TEXT (scriptable),
3146 g_value_get_string (value));
3149 g_object_set_property (G_OBJECT (scriptable), name, value);
3153 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
3155 iface->parse_custom_node = clutter_text_parse_custom_node;
3156 iface->set_custom_property = clutter_text_set_custom_property;
3160 clutter_text_set_final_state (ClutterAnimatable *animatable,
3161 const char *property_name,
3162 const GValue *value)
3164 if (strcmp (property_name, "color") == 0)
3166 const ClutterColor *color = clutter_value_get_color (value);
3167 clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3168 obj_props[PROP_COLOR], color);
3170 else if (strcmp (property_name, "cursor-color") == 0)
3172 const ClutterColor *color = clutter_value_get_color (value);
3173 clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3174 obj_props[PROP_CURSOR_COLOR],
3177 else if (strcmp (property_name, "selected-text-color") == 0)
3179 const ClutterColor *color = clutter_value_get_color (value);
3180 clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3181 obj_props[PROP_SELECTED_TEXT_COLOR],
3184 else if (strcmp (property_name, "selection-color") == 0)
3186 const ClutterColor *color = clutter_value_get_color (value);
3187 clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3188 obj_props[PROP_SELECTION_COLOR],
3192 parent_animatable_iface->set_final_state (animatable, property_name, value);
3196 clutter_animatable_iface_init (ClutterAnimatableIface *iface)
3198 parent_animatable_iface = g_type_interface_peek_parent (iface);
3200 iface->set_final_state = clutter_text_set_final_state;
3204 clutter_text_class_init (ClutterTextClass *klass)
3206 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3207 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
3208 ClutterBindingPool *binding_pool;
3211 g_type_class_add_private (klass, sizeof (ClutterTextPrivate));
3213 gobject_class->set_property = clutter_text_set_property;
3214 gobject_class->get_property = clutter_text_get_property;
3215 gobject_class->dispose = clutter_text_dispose;
3216 gobject_class->finalize = clutter_text_finalize;
3218 actor_class->paint = clutter_text_paint;
3219 actor_class->get_paint_volume = clutter_text_get_paint_volume;
3220 actor_class->get_preferred_width = clutter_text_get_preferred_width;
3221 actor_class->get_preferred_height = clutter_text_get_preferred_height;
3222 actor_class->allocate = clutter_text_allocate;
3223 actor_class->key_press_event = clutter_text_key_press;
3224 actor_class->button_press_event = clutter_text_button_press;
3225 actor_class->button_release_event = clutter_text_button_release;
3226 actor_class->motion_event = clutter_text_motion;
3227 actor_class->key_focus_in = clutter_text_key_focus_in;
3228 actor_class->key_focus_out = clutter_text_key_focus_out;
3229 actor_class->has_overlaps = clutter_text_has_overlaps;
3232 * ClutterText:buffer:
3234 * The buffer which stores the text for this #ClutterText.
3236 * If set to %NULL, a default buffer will be created.
3240 pspec = g_param_spec_object ("buffer",
3242 P_("The buffer for the text"),
3243 CLUTTER_TYPE_TEXT_BUFFER,
3244 CLUTTER_PARAM_READWRITE);
3245 obj_props[PROP_BUFFER] = pspec;
3246 g_object_class_install_property (gobject_class, PROP_BUFFER, pspec);
3249 * ClutterText:font-name:
3251 * The font to be used by the #ClutterText, as a string
3252 * that can be parsed by pango_font_description_from_string().
3254 * If set to %NULL, the default system font will be used instead.
3258 pspec = g_param_spec_string ("font-name",
3260 P_("The font to be used by the text"),
3262 CLUTTER_PARAM_READWRITE);
3263 obj_props[PROP_FONT_NAME] = pspec;
3264 g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec);
3267 * ClutterText:font-description:
3269 * The #PangoFontDescription that should be used by the #ClutterText
3271 * If you have a string describing the font then you should look at
3272 * #ClutterText:font-name instead
3276 pspec = g_param_spec_boxed ("font-description",
3277 P_("Font Description"),
3278 P_("The font description to be used"),
3279 PANGO_TYPE_FONT_DESCRIPTION,
3280 CLUTTER_PARAM_READWRITE);
3281 obj_props[PROP_FONT_DESCRIPTION] = pspec;
3282 g_object_class_install_property (gobject_class,
3283 PROP_FONT_DESCRIPTION,
3289 * The text to render inside the actor.
3293 pspec = g_param_spec_string ("text",
3295 P_("The text to render"),
3297 CLUTTER_PARAM_READWRITE);
3298 obj_props[PROP_TEXT] = pspec;
3299 g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
3302 * ClutterText:color:
3304 * The color used to render the text.
3308 pspec = clutter_param_spec_color ("color",
3310 P_("Color of the font used by the text"),
3311 &default_text_color,
3312 CLUTTER_PARAM_READWRITE |
3313 CLUTTER_PARAM_ANIMATABLE);
3314 obj_props[PROP_COLOR] = pspec;
3315 g_object_class_install_property (gobject_class, PROP_COLOR, pspec);
3318 * ClutterText:editable:
3320 * Whether key events delivered to the actor causes editing.
3324 pspec = g_param_spec_boolean ("editable",
3326 P_("Whether the text is editable"),
3329 obj_props[PROP_EDITABLE] = pspec;
3330 g_object_class_install_property (gobject_class, PROP_EDITABLE, pspec);
3333 * ClutterText:selectable:
3335 * Whether it is possible to select text, either using the pointer
3340 pspec = g_param_spec_boolean ("selectable",
3342 P_("Whether the text is selectable"),
3345 obj_props[PROP_SELECTABLE] = pspec;
3346 g_object_class_install_property (gobject_class, PROP_SELECTABLE, pspec);
3349 * ClutterText:activatable:
3351 * Toggles whether return invokes the activate signal or not.
3355 pspec = g_param_spec_boolean ("activatable",
3357 P_("Whether pressing return causes the activate signal to be emitted"),
3360 obj_props[PROP_ACTIVATABLE] = pspec;
3361 g_object_class_install_property (gobject_class, PROP_ACTIVATABLE, pspec);
3364 * ClutterText:cursor-visible:
3366 * Whether the input cursor is visible or not, it will only be visible
3367 * if both #ClutterText:cursor-visible and #ClutterText:editable are
3372 pspec = g_param_spec_boolean ("cursor-visible",
3373 P_("Cursor Visible"),
3374 P_("Whether the input cursor is visible"),
3376 CLUTTER_PARAM_READWRITE);
3377 obj_props[PROP_CURSOR_VISIBLE] = pspec;
3378 g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec);
3381 * ClutterText:cursor-color:
3383 * The color of the cursor.
3387 pspec = clutter_param_spec_color ("cursor-color",
3390 &default_cursor_color,
3391 CLUTTER_PARAM_READWRITE |
3392 CLUTTER_PARAM_ANIMATABLE);
3393 obj_props[PROP_CURSOR_COLOR] = pspec;
3394 g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec);
3397 * ClutterText:cursor-color-set:
3399 * Will be set to %TRUE if #ClutterText:cursor-color has been set.
3403 pspec = g_param_spec_boolean ("cursor-color-set",
3404 P_("Cursor Color Set"),
3405 P_("Whether the cursor color has been set"),
3407 CLUTTER_PARAM_READABLE);
3408 obj_props[PROP_CURSOR_COLOR_SET] = pspec;
3409 g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR_SET, pspec);
3412 * ClutterText:cursor-size:
3414 * The size of the cursor, in pixels. If set to -1 the size used will
3415 * be the default cursor size of 2 pixels.
3419 pspec = g_param_spec_int ("cursor-size",
3421 P_("The width of the cursor, in pixels"),
3422 -1, G_MAXINT, DEFAULT_CURSOR_SIZE,
3423 CLUTTER_PARAM_READWRITE);
3424 obj_props[PROP_CURSOR_SIZE] = pspec;
3425 g_object_class_install_property (gobject_class, PROP_CURSOR_SIZE, pspec);
3428 * ClutterText:position:
3430 * The current input cursor position. -1 is taken to be the end of the text
3434 pspec = g_param_spec_int ("position",
3435 P_("Cursor Position"),
3436 P_("The cursor position"),
3439 CLUTTER_PARAM_READWRITE);
3440 obj_props[PROP_POSITION] = pspec;
3441 g_object_class_install_property (gobject_class, PROP_POSITION, pspec);
3444 * ClutterText:selection-bound:
3446 * The current input cursor position. -1 is taken to be the end of the text
3450 pspec = g_param_spec_int ("selection-bound",
3451 P_("Selection-bound"),
3452 P_("The cursor position of the other end of the selection"),
3455 CLUTTER_PARAM_READWRITE);
3456 obj_props[PROP_SELECTION_BOUND] = pspec;
3457 g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec);
3460 * ClutterText:selection-color:
3462 * The color of the selection.
3466 pspec = clutter_param_spec_color ("selection-color",
3467 P_("Selection Color"),
3468 P_("Selection Color"),
3469 &default_selection_color,
3470 CLUTTER_PARAM_READWRITE |
3471 CLUTTER_PARAM_ANIMATABLE);
3472 obj_props[PROP_SELECTION_COLOR] = pspec;
3473 g_object_class_install_property (gobject_class, PROP_SELECTION_COLOR, pspec);
3476 * ClutterText:selection-color-set:
3478 * Will be set to %TRUE if #ClutterText:selection-color has been set.
3482 pspec = g_param_spec_boolean ("selection-color-set",
3483 P_("Selection Color Set"),
3484 P_("Whether the selection color has been set"),
3486 CLUTTER_PARAM_READABLE);
3487 obj_props[PROP_SELECTION_COLOR_SET] = pspec;
3488 g_object_class_install_property (gobject_class, PROP_SELECTION_COLOR_SET, pspec);
3491 * ClutterText:attributes:
3493 * A list of #PangoStyleAttribute<!-- -->s to be applied to the
3494 * contents of the #ClutterText actor.
3498 pspec = g_param_spec_boxed ("attributes",
3500 P_("A list of style attributes to apply to the contents of the actor"),
3501 PANGO_TYPE_ATTR_LIST,
3502 CLUTTER_PARAM_READWRITE);
3503 obj_props[PROP_ATTRIBUTES] = pspec;
3504 g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec);
3507 * ClutterText:use-markup:
3509 * Whether the text includes Pango markup.
3511 * For more informations about the Pango markup format, see
3512 * pango_layout_set_markup() in the Pango documentation.
3514 * <note>It is not possible to round-trip this property between
3515 * %TRUE and %FALSE. Once a string with markup has been set on
3516 * a #ClutterText actor with :use-markup set to %TRUE, the markup
3517 * is stripped from the string.</note>
3521 pspec = g_param_spec_boolean ("use-markup",
3523 P_("Whether or not the text includes Pango markup"),
3525 CLUTTER_PARAM_READWRITE);
3526 obj_props[PROP_USE_MARKUP] = pspec;
3527 g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec);
3530 * ClutterText:line-wrap:
3532 * Whether to wrap the lines of #ClutterText:text if the contents
3533 * exceed the available allocation. The wrapping strategy is
3534 * controlled by the #ClutterText:line-wrap-mode property.
3538 pspec = g_param_spec_boolean ("line-wrap",
3540 P_("If set, wrap the lines if the text becomes too wide"),
3542 CLUTTER_PARAM_READWRITE);
3543 obj_props[PROP_LINE_WRAP] = pspec;
3544 g_object_class_install_property (gobject_class, PROP_LINE_WRAP, pspec);
3547 * ClutterText:line-wrap-mode:
3549 * If #ClutterText:line-wrap is set to %TRUE, this property will
3550 * control how the text is wrapped.
3554 pspec = g_param_spec_enum ("line-wrap-mode",
3555 P_("Line wrap mode"),
3556 P_("Control how line-wrapping is done"),
3557 PANGO_TYPE_WRAP_MODE,
3559 CLUTTER_PARAM_READWRITE);
3560 obj_props[PROP_LINE_WRAP_MODE] = pspec;
3561 g_object_class_install_property (gobject_class, PROP_LINE_WRAP_MODE, pspec);
3564 * ClutterText:ellipsize:
3566 * The preferred place to ellipsize the contents of the #ClutterText actor
3570 pspec = g_param_spec_enum ("ellipsize",
3572 P_("The preferred place to ellipsize the string"),
3573 PANGO_TYPE_ELLIPSIZE_MODE,
3574 PANGO_ELLIPSIZE_NONE,
3575 CLUTTER_PARAM_READWRITE);
3576 obj_props[PROP_ELLIPSIZE] = pspec;
3577 g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec);
3580 * ClutterText:line-alignment:
3582 * The preferred alignment for the text. This property controls
3583 * the alignment of multi-line paragraphs.
3587 pspec = g_param_spec_enum ("line-alignment",
3588 P_("Line Alignment"),
3589 P_("The preferred alignment for the string, for multi-line text"),
3590 PANGO_TYPE_ALIGNMENT,
3592 CLUTTER_PARAM_READWRITE);
3593 obj_props[PROP_LINE_ALIGNMENT] = pspec;
3594 g_object_class_install_property (gobject_class, PROP_LINE_ALIGNMENT, pspec);
3597 * ClutterText:justify:
3599 * Whether the contents of the #ClutterText should be justified
3604 pspec = g_param_spec_boolean ("justify",
3606 P_("Whether the text should be justified"),
3608 CLUTTER_PARAM_READWRITE);
3609 obj_props[PROP_JUSTIFY] = pspec;
3610 g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec);
3613 * ClutterText:password-char:
3615 * If non-zero, the character that should be used in place of
3616 * the actual text in a password text actor.
3620 pspec = g_param_spec_unichar ("password-char",
3621 P_("Password Character"),
3622 P_("If non-zero, use this character to display the actor's contents"),
3624 CLUTTER_PARAM_READWRITE);
3625 obj_props[PROP_PASSWORD_CHAR] = pspec;
3626 g_object_class_install_property (gobject_class, PROP_PASSWORD_CHAR, pspec);
3629 * ClutterText:max-length:
3631 * The maximum length of the contents of the #ClutterText actor.
3635 pspec = g_param_spec_int ("max-length",
3637 P_("Maximum length of the text inside the actor"),
3639 CLUTTER_PARAM_READWRITE);
3640 obj_props[PROP_MAX_LENGTH] = pspec;
3641 g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec);
3644 * ClutterText:single-line-mode:
3646 * Whether the #ClutterText actor should be in single line mode
3647 * or not. A single line #ClutterText actor will only contain a
3648 * single line of text, scrolling it in case its length is bigger
3649 * than the allocated size.
3651 * Setting this property will also set the #ClutterText:activatable
3652 * property as a side-effect.
3654 * The #ClutterText:single-line-mode property is used only if the
3655 * #ClutterText:editable property is set to %TRUE.
3659 pspec = g_param_spec_boolean ("single-line-mode",
3660 P_("Single Line Mode"),
3661 P_("Whether the text should be a single line"),
3663 CLUTTER_PARAM_READWRITE);
3664 obj_props[PROP_SINGLE_LINE_MODE] = pspec;
3665 g_object_class_install_property (gobject_class, PROP_SINGLE_LINE_MODE, pspec);
3668 * ClutterText:selected-text-color:
3670 * The color of selected text.
3674 pspec = clutter_param_spec_color ("selected-text-color",
3675 P_("Selected Text Color"),
3676 P_("Selected Text Color"),
3677 &default_selected_text_color,
3678 CLUTTER_PARAM_READWRITE |
3679 CLUTTER_PARAM_ANIMATABLE);
3680 obj_props[PROP_SELECTED_TEXT_COLOR] = pspec;
3681 g_object_class_install_property (gobject_class, PROP_SELECTED_TEXT_COLOR, pspec);
3684 * ClutterText:selected-text-color-set:
3686 * Will be set to %TRUE if #ClutterText:selected-text-color has been set.
3690 pspec = g_param_spec_boolean ("selected-text-color-set",
3691 P_("Selected Text Color Set"),
3692 P_("Whether the selected text color has been set"),
3694 CLUTTER_PARAM_READABLE);
3695 obj_props[PROP_SELECTED_TEXT_COLOR_SET] = pspec;
3696 g_object_class_install_property (gobject_class, PROP_SELECTED_TEXT_COLOR_SET, pspec);
3699 * ClutterText::text-changed:
3700 * @self: the #ClutterText that emitted the signal
3702 * The ::text-changed signal is emitted after @actor's text changes
3706 text_signals[TEXT_CHANGED] =
3707 g_signal_new (I_("text-changed"),
3708 G_TYPE_FROM_CLASS (gobject_class),
3710 G_STRUCT_OFFSET (ClutterTextClass, text_changed),
3712 _clutter_marshal_VOID__VOID,
3716 * ClutterText::insert-text:
3717 * @self: the #ClutterText that emitted the signal
3718 * @new_text: the new text to insert
3719 * @new_text_length: the length of the new text, in bytes, or -1 if
3720 * new_text is nul-terminated
3721 * @position: the position, in characters, at which to insert the
3722 * new text. this is an in-out parameter. After the signal
3723 * emission is finished, it should point after the newly
3726 * This signal is emitted when text is inserted into the actor by
3727 * the user. It is emitted before @self text changes.
3731 text_signals[INSERT_TEXT] =
3732 g_signal_new (I_("insert-text"),
3733 G_TYPE_FROM_CLASS (gobject_class),
3734 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3737 _clutter_marshal_VOID__STRING_INT_POINTER,
3744 * ClutterText::delete-text:
3745 * @self: the #ClutterText that emitted the signal
3746 * @start_pos: the starting position
3747 * @end_pos: the end position
3749 * This signal is emitted when text is deleted from the actor by
3750 * the user. It is emitted before @self text changes.
3754 text_signals[DELETE_TEXT] =
3755 g_signal_new (I_("delete-text"),
3756 G_TYPE_FROM_CLASS (gobject_class),
3757 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3760 _clutter_marshal_VOID__INT_INT,
3766 * ClutterText::cursor-event:
3767 * @self: the #ClutterText that emitted the signal
3768 * @geometry: the coordinates of the cursor
3770 * The ::cursor-event signal is emitted whenever the cursor position
3771 * changes inside a #ClutterText actor. Inside @geometry it is stored
3772 * the current position and size of the cursor, relative to the actor
3777 text_signals[CURSOR_EVENT] =
3778 g_signal_new (I_("cursor-event"),
3779 G_TYPE_FROM_CLASS (gobject_class),
3781 G_STRUCT_OFFSET (ClutterTextClass, cursor_event),
3783 _clutter_marshal_VOID__BOXED,
3785 CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE);
3788 * ClutterText::activate:
3789 * @self: the #ClutterText that emitted the signal
3791 * The ::activate signal is emitted each time the actor is 'activated'
3792 * by the user, normally by pressing the 'Enter' key. The signal is
3793 * emitted only if #ClutterText:activatable is set to %TRUE.
3797 text_signals[ACTIVATE] =
3798 g_signal_new (I_("activate"),
3799 G_TYPE_FROM_CLASS (gobject_class),
3801 G_STRUCT_OFFSET (ClutterTextClass, activate),
3803 _clutter_marshal_VOID__VOID,
3806 binding_pool = clutter_binding_pool_get_for_class (klass);
3808 clutter_text_add_move_binding (binding_pool, "move-left",
3809 CLUTTER_KEY_Left, CLUTTER_CONTROL_MASK,
3810 G_CALLBACK (clutter_text_real_move_left));
3811 clutter_text_add_move_binding (binding_pool, "move-left",
3812 CLUTTER_KEY_KP_Left, CLUTTER_CONTROL_MASK,
3813 G_CALLBACK (clutter_text_real_move_left));
3814 clutter_text_add_move_binding (binding_pool, "move-right",
3815 CLUTTER_KEY_Right, CLUTTER_CONTROL_MASK,
3816 G_CALLBACK (clutter_text_real_move_right));
3817 clutter_text_add_move_binding (binding_pool, "move-right",
3818 CLUTTER_KEY_KP_Right, CLUTTER_CONTROL_MASK,
3819 G_CALLBACK (clutter_text_real_move_right));
3820 clutter_text_add_move_binding (binding_pool, "move-up",
3822 G_CALLBACK (clutter_text_real_move_up));
3823 clutter_text_add_move_binding (binding_pool, "move-up",
3824 CLUTTER_KEY_KP_Up, 0,
3825 G_CALLBACK (clutter_text_real_move_up));
3826 clutter_text_add_move_binding (binding_pool, "move-down",
3827 CLUTTER_KEY_Down, 0,
3828 G_CALLBACK (clutter_text_real_move_down));
3829 clutter_text_add_move_binding (binding_pool, "move-down",
3830 CLUTTER_KEY_KP_Down, 0,
3831 G_CALLBACK (clutter_text_real_move_down));
3833 clutter_text_add_move_binding (binding_pool, "line-start",
3834 CLUTTER_KEY_Home, 0,
3835 G_CALLBACK (clutter_text_real_line_start));
3836 clutter_text_add_move_binding (binding_pool, "line-start",
3837 CLUTTER_KEY_KP_Home, 0,
3838 G_CALLBACK (clutter_text_real_line_start));
3839 clutter_text_add_move_binding (binding_pool, "line-start",
3840 CLUTTER_KEY_Begin, 0,
3841 G_CALLBACK (clutter_text_real_line_start));
3842 clutter_text_add_move_binding (binding_pool, "line-end",
3844 G_CALLBACK (clutter_text_real_line_end));
3845 clutter_text_add_move_binding (binding_pool, "line-end",
3846 CLUTTER_KEY_KP_End, 0,
3847 G_CALLBACK (clutter_text_real_line_end));
3849 clutter_binding_pool_install_action (binding_pool, "select-all",
3850 CLUTTER_KEY_a, CLUTTER_CONTROL_MASK,
3851 G_CALLBACK (clutter_text_real_select_all),
3854 clutter_binding_pool_install_action (binding_pool, "delete-next",
3855 CLUTTER_KEY_Delete, 0,
3856 G_CALLBACK (clutter_text_real_del_next),
3858 clutter_binding_pool_install_action (binding_pool, "delete-next",
3859 CLUTTER_KEY_Delete, CLUTTER_CONTROL_MASK,
3860 G_CALLBACK (clutter_text_real_del_word_next),
3862 clutter_binding_pool_install_action (binding_pool, "delete-next",
3863 CLUTTER_KEY_KP_Delete, 0,
3864 G_CALLBACK (clutter_text_real_del_next),
3866 clutter_binding_pool_install_action (binding_pool, "delete-next",
3867 CLUTTER_KEY_KP_Delete, CLUTTER_CONTROL_MASK,
3868 G_CALLBACK (clutter_text_real_del_word_next),
3870 clutter_binding_pool_install_action (binding_pool, "delete-prev",
3871 CLUTTER_KEY_BackSpace, 0,
3872 G_CALLBACK (clutter_text_real_del_prev),
3874 clutter_binding_pool_install_action (binding_pool, "delete-prev",
3875 CLUTTER_KEY_BackSpace, CLUTTER_SHIFT_MASK,
3876 G_CALLBACK (clutter_text_real_del_prev),
3878 clutter_binding_pool_install_action (binding_pool, "delete-prev",
3879 CLUTTER_KEY_BackSpace, CLUTTER_CONTROL_MASK,
3880 G_CALLBACK (clutter_text_real_del_word_prev),
3883 clutter_binding_pool_install_action (binding_pool, "activate",
3884 CLUTTER_KEY_Return, 0,
3885 G_CALLBACK (clutter_text_real_activate),
3887 clutter_binding_pool_install_action (binding_pool, "activate",
3888 CLUTTER_KEY_KP_Enter, 0,
3889 G_CALLBACK (clutter_text_real_activate),
3891 clutter_binding_pool_install_action (binding_pool, "activate",
3892 CLUTTER_KEY_ISO_Enter, 0,
3893 G_CALLBACK (clutter_text_real_activate),
3898 clutter_text_init (ClutterText *self)
3900 ClutterSettings *settings;
3901 ClutterTextPrivate *priv;
3903 int i, password_hint_time;
3905 self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self);
3907 priv->alignment = PANGO_ALIGN_LEFT;
3909 priv->wrap_mode = PANGO_WRAP_WORD;
3910 priv->ellipsize = PANGO_ELLIPSIZE_NONE;
3911 priv->use_underline = FALSE;
3912 priv->use_markup = FALSE;
3913 priv->justify = FALSE;
3915 for (i = 0; i < N_CACHED_LAYOUTS; i++)
3916 priv->cached_layouts[i].layout = NULL;
3918 /* default to "" so that clutter_text_get_text() will
3919 * return a valid string and we can safely call strlen()
3922 priv->buffer = NULL;
3924 priv->text_color = default_text_color;
3925 priv->cursor_color = default_cursor_color;
3926 priv->selection_color = default_selection_color;
3927 priv->selected_text_color = default_selected_text_color;
3929 /* get the default font name from the context; we don't use
3930 * set_font_description() here because we are initializing
3931 * the Text and we don't need notifications and sanity checks
3933 settings = clutter_settings_get_default ();
3934 g_object_get (settings,
3935 "font-name", &font_name,
3936 "password-hint-time", &password_hint_time,
3939 priv->font_name = font_name; /* font_name is allocated */
3940 priv->font_desc = pango_font_description_from_string (font_name);
3941 priv->is_default_font = TRUE;
3943 priv->position = -1;
3944 priv->selection_bound = -1;
3947 priv->cursor_visible = TRUE;
3948 priv->editable = FALSE;
3949 priv->selectable = TRUE;
3951 priv->selection_color_set = FALSE;
3952 priv->cursor_color_set = FALSE;
3953 priv->selected_text_color_set = FALSE;
3954 priv->preedit_set = FALSE;
3956 priv->password_char = 0;
3957 priv->show_password_hint = password_hint_time > 0;
3958 priv->password_hint_timeout = password_hint_time;
3962 priv->cursor_size = DEFAULT_CURSOR_SIZE;
3963 memset (&priv->cursor_pos, 0, sizeof (ClutterGeometry));
3965 priv->settings_changed_id =
3966 g_signal_connect_swapped (clutter_get_default_backend (),
3968 G_CALLBACK (clutter_text_settings_changed_cb),
3971 priv->direction_changed_id =
3972 g_signal_connect (self, "notify::text-direction",
3973 G_CALLBACK (clutter_text_direction_changed_cb),
3980 * Creates a new #ClutterText actor. This actor can be used to
3981 * display and edit text.
3983 * Return value: the newly created #ClutterText actor
3988 clutter_text_new (void)
3990 return g_object_new (CLUTTER_TYPE_TEXT, NULL);
3994 * clutter_text_new_full:
3995 * @font_name: a string with a font description
3996 * @text: the contents of the actor
3997 * @color: the color to be used to render @text
3999 * Creates a new #ClutterText actor, using @font_name as the font
4000 * description; @text will be used to set the contents of the actor;
4001 * and @color will be used as the color to render @text.
4003 * This function is equivalent to calling clutter_text_new(),
4004 * clutter_text_set_font_name(), clutter_text_set_text() and
4005 * clutter_text_set_color().
4007 * Return value: the newly created #ClutterText actor
4012 clutter_text_new_full (const gchar *font_name,
4014 const ClutterColor *color)
4016 return g_object_new (CLUTTER_TYPE_TEXT,
4017 "font-name", font_name,
4024 * clutter_text_new_with_text:
4025 * @font_name: (allow-none): a string with a font description
4026 * @text: the contents of the actor
4028 * Creates a new #ClutterText actor, using @font_name as the font
4029 * description; @text will be used to set the contents of the actor.
4031 * This function is equivalent to calling clutter_text_new(),
4032 * clutter_text_set_font_name(), and clutter_text_set_text().
4034 * Return value: the newly created #ClutterText actor
4039 clutter_text_new_with_text (const gchar *font_name,
4042 return g_object_new (CLUTTER_TYPE_TEXT,
4043 "font-name", font_name,
4048 static ClutterTextBuffer*
4049 get_buffer (ClutterText *self)
4051 ClutterTextPrivate *priv = self->priv;
4053 if (priv->buffer == NULL)
4055 ClutterTextBuffer *buffer;
4056 buffer = clutter_text_buffer_new ();
4057 clutter_text_set_buffer (self, buffer);
4058 g_object_unref (buffer);
4061 return priv->buffer;
4064 /* GtkEntryBuffer signal handlers
4067 buffer_inserted_text (ClutterTextBuffer *buffer,
4073 ClutterTextPrivate *priv;
4075 gint new_selection_bound;
4079 if (priv->position >= 0 || priv->selection_bound >= 0)
4081 new_position = priv->position;
4082 new_selection_bound = priv->selection_bound;
4084 if (position <= new_position)
4085 new_position += n_chars;
4086 if (position <= new_selection_bound)
4087 new_selection_bound += n_chars;
4089 if (priv->position != new_position || priv->selection_bound != new_selection_bound)
4090 clutter_text_set_positions (self, new_position, new_selection_bound);
4093 n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars;
4094 g_signal_emit (self, text_signals[INSERT_TEXT], 0, chars,
4095 n_bytes, &position);
4097 /* TODO: What are we supposed to with the out value of position? */
4101 buffer_deleted_text (ClutterTextBuffer *buffer,
4106 ClutterTextPrivate *priv;
4108 gint new_selection_bound;
4111 if (priv->position >= 0 || priv->selection_bound >= 0)
4113 new_position = priv->position;
4114 new_selection_bound = priv->selection_bound;
4116 if (position < new_position)
4117 new_position -= n_chars;
4118 if (position < new_selection_bound)
4119 new_selection_bound -= n_chars;
4121 if (priv->position != new_position || priv->selection_bound != new_selection_bound)
4122 clutter_text_set_positions (self, new_position, new_selection_bound);
4125 g_signal_emit (self, text_signals[DELETE_TEXT], 0, position, position + n_chars);
4129 buffer_notify_text (ClutterTextBuffer *buffer,
4133 g_object_freeze_notify (G_OBJECT (self));
4135 clutter_text_dirty_cache (self);
4137 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
4139 g_signal_emit (self, text_signals[TEXT_CHANGED], 0);
4140 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT]);
4142 g_object_thaw_notify (G_OBJECT (self));
4146 buffer_notify_max_length (ClutterTextBuffer *buffer,
4150 g_object_notify (G_OBJECT (self), "max-length");
4154 buffer_connect_signals (ClutterText *self)
4156 ClutterTextPrivate *priv = self->priv;
4157 g_signal_connect (priv->buffer, "inserted-text", G_CALLBACK (buffer_inserted_text), self);
4158 g_signal_connect (priv->buffer, "deleted-text", G_CALLBACK (buffer_deleted_text), self);
4159 g_signal_connect (priv->buffer, "notify::text", G_CALLBACK (buffer_notify_text), self);
4160 g_signal_connect (priv->buffer, "notify::max-length", G_CALLBACK (buffer_notify_max_length), self);
4164 buffer_disconnect_signals (ClutterText *self)
4166 ClutterTextPrivate *priv = self->priv;
4167 g_signal_handlers_disconnect_by_func (priv->buffer, buffer_inserted_text, self);
4168 g_signal_handlers_disconnect_by_func (priv->buffer, buffer_deleted_text, self);
4169 g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_text, self);
4170 g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_max_length, self);
4174 * clutter_text_new_with_buffer:
4175 * @buffer: The buffer to use for the new #ClutterText.
4177 * Creates a new entry with the specified text buffer.
4179 * Return value: a new #ClutterText
4184 clutter_text_new_with_buffer (ClutterTextBuffer *buffer)
4186 g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), NULL);
4187 return g_object_new (CLUTTER_TYPE_TEXT, "buffer", buffer, NULL);
4191 * clutter_text_get_buffer:
4192 * @self: a #ClutterText
4194 * Get the #ClutterTextBuffer object which holds the text for
4197 * Returns: (transfer none): A #GtkEntryBuffer object.
4202 clutter_text_get_buffer (ClutterText *self)
4204 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4206 return get_buffer (self);
4210 * clutter_text_set_buffer:
4211 * @self: a #ClutterText
4212 * @buffer: a #ClutterTextBuffer
4214 * Set the #ClutterTextBuffer object which holds the text for
4220 clutter_text_set_buffer (ClutterText *self,
4221 ClutterTextBuffer *buffer)
4223 ClutterTextPrivate *priv;
4226 g_return_if_fail (CLUTTER_IS_TEXT (self));
4232 g_return_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer));
4233 g_object_ref (buffer);
4238 buffer_disconnect_signals (self);
4239 g_object_unref (priv->buffer);
4242 priv->buffer = buffer;
4245 buffer_connect_signals (self);
4247 obj = G_OBJECT (self);
4248 g_object_freeze_notify (obj);
4249 g_object_notify (obj, "buffer");
4250 g_object_notify (obj, "text");
4251 g_object_notify (obj, "max-length");
4252 g_object_thaw_notify (obj);
4256 * clutter_text_set_editable:
4257 * @self: a #ClutterText
4258 * @editable: whether the #ClutterText should be editable
4260 * Sets whether the #ClutterText actor should be editable.
4262 * An editable #ClutterText with key focus set using
4263 * clutter_actor_grab_key_focus() or clutter_stage_set_key_focus()
4264 * will receive key events and will update its contents accordingly.
4269 clutter_text_set_editable (ClutterText *self,
4272 ClutterTextPrivate *priv;
4274 g_return_if_fail (CLUTTER_IS_TEXT (self));
4278 if (priv->editable != editable)
4280 priv->editable = editable;
4282 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4284 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EDITABLE]);
4289 * clutter_text_get_editable:
4290 * @self: a #ClutterText
4292 * Retrieves whether a #ClutterText is editable or not.
4294 * Return value: %TRUE if the actor is editable
4299 clutter_text_get_editable (ClutterText *self)
4301 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
4303 return self->priv->editable;
4307 * clutter_text_set_selectable:
4308 * @self: a #ClutterText
4309 * @selectable: whether the #ClutterText actor should be selectable
4311 * Sets whether a #ClutterText actor should be selectable.
4313 * A selectable #ClutterText will allow selecting its contents using
4314 * the pointer or the keyboard.
4319 clutter_text_set_selectable (ClutterText *self,
4320 gboolean selectable)
4322 ClutterTextPrivate *priv;
4324 g_return_if_fail (CLUTTER_IS_TEXT (self));
4328 if (priv->selectable != selectable)
4330 priv->selectable = selectable;
4332 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4334 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTABLE]);
4339 * clutter_text_get_selectable:
4340 * @self: a #ClutterText
4342 * Retrieves whether a #ClutterText is selectable or not.
4344 * Return value: %TRUE if the actor is selectable
4349 clutter_text_get_selectable (ClutterText *self)
4351 g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
4353 return self->priv->selectable;
4357 * clutter_text_set_activatable:
4358 * @self: a #ClutterText
4359 * @activatable: whether the #ClutterText actor should be activatable
4361 * Sets whether a #ClutterText actor should be activatable.
4363 * An activatable #ClutterText actor will emit the #ClutterText::activate
4364 * signal whenever the 'Enter' (or 'Return') key is pressed; if it is not
4365 * activatable, a new line will be appended to the current content.
4367 * An activatable #ClutterText must also be set as editable using
4368 * clutter_text_set_editable().
4373 clutter_text_set_activatable (ClutterText *self,
4374 gboolean activatable)
4376 ClutterTextPrivate *priv;
4378 g_return_if_fail (CLUTTER_IS_TEXT (self));
4382 if (priv->activatable != activatable)
4384 priv->activatable = activatable;
4386 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4388 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]);
4393 * clutter_text_get_activatable:
4394 * @self: a #ClutterText
4396 * Retrieves whether a #ClutterText is activatable or not.
4398 * Return value: %TRUE if the actor is activatable
4403 clutter_text_get_activatable (ClutterText *self)
4405 g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
4407 return self->priv->activatable;
4411 * clutter_text_activate:
4412 * @self: a #ClutterText
4414 * Emits the #ClutterText::activate signal, if @self has been set
4415 * as activatable using clutter_text_set_activatable().
4417 * This function can be used to emit the ::activate signal inside
4418 * a #ClutterActor::captured-event or #ClutterActor::key-press-event
4419 * signal handlers before the default signal handler for the
4420 * #ClutterText is invoked.
4422 * Return value: %TRUE if the ::activate signal has been emitted,
4423 * and %FALSE otherwise
4428 clutter_text_activate (ClutterText *self)
4430 ClutterTextPrivate *priv;
4432 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
4436 if (priv->activatable)
4438 g_signal_emit (self, text_signals[ACTIVATE], 0);
4446 * clutter_text_set_cursor_visible:
4447 * @self: a #ClutterText
4448 * @cursor_visible: whether the cursor should be visible
4450 * Sets whether the cursor of a #ClutterText actor should be
4453 * The color of the cursor will be the same as the text color
4454 * unless clutter_text_set_cursor_color() has been called.
4456 * The size of the cursor can be set using clutter_text_set_cursor_size().
4458 * The position of the cursor can be changed programmatically using
4459 * clutter_text_set_cursor_position().
4464 clutter_text_set_cursor_visible (ClutterText *self,
4465 gboolean cursor_visible)
4467 ClutterTextPrivate *priv;
4469 g_return_if_fail (CLUTTER_IS_TEXT (self));
4473 if (priv->cursor_visible != cursor_visible)
4475 priv->cursor_visible = cursor_visible;
4477 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4479 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_VISIBLE]);
4484 * clutter_text_get_cursor_visible:
4485 * @self: a #ClutterText
4487 * Retrieves whether the cursor of a #ClutterText actor is visible.
4489 * Return value: %TRUE if the cursor is visible
4494 clutter_text_get_cursor_visible (ClutterText *self)
4496 g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
4498 return self->priv->cursor_visible;
4502 * clutter_text_set_cursor_color:
4503 * @self: a #ClutterText
4504 * @color: (allow-none): the color of the cursor, or %NULL to unset it
4506 * Sets the color of the cursor of a #ClutterText actor.
4508 * If @color is %NULL, the cursor color will be the same as the
4514 clutter_text_set_cursor_color (ClutterText *self,
4515 const ClutterColor *color)
4517 g_return_if_fail (CLUTTER_IS_TEXT (self));
4519 clutter_text_set_color_animated (self, obj_props[PROP_CURSOR_COLOR], color);
4523 * clutter_text_get_cursor_color:
4524 * @self: a #ClutterText
4525 * @color: (out): return location for a #ClutterColor
4527 * Retrieves the color of the cursor of a #ClutterText actor.
4532 clutter_text_get_cursor_color (ClutterText *self,
4533 ClutterColor *color)
4535 ClutterTextPrivate *priv;
4537 g_return_if_fail (CLUTTER_IS_TEXT (self));
4538 g_return_if_fail (color != NULL);
4542 *color = priv->cursor_color;
4546 * clutter_text_set_selection:
4547 * @self: a #ClutterText
4548 * @start_pos: start of the selection, in characters
4549 * @end_pos: end of the selection, in characters
4551 * Selects the region of text between @start_pos and @end_pos.
4553 * This function changes the position of the cursor to match
4554 * @start_pos and the selection bound to match @end_pos.
4559 clutter_text_set_selection (ClutterText *self,
4565 g_return_if_fail (CLUTTER_IS_TEXT (self));
4567 n_chars = clutter_text_buffer_get_length (get_buffer (self));
4571 start_pos = MIN (n_chars, start_pos);
4572 end_pos = MIN (n_chars, end_pos);
4574 clutter_text_set_positions (self, start_pos, end_pos);
4578 * clutter_text_get_selection:
4579 * @self: a #ClutterText
4581 * Retrieves the currently selected text.
4583 * Return value: a newly allocated string containing the currently
4584 * selected text, or %NULL. Use g_free() to free the returned
4590 clutter_text_get_selection (ClutterText *self)
4592 ClutterTextPrivate *priv;
4595 gint start_index, end_index;
4596 gint start_offset, end_offset;
4599 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4603 start_index = priv->position;
4604 end_index = priv->selection_bound;
4606 if (end_index == start_index)
4607 return g_strdup ("");
4609 if ((end_index != -1 && end_index < start_index) ||
4612 gint temp = start_index;
4613 start_index = end_index;
4617 text = clutter_text_buffer_get_text (get_buffer (self));
4618 start_offset = offset_to_bytes (text, start_index);
4619 end_offset = offset_to_bytes (text, end_index);
4620 len = end_offset - start_offset;
4622 str = g_malloc (len + 1);
4623 g_utf8_strncpy (str, text + start_offset, end_index - start_index);
4629 * clutter_text_set_selection_bound:
4630 * @self: a #ClutterText
4631 * @selection_bound: the position of the end of the selection, in characters
4633 * Sets the other end of the selection, starting from the current
4636 * If @selection_bound is -1, the selection unset.
4641 clutter_text_set_selection_bound (ClutterText *self,
4642 gint selection_bound)
4644 ClutterTextPrivate *priv;
4646 g_return_if_fail (CLUTTER_IS_TEXT (self));
4650 if (priv->selection_bound != selection_bound)
4652 gint len = clutter_text_buffer_get_length (get_buffer (self));;
4654 if (selection_bound < 0 || selection_bound >= len)
4655 priv->selection_bound = -1;
4657 priv->selection_bound = selection_bound;
4659 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4661 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
4666 * clutter_text_get_selection_bound:
4667 * @self: a #ClutterText
4669 * Retrieves the other end of the selection of a #ClutterText actor,
4670 * in characters from the current cursor position.
4672 * Return value: the position of the other end of the selection
4677 clutter_text_get_selection_bound (ClutterText *self)
4679 g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1);
4681 return self->priv->selection_bound;
4685 * clutter_text_set_selection_color:
4686 * @self: a #ClutterText
4687 * @color: (allow-none): the color of the selection, or %NULL to unset it
4689 * Sets the color of the selection of a #ClutterText actor.
4691 * If @color is %NULL, the selection color will be the same as the
4692 * cursor color, or if no cursor color is set either then it will be
4693 * the same as the text color.
4698 clutter_text_set_selection_color (ClutterText *self,
4699 const ClutterColor *color)
4701 g_return_if_fail (CLUTTER_IS_TEXT (self));
4703 clutter_text_set_color_animated (self, obj_props[PROP_SELECTION_COLOR],
4708 * clutter_text_get_selection_color:
4709 * @self: a #ClutterText
4710 * @color: (out caller-allocates): return location for a #ClutterColor
4712 * Retrieves the color of the selection of a #ClutterText actor.
4717 clutter_text_get_selection_color (ClutterText *self,
4718 ClutterColor *color)
4720 ClutterTextPrivate *priv;
4722 g_return_if_fail (CLUTTER_IS_TEXT (self));
4723 g_return_if_fail (color != NULL);
4727 *color = priv->selection_color;
4731 * clutter_text_set_selected_text_color:
4732 * @self: a #ClutterText
4733 * @color: (allow-none): the selected text color, or %NULL to unset it
4735 * Sets the selected text color of a #ClutterText actor.
4737 * If @color is %NULL, the selected text color will be the same as the
4738 * selection color, which then falls back to cursor, and then text color.
4743 clutter_text_set_selected_text_color (ClutterText *self,
4744 const ClutterColor *color)
4746 g_return_if_fail (CLUTTER_IS_TEXT (self));
4748 clutter_text_set_color_animated (self, obj_props[PROP_SELECTED_TEXT_COLOR],
4753 * clutter_text_get_selected_text_color:
4754 * @self: a #ClutterText
4755 * @color: (out caller-allocates): return location for a #ClutterColor
4757 * Retrieves the color of selected text of a #ClutterText actor.
4762 clutter_text_get_selected_text_color (ClutterText *self,
4763 ClutterColor *color)
4765 ClutterTextPrivate *priv;
4767 g_return_if_fail (CLUTTER_IS_TEXT (self));
4768 g_return_if_fail (color != NULL);
4772 *color = priv->selected_text_color;
4776 * clutter_text_set_font_description:
4777 * @self: a #ClutterText
4778 * @font_desc: a #PangoFontDescription
4780 * Sets @font_desc as the font description for a #ClutterText
4782 * The #PangoFontDescription is copied by the #ClutterText actor
4783 * so you can safely call pango_font_description_free() on it after
4784 * calling this function.
4789 clutter_text_set_font_description (ClutterText *self,
4790 PangoFontDescription *font_desc)
4792 PangoFontDescription *copy;
4794 g_return_if_fail (CLUTTER_IS_TEXT (self));
4796 copy = pango_font_description_copy (font_desc);
4797 clutter_text_set_font_description_internal (self, copy);
4801 * clutter_text_get_font_description:
4802 * @self: a #ClutterText
4804 * Retrieves the #PangoFontDescription used by @self
4806 * Return value: a #PangoFontDescription. The returned value is owned
4807 * by the #ClutterText actor and it should not be modified or freed
4811 PangoFontDescription *
4812 clutter_text_get_font_description (ClutterText *self)
4814 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4816 return self->priv->font_desc;
4820 * clutter_text_get_font_name:
4821 * @self: a #ClutterText
4823 * Retrieves the font name as set by clutter_text_set_font_name().
4825 * Return value: a string containing the font name. The returned
4826 * string is owned by the #ClutterText actor and should not be
4832 clutter_text_get_font_name (ClutterText *text)
4834 g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL);
4836 return text->priv->font_name;
4840 * clutter_text_set_font_name:
4841 * @self: a #ClutterText
4842 * @font_name: (allow-none): a font name, or %NULL to set the default font name
4844 * Sets the font used by a #ClutterText. The @font_name string
4845 * must either be %NULL, which means that the font name from the
4846 * default #ClutterBackend will be used; or be something that can
4847 * be parsed by the pango_font_description_from_string() function,
4851 * clutter_text_set_font_name (text, "Sans 10pt");
4852 * clutter_text_set_font_name (text, "Serif 16px");
4853 * clutter_text_set_font_name (text, "Helvetica 10");
4859 clutter_text_set_font_name (ClutterText *self,
4860 const gchar *font_name)
4862 ClutterTextPrivate *priv;
4863 PangoFontDescription *desc;
4864 gboolean is_default_font;
4866 g_return_if_fail (CLUTTER_IS_TEXT (self));
4868 /* get the default font name from the backend */
4869 if (font_name == NULL || font_name[0] == '\0')
4871 ClutterSettings *settings = clutter_settings_get_default ();
4872 gchar *default_font_name = NULL;
4874 g_object_get (settings, "font-name", &default_font_name, NULL);
4876 if (default_font_name != NULL)
4877 font_name = default_font_name;
4881 default_font_name = g_strdup ("Sans 12");
4884 is_default_font = TRUE;
4887 is_default_font = FALSE;
4891 if (g_strcmp0 (priv->font_name, font_name) == 0)
4894 desc = pango_font_description_from_string (font_name);
4897 g_warning ("Attempting to create a PangoFontDescription for "
4898 "font name '%s', but failed.",
4903 /* this will set the font_name field as well */
4904 clutter_text_set_font_description_internal (self, desc);
4905 priv->is_default_font = is_default_font;
4907 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_NAME]);
4910 if (is_default_font)
4911 g_free ((gchar *) font_name);
4915 * clutter_text_get_text:
4916 * @self: a #ClutterText
4918 * Retrieves a pointer to the current contents of a #ClutterText
4921 * If you need a copy of the contents for manipulating, either
4922 * use g_strdup() on the returned string, or use:
4925 * copy = clutter_text_get_chars (text, 0, -1);
4928 * Which will return a newly allocated string.
4930 * If the #ClutterText actor is empty, this function will return
4931 * an empty string, and not %NULL.
4933 * Return value: (transfer none): the contents of the actor. The returned
4934 * string is owned by the #ClutterText actor and should never be modified
4940 clutter_text_get_text (ClutterText *self)
4942 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4944 return clutter_text_buffer_get_text (get_buffer (self));
4948 clutter_text_set_use_markup_internal (ClutterText *self,
4949 gboolean use_markup)
4951 ClutterTextPrivate *priv = self->priv;
4953 if (priv->use_markup != use_markup)
4955 priv->use_markup = use_markup;
4957 /* reset the attributes lists so that they can be
4960 if (priv->effective_attrs != NULL)
4962 pango_attr_list_unref (priv->effective_attrs);
4963 priv->effective_attrs = NULL;
4966 if (priv->markup_attrs)
4968 pango_attr_list_unref (priv->markup_attrs);
4969 priv->markup_attrs = NULL;
4972 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_USE_MARKUP]);
4977 * clutter_text_set_text:
4978 * @self: a #ClutterText
4979 * @text: (allow-none): the text to set. Passing %NULL is the same
4980 * as passing "" (the empty string)
4982 * Sets the contents of a #ClutterText actor.
4984 * If the #ClutterText:use-markup property was set to %TRUE it
4985 * will be reset to %FALSE as a side effect. If you want to
4986 * maintain the #ClutterText:use-markup you should use the
4987 * clutter_text_set_markup() function instead
4992 clutter_text_set_text (ClutterText *self,
4995 g_return_if_fail (CLUTTER_IS_TEXT (self));
4997 /* if the text is editable (i.e. there is not markup flag to reset) then
4998 * changing the contents will result in selection and cursor changes that
5001 if (self->priv->editable)
5003 if (g_strcmp0 (clutter_text_buffer_get_text (get_buffer (self)), text) == 0)
5007 clutter_text_set_use_markup_internal (self, FALSE);
5008 clutter_text_buffer_set_text (get_buffer (self), text ? text : "", -1);
5012 * clutter_text_set_markup:
5013 * @self: a #ClutterText
5014 * @markup: (allow-none): a string containing Pango markup.
5015 * Passing %NULL is the same as passing "" (the empty string)
5017 * Sets @markup as the contents of a #ClutterText.
5019 * This is a convenience function for setting a string containing
5020 * Pango markup, and it is logically equivalent to:
5023 * /* the order is important */
5024 * clutter_text_set_text (CLUTTER_TEXT (actor), markup);
5025 * clutter_text_set_use_markup (CLUTTER_TEXT (actor), TRUE);
5031 clutter_text_set_markup (ClutterText *self,
5032 const gchar *markup)
5034 g_return_if_fail (CLUTTER_IS_TEXT (self));
5036 clutter_text_set_use_markup_internal (self, TRUE);
5037 if (markup != NULL && *markup != '\0')
5038 clutter_text_set_markup_internal (self, markup);
5040 clutter_text_buffer_set_text (get_buffer (self), "", 0);
5044 * clutter_text_get_layout:
5045 * @self: a #ClutterText
5047 * Retrieves the current #PangoLayout used by a #ClutterText actor.
5049 * Return value: (transfer none): a #PangoLayout. The returned object is owned by
5050 * the #ClutterText actor and should not be modified or freed
5055 clutter_text_get_layout (ClutterText *self)
5057 gfloat width, height;
5059 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
5061 if (self->priv->editable && self->priv->single_line_mode)
5062 return clutter_text_create_layout (self, -1, -1);
5064 clutter_actor_get_size (CLUTTER_ACTOR (self), &width, &height);
5066 return clutter_text_create_layout (self, width, height);
5070 * clutter_text_set_color:
5071 * @self: a #ClutterText
5072 * @color: a #ClutterColor
5074 * Sets the color of the contents of a #ClutterText actor.
5076 * The overall opacity of the #ClutterText actor will be the
5077 * result of the alpha value of @color and the composited
5078 * opacity of the actor itself on the scenegraph, as returned
5079 * by clutter_actor_get_paint_opacity().
5084 clutter_text_set_color (ClutterText *self,
5085 const ClutterColor *color)
5087 g_return_if_fail (CLUTTER_IS_TEXT (self));
5088 g_return_if_fail (color != NULL);
5090 clutter_text_set_color_animated (self, obj_props[PROP_COLOR], color);
5094 * clutter_text_get_color:
5095 * @self: a #ClutterText
5096 * @color: (out caller-allocates): return location for a #ClutterColor
5098 * Retrieves the text color as set by clutter_text_set_color().
5103 clutter_text_get_color (ClutterText *self,
5104 ClutterColor *color)
5106 ClutterTextPrivate *priv;
5108 g_return_if_fail (CLUTTER_IS_TEXT (self));
5109 g_return_if_fail (color != NULL);
5113 *color = priv->text_color;
5117 * clutter_text_set_ellipsize:
5118 * @self: a #ClutterText
5119 * @mode: a #PangoEllipsizeMode
5121 * Sets the mode used to ellipsize (add an ellipsis: "...") to the
5122 * text if there is not enough space to render the entire contents
5123 * of a #ClutterText actor
5128 clutter_text_set_ellipsize (ClutterText *self,
5129 PangoEllipsizeMode mode)
5131 ClutterTextPrivate *priv;
5133 g_return_if_fail (CLUTTER_IS_TEXT (self));
5134 g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE &&
5135 mode <= PANGO_ELLIPSIZE_END);
5139 if ((PangoEllipsizeMode) priv->ellipsize != mode)
5141 priv->ellipsize = mode;
5143 clutter_text_dirty_cache (self);
5145 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5147 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ELLIPSIZE]);
5152 * clutter_text_get_ellipsize:
5153 * @self: a #ClutterText
5155 * Returns the ellipsizing position of a #ClutterText actor, as
5156 * set by clutter_text_set_ellipsize().
5158 * Return value: #PangoEllipsizeMode
5163 clutter_text_get_ellipsize (ClutterText *self)
5165 g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ELLIPSIZE_NONE);
5167 return self->priv->ellipsize;
5171 * clutter_text_get_line_wrap:
5172 * @self: a #ClutterText
5174 * Retrieves the value set using clutter_text_set_line_wrap().
5176 * Return value: %TRUE if the #ClutterText actor should wrap
5182 clutter_text_get_line_wrap (ClutterText *self)
5184 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5186 return self->priv->wrap;
5190 * clutter_text_set_line_wrap:
5191 * @self: a #ClutterText
5192 * @line_wrap: whether the contents should wrap
5194 * Sets whether the contents of a #ClutterText actor should wrap,
5195 * if they don't fit the size assigned to the actor.
5200 clutter_text_set_line_wrap (ClutterText *self,
5203 ClutterTextPrivate *priv;
5205 g_return_if_fail (CLUTTER_IS_TEXT (self));
5209 if (priv->wrap != line_wrap)
5211 priv->wrap = line_wrap;
5213 clutter_text_dirty_cache (self);
5215 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5217 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_WRAP]);
5222 * clutter_text_set_line_wrap_mode:
5223 * @self: a #ClutterText
5224 * @wrap_mode: the line wrapping mode
5226 * If line wrapping is enabled (see clutter_text_set_line_wrap()) this
5227 * function controls how the line wrapping is performed. The default is
5228 * %PANGO_WRAP_WORD which means wrap on word boundaries.
5233 clutter_text_set_line_wrap_mode (ClutterText *self,
5234 PangoWrapMode wrap_mode)
5236 ClutterTextPrivate *priv;
5238 g_return_if_fail (CLUTTER_IS_TEXT (self));
5242 if (priv->wrap_mode != wrap_mode)
5244 priv->wrap_mode = wrap_mode;
5246 clutter_text_dirty_cache (self);
5248 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5250 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_WRAP_MODE]);
5255 * clutter_text_get_line_wrap_mode:
5256 * @self: a #ClutterText
5258 * Retrieves the line wrap mode used by the #ClutterText actor.
5260 * See clutter_text_set_line_wrap_mode ().
5262 * Return value: the wrap mode used by the #ClutterText
5267 clutter_text_get_line_wrap_mode (ClutterText *self)
5269 g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_WRAP_WORD);
5271 return self->priv->wrap_mode;
5275 * clutter_text_set_attributes:
5276 * @self: a #ClutterText
5277 * @attrs: (allow-none): a #PangoAttrList or %NULL to unset the attributes
5279 * Sets the attributes list that are going to be applied to the
5280 * #ClutterText contents.
5282 * The #ClutterText actor will take a reference on the #PangoAttrList
5283 * passed to this function.
5288 clutter_text_set_attributes (ClutterText *self,
5289 PangoAttrList *attrs)
5291 ClutterTextPrivate *priv;
5293 g_return_if_fail (CLUTTER_IS_TEXT (self));
5298 pango_attr_list_ref (attrs);
5301 pango_attr_list_unref (priv->attrs);
5303 priv->attrs = attrs;
5305 /* Clear the effective attributes so they will be regenerated when a
5306 layout is created */
5307 if (priv->effective_attrs)
5309 pango_attr_list_unref (priv->effective_attrs);
5310 priv->effective_attrs = NULL;
5313 clutter_text_dirty_cache (self);
5315 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ATTRIBUTES]);
5317 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5321 * clutter_text_get_attributes:
5322 * @self: a #ClutterText
5324 * Gets the attribute list that was set on the #ClutterText actor
5325 * clutter_text_set_attributes(), if any.
5327 * Return value: (transfer none): the attribute list, or %NULL if none was set. The
5328 * returned value is owned by the #ClutterText and should not be unreferenced.
5333 clutter_text_get_attributes (ClutterText *self)
5335 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
5337 return self->priv->attrs;
5341 * clutter_text_set_line_alignment:
5342 * @self: a #ClutterText
5343 * @alignment: A #PangoAlignment
5345 * Sets the way that the lines of a wrapped label are aligned with
5346 * respect to each other. This does not affect the overall alignment
5347 * of the label within its allocated or specified width.
5349 * To align a #ClutterText actor you should add it to a container
5350 * that supports alignment, or use the anchor point.
5355 clutter_text_set_line_alignment (ClutterText *self,
5356 PangoAlignment alignment)
5358 ClutterTextPrivate *priv;
5360 g_return_if_fail (CLUTTER_IS_TEXT (self));
5364 if (priv->alignment != alignment)
5366 priv->alignment = alignment;
5368 clutter_text_dirty_cache (self);
5370 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5372 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_ALIGNMENT]);
5377 * clutter_text_get_line_alignment:
5378 * @self: a #ClutterText
5380 * Retrieves the alignment of a #ClutterText, as set by
5381 * clutter_text_set_line_alignment().
5383 * Return value: a #PangoAlignment
5388 clutter_text_get_line_alignment (ClutterText *self)
5390 g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ALIGN_LEFT);
5392 return self->priv->alignment;
5396 * clutter_text_set_use_markup:
5397 * @self: a #ClutterText
5398 * @setting: %TRUE if the text should be parsed for markup.
5400 * Sets whether the contents of the #ClutterText actor contains markup
5401 * in <link linkend="PangoMarkupFormat">Pango's text markup language</link>.
5403 * Setting #ClutterText:use-markup on an editable #ClutterText will
5404 * not have any effect except hiding the markup.
5406 * See also #ClutterText:use-markup.
5411 clutter_text_set_use_markup (ClutterText *self,
5416 g_return_if_fail (CLUTTER_IS_TEXT (self));
5418 text = clutter_text_buffer_get_text (get_buffer (self));
5420 clutter_text_set_use_markup_internal (self, setting);
5423 clutter_text_set_markup_internal (self, text);
5425 clutter_text_dirty_cache (self);
5427 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5431 * clutter_text_get_use_markup:
5432 * @self: a #ClutterText
5434 * Retrieves whether the contents of the #ClutterText actor should be
5435 * parsed for the Pango text markup.
5437 * Return value: %TRUE if the contents will be parsed for markup
5442 clutter_text_get_use_markup (ClutterText *self)
5444 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5446 return self->priv->use_markup;
5450 * clutter_text_set_justify:
5451 * @self: a #ClutterText
5452 * @justify: whether the text should be justified
5454 * Sets whether the text of the #ClutterText actor should be justified
5455 * on both margins. This setting is ignored if Clutter is compiled
5456 * against Pango < 1.18.
5461 clutter_text_set_justify (ClutterText *self,
5464 ClutterTextPrivate *priv;
5466 g_return_if_fail (CLUTTER_IS_TEXT (self));
5470 if (priv->justify != justify)
5472 priv->justify = justify;
5474 clutter_text_dirty_cache (self);
5476 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5478 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_JUSTIFY]);
5483 * clutter_text_get_justify:
5484 * @self: a #ClutterText
5486 * Retrieves whether the #ClutterText actor should justify its contents
5489 * Return value: %TRUE if the text should be justified
5494 clutter_text_get_justify (ClutterText *self)
5496 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5498 return self->priv->justify;
5502 * clutter_text_get_cursor_position:
5503 * @self: a #ClutterText
5505 * Retrieves the cursor position.
5507 * Return value: the cursor position, in characters
5512 clutter_text_get_cursor_position (ClutterText *self)
5514 g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1);
5516 return self->priv->position;
5520 * clutter_text_set_cursor_position:
5521 * @self: a #ClutterText
5522 * @position: the new cursor position, in characters
5524 * Sets the cursor of a #ClutterText actor at @position.
5526 * The position is expressed in characters, not in bytes.
5531 clutter_text_set_cursor_position (ClutterText *self,
5534 ClutterTextPrivate *priv;
5537 g_return_if_fail (CLUTTER_IS_TEXT (self));
5541 if (priv->position == position)
5544 len = clutter_text_buffer_get_length (get_buffer (self));
5546 if (position < 0 || position >= len)
5547 priv->position = -1;
5549 priv->position = position;
5551 /* Forget the target x position so that it will be recalculated next
5552 time the cursor is moved up or down */
5555 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
5557 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]);
5561 * clutter_text_set_cursor_size:
5562 * @self: a #ClutterText
5563 * @size: the size of the cursor, in pixels, or -1 to use the
5566 * Sets the size of the cursor of a #ClutterText. The cursor
5567 * will only be visible if the #ClutterText:cursor-visible property
5573 clutter_text_set_cursor_size (ClutterText *self,
5576 ClutterTextPrivate *priv;
5578 g_return_if_fail (CLUTTER_IS_TEXT (self));
5582 if (priv->cursor_size != size)
5585 size = DEFAULT_CURSOR_SIZE;
5587 priv->cursor_size = size;
5589 clutter_text_queue_redraw (CLUTTER_ACTOR (self));
5591 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_SIZE]);
5596 * clutter_text_get_cursor_size:
5597 * @self: a #ClutterText
5599 * Retrieves the size of the cursor of a #ClutterText actor.
5601 * Return value: the size of the cursor, in pixels
5606 clutter_text_get_cursor_size (ClutterText *self)
5608 g_return_val_if_fail (CLUTTER_IS_TEXT (self), DEFAULT_CURSOR_SIZE);
5610 return self->priv->cursor_size;
5614 * clutter_text_set_password_char:
5615 * @self: a #ClutterText
5616 * @wc: a Unicode character, or 0 to unset the password character
5618 * Sets the character to use in place of the actual text in a
5619 * password text actor.
5621 * If @wc is 0 the text will be displayed as it is entered in the
5622 * #ClutterText actor.
5627 clutter_text_set_password_char (ClutterText *self,
5630 ClutterTextPrivate *priv;
5632 g_return_if_fail (CLUTTER_IS_TEXT (self));
5636 if (priv->password_char != wc)
5638 priv->password_char = wc;
5640 clutter_text_dirty_cache (self);
5641 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5643 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PASSWORD_CHAR]);
5648 * clutter_text_get_password_char:
5649 * @self: a #ClutterText
5651 * Retrieves the character to use in place of the actual text
5652 * as set by clutter_text_set_password_char().
5654 * Return value: a Unicode character or 0 if the password
5655 * character is not set
5660 clutter_text_get_password_char (ClutterText *self)
5662 g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
5664 return self->priv->password_char;
5668 * clutter_text_set_max_length:
5669 * @self: a #ClutterText
5670 * @max: the maximum number of characters allowed in the text actor; 0
5671 * to disable or -1 to set the length of the current string
5673 * Sets the maximum allowed length of the contents of the actor. If the
5674 * current contents are longer than the given length, then they will be
5680 clutter_text_set_max_length (ClutterText *self,
5683 g_return_if_fail (CLUTTER_IS_TEXT (self));
5684 clutter_text_buffer_set_max_length (get_buffer (self), max);
5688 * clutter_text_get_max_length:
5689 * @self: a #ClutterText
5691 * Gets the maximum length of text that can be set into a text actor.
5693 * See clutter_text_set_max_length().
5695 * Return value: the maximum number of characters.
5700 clutter_text_get_max_length (ClutterText *self)
5702 g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
5704 return clutter_text_buffer_get_max_length (get_buffer (self));
5708 * clutter_text_insert_unichar:
5709 * @self: a #ClutterText
5710 * @wc: a Unicode character
5712 * Inserts @wc at the current cursor position of a
5713 * #ClutterText actor.
5718 clutter_text_insert_unichar (ClutterText *self,
5721 ClutterTextPrivate *priv;
5726 new = g_string_new ("");
5727 g_string_append_unichar (new, wc);
5729 clutter_text_buffer_insert_text (get_buffer (self), priv->position, new->str, 1);
5731 g_string_free (new, TRUE);
5735 * clutter_text_insert_text:
5736 * @self: a #ClutterText
5737 * @text: the text to be inserted
5738 * @position: the position of the insertion, or -1
5740 * Inserts @text into a #ClutterActor at the given position.
5742 * If @position is a negative number, the text will be appended
5743 * at the end of the current contents of the #ClutterText.
5745 * The position is expressed in characters, not in bytes.
5750 clutter_text_insert_text (ClutterText *self,
5754 g_return_if_fail (CLUTTER_IS_TEXT (self));
5755 g_return_if_fail (text != NULL);
5757 clutter_text_buffer_insert_text (get_buffer (self), position, text,
5758 g_utf8_strlen (text, -1));
5762 * clutter_text_delete_text:
5763 * @self: a #ClutterText
5764 * @start_pos: starting position
5765 * @end_pos: ending position
5767 * Deletes the text inside a #ClutterText actor between @start_pos
5770 * The starting and ending positions are expressed in characters,
5776 clutter_text_delete_text (ClutterText *self,
5780 g_return_if_fail (CLUTTER_IS_TEXT (self));
5782 clutter_text_buffer_delete_text (get_buffer (self), start_pos, end_pos - start_pos);
5786 * clutter_text_delete_chars:
5787 * @self: a #ClutterText
5788 * @n_chars: the number of characters to delete
5790 * Deletes @n_chars inside a #ClutterText actor, starting from the
5791 * current cursor position.
5793 * Somewhat awkwardly, the cursor position is decremented by the same
5794 * number of characters you've deleted.
5799 clutter_text_delete_chars (ClutterText *self,
5802 ClutterTextPrivate *priv;
5804 g_return_if_fail (CLUTTER_IS_TEXT (self));
5808 clutter_text_buffer_delete_text (get_buffer (self), priv->position, n_chars);
5810 if (priv->position > 0)
5811 clutter_text_set_cursor_position (self, priv->position - n_chars);
5815 * clutter_text_get_chars:
5816 * @self: a #ClutterText
5817 * @start_pos: start of text, in characters
5818 * @end_pos: end of text, in characters
5820 * Retrieves the contents of the #ClutterText actor between
5821 * @start_pos and @end_pos, but not including @end_pos.
5823 * The positions are specified in characters, not in bytes.
5825 * Return value: a newly allocated string with the contents of
5826 * the text actor between the specified positions. Use g_free()
5827 * to free the resources when done
5832 clutter_text_get_chars (ClutterText *self,
5836 gint start_index, end_index;
5840 g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
5842 n_chars = clutter_text_buffer_get_length (get_buffer (self));
5843 text = clutter_text_buffer_get_text (get_buffer (self));
5848 start_pos = MIN (n_chars, start_pos);
5849 end_pos = MIN (n_chars, end_pos);
5851 start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
5852 end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
5854 return g_strndup (text + start_index, end_index - start_index);
5858 * clutter_text_set_single_line_mode:
5859 * @self: a #ClutterText
5860 * @single_line: whether to enable single line mode
5862 * Sets whether a #ClutterText actor should be in single line mode
5863 * or not. Only editable #ClutterText<!-- -->s can be in single line
5866 * A text actor in single line mode will not wrap text and will clip
5867 * the visible area to the predefined size. The contents of the
5868 * text actor will scroll to display the end of the text if its length
5869 * is bigger than the allocated width.
5871 * When setting the single line mode the #ClutterText:activatable
5872 * property is also set as a side effect. Instead of entering a new
5873 * line character, the text actor will emit the #ClutterText::activate
5879 clutter_text_set_single_line_mode (ClutterText *self,
5880 gboolean single_line)
5882 ClutterTextPrivate *priv;
5884 g_return_if_fail (CLUTTER_IS_TEXT (self));
5888 if (priv->single_line_mode != single_line)
5890 g_object_freeze_notify (G_OBJECT (self));
5892 priv->single_line_mode = single_line;
5894 if (priv->single_line_mode)
5896 priv->activatable = TRUE;
5898 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]);
5901 clutter_text_dirty_cache (self);
5902 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5904 g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SINGLE_LINE_MODE]);
5906 g_object_thaw_notify (G_OBJECT (self));
5911 * clutter_text_get_single_line_mode:
5912 * @self: a #ClutterText
5914 * Retrieves whether the #ClutterText actor is in single line mode.
5916 * Return value: %TRUE if the #ClutterText actor is in single line mode
5921 clutter_text_get_single_line_mode (ClutterText *self)
5923 g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5925 return self->priv->single_line_mode;
5929 * clutter_text_set_preedit_string:
5930 * @self: a #ClutterText
5931 * @preedit_str: (allow-none): the pre-edit string, or %NULL to unset it
5932 * @preedit_attrs: (allow-none): the pre-edit string attributes
5933 * @cursor_pos: the cursor position for the pre-edit string
5935 * Sets, or unsets, the pre-edit string. This function is useful
5936 * for input methods to display a string (with eventual specific
5937 * Pango attributes) before it is entered inside the #ClutterText
5940 * The preedit string and attributes are ignored if the #ClutterText
5941 * actor is not editable.
5943 * This function should not be used by applications
5948 clutter_text_set_preedit_string (ClutterText *self,
5949 const gchar *preedit_str,
5950 PangoAttrList *preedit_attrs,
5953 ClutterTextPrivate *priv;
5955 g_return_if_fail (CLUTTER_IS_TEXT (self));
5959 g_free (priv->preedit_str);
5960 priv->preedit_str = NULL;
5962 if (priv->preedit_attrs != NULL)
5964 pango_attr_list_unref (priv->preedit_attrs);
5965 priv->preedit_attrs = NULL;
5968 priv->preedit_n_chars = 0;
5969 priv->preedit_cursor_pos = 0;
5971 if (preedit_str == NULL || *preedit_str == '\0')
5972 priv->preedit_set = FALSE;
5975 priv->preedit_str = g_strdup (preedit_str);
5977 if (priv->preedit_str != NULL)
5978 priv->preedit_n_chars = g_utf8_strlen (priv->preedit_str, -1);
5980 priv->preedit_n_chars = 0;
5982 if (preedit_attrs != NULL)
5983 priv->preedit_attrs = pango_attr_list_ref (preedit_attrs);
5985 priv->preedit_cursor_pos =
5986 CLAMP (cursor_pos, 0, priv->preedit_n_chars);
5988 priv->preedit_set = TRUE;
5991 clutter_text_dirty_cache (self);
5992 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5997 * clutter_text_get_layout_offsets:
5998 * @self: a #ClutterText
5999 * @x: (out): location to store X offset of layout, or %NULL
6000 * @y: (out): location to store Y offset of layout, or %NULL
6002 * Obtains the coordinates where the #ClutterText will draw the #PangoLayout
6003 * representing the text.
6008 clutter_text_get_layout_offsets (ClutterText *self,
6012 ClutterTextPrivate *priv;
6014 g_return_if_fail (CLUTTER_IS_TEXT (self));