1d2da469dc85cbd53dceec63fcb8038a281932fd
[profile/ivi/clutter.git] / clutter / clutter-text.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2008  Intel Corporation.
7  *
8  * Authored By: Øyvind Kolås <pippin@o-hand.com>
9  *              Emmanuele Bassi <ebassi@linux.intel.com>
10  *
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.
15  *
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.
20  *
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/>.
23  */
24
25 /**
26  * SECTION:clutter-text
27  * @short_description: An actor for displaying and editing text
28  *
29  * #ClutterText is an actor that displays custom text using Pango
30  * as the text rendering engine.
31  *
32  * #ClutterText also allows inline editing of the text if the
33  * actor is set editable using clutter_text_set_editable().
34  *
35  * Selection using keyboard or pointers can be enabled using
36  * clutter_text_set_selectable().
37  *
38  * #ClutterText is available since Clutter 1.0
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include <string.h>
46 #include <math.h>
47
48 #include "clutter-text.h"
49
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"
66
67 /* cursor width in pixels */
68 #define DEFAULT_CURSOR_SIZE     2
69
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
76  *
77  * since we might get multiple queries from layout managers doing a
78  * double-pass allocations, like tabular ones, we should use 6 slots
79  */
80 #define N_CACHED_LAYOUTS        6
81
82 #define CLUTTER_TEXT_GET_PRIVATE(obj)   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate))
83
84 typedef struct _LayoutCache     LayoutCache;
85
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 };
90
91 static ClutterAnimatableIface *parent_animatable_iface = NULL;
92
93 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
94 static void clutter_animatable_iface_init (ClutterAnimatableIface *iface);
95
96 G_DEFINE_TYPE_WITH_CODE (ClutterText,
97                          clutter_text,
98                          CLUTTER_TYPE_ACTOR,
99                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
100                                                 clutter_scriptable_iface_init)
101                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
102                                                 clutter_animatable_iface_init));
103
104 struct _LayoutCache
105 {
106   /* Cached layout. Pango internally caches the computed extents
107    * when they are requested so there is no need to cache that as
108    * well
109    */
110   PangoLayout *layout;
111
112   /* A number representing the age of this cache (so that when a
113    * new layout is needed the last used cache is replaced)
114    */
115   guint age;
116 };
117
118 struct _ClutterTextPrivate
119 {
120   PangoFontDescription *font_desc;
121
122   /* the displayed text */
123   ClutterTextBuffer *buffer;
124
125   gchar *font_name;
126
127   gchar *preedit_str;
128
129   ClutterColor text_color;
130
131   LayoutCache cached_layouts[N_CACHED_LAYOUTS];
132   guint cache_age;
133
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
145      creating a layout */
146   PangoAttrList *preedit_attrs;
147
148   /* current cursor position */
149   gint position;
150
151   /* current 'other end of selection' position */
152   gint selection_bound;
153
154   /* the x position in the PangoLayout, used to
155    * avoid drifting when repeatedly moving up|down
156    */
157   gint x_pos;
158
159   /* the x position of the PangoLayout when in
160    * single line mode, to scroll the contents of the
161    * text actor
162    */
163   gint text_x;
164
165   /* the y position of the PangoLayout, fixed to 0 by
166    * default for now */
167   gint text_y;
168
169   /* Where to draw the cursor */
170   ClutterGeometry cursor_pos;
171   ClutterColor cursor_color;
172   guint cursor_size;
173
174   /* Box representing the paint volume. The box is lazily calculated
175      and cached */
176   ClutterPaintVolume paint_volume;
177
178   guint preedit_cursor_pos;
179   gint preedit_n_chars;
180
181   ClutterColor selection_color;
182
183   ClutterColor selected_text_color;
184
185   gunichar password_char;
186
187   guint password_hint_id;
188   guint password_hint_timeout;
189
190   /* Signal handler for when the backend changes its font settings */
191   guint settings_changed_id;
192
193   /* Signal handler for when the :text-direction changes */
194   guint direction_changed_id;
195
196   /* bitfields */
197   guint alignment               : 2;
198   guint wrap                    : 1;
199   guint use_underline           : 1;
200   guint use_markup              : 1;
201   guint ellipsize               : 3;
202   guint single_line_mode        : 1;
203   guint wrap_mode               : 3;
204   guint justify                 : 1;
205   guint editable                : 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;
214   guint has_focus               : 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;
219 };
220
221 enum
222 {
223   PROP_0,
224
225   PROP_BUFFER,
226   PROP_FONT_NAME,
227   PROP_FONT_DESCRIPTION,
228   PROP_TEXT,
229   PROP_COLOR,
230   PROP_USE_MARKUP,
231   PROP_ATTRIBUTES,
232   PROP_LINE_ALIGNMENT,
233   PROP_LINE_WRAP,
234   PROP_LINE_WRAP_MODE,
235   PROP_JUSTIFY,
236   PROP_ELLIPSIZE,
237   PROP_POSITION,
238   PROP_SELECTION_BOUND,
239   PROP_SELECTION_COLOR,
240   PROP_SELECTION_COLOR_SET,
241   PROP_CURSOR_VISIBLE,
242   PROP_CURSOR_COLOR,
243   PROP_CURSOR_COLOR_SET,
244   PROP_CURSOR_SIZE,
245   PROP_EDITABLE,
246   PROP_SELECTABLE,
247   PROP_ACTIVATABLE,
248   PROP_PASSWORD_CHAR,
249   PROP_MAX_LENGTH,
250   PROP_SINGLE_LINE_MODE,
251   PROP_SELECTED_TEXT_COLOR,
252   PROP_SELECTED_TEXT_COLOR_SET,
253
254   PROP_LAST
255 };
256
257 static GParamSpec *obj_props[PROP_LAST];
258
259 enum
260 {
261   TEXT_CHANGED,
262   CURSOR_EVENT,
263   ACTIVATE,
264   INSERT_TEXT,
265   DELETE_TEXT,
266
267   LAST_SIGNAL
268 };
269
270 static guint text_signals[LAST_SIGNAL] = { 0, };
271
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);
276
277 static inline void
278 clutter_text_dirty_paint_volume (ClutterText *text)
279 {
280   ClutterTextPrivate *priv = text->priv;
281
282   if (priv->paint_volume_valid)
283     {
284       clutter_paint_volume_free (&priv->paint_volume);
285       priv->paint_volume_valid = FALSE;
286     }
287 }
288
289 static inline void
290 clutter_text_queue_redraw (ClutterActor *self)
291 {
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. */
299
300   clutter_text_dirty_paint_volume (CLUTTER_TEXT (self));
301
302   clutter_actor_queue_redraw (self);
303 }
304
305 #define clutter_actor_queue_redraw \
306   Please_use_clutter_text_queue_redraw_instead
307
308 #define offset_real(t,p)        ((p) == -1 ? g_utf8_strlen ((t), -1) : (p))
309
310 static gint
311 offset_to_bytes (const gchar *text,
312                  gint         pos)
313 {
314   const gchar *ptr;
315
316   if (pos < 0)
317     return strlen (text);
318
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));
322
323   return ptr - text;
324 }
325
326 #define bytes_to_offset(t,p)    (g_utf8_pointer_to_offset ((t), (t) + (p)))
327
328 static inline void
329 clutter_text_clear_selection (ClutterText *self)
330 {
331   ClutterTextPrivate *priv = self->priv;
332
333   if (priv->selection_bound != priv->position)
334     {
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));
338     }
339 }
340
341 static gchar *
342 clutter_text_get_display_text (ClutterText *self)
343 {
344   ClutterTextPrivate *priv = self->priv;
345   ClutterTextBuffer *buffer;
346   const gchar *text;
347
348   buffer = get_buffer (self);
349   text = clutter_text_buffer_get_text (buffer);
350
351   /* simple short-circuit to avoid going through GString
352    * with an empty text and a password char set
353    */
354   if (text[0] == '\0')
355     return g_strdup ("");
356
357   if (G_LIKELY (priv->password_char == 0))
358     return g_strdup (text);
359   else
360     {
361       GString *str;
362       gunichar invisible_char;
363       gchar buf[7];
364       gint char_len, i;
365       guint n_chars;
366
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;
370
371       /* we need to convert the string built of invisible
372        * characters into UTF-8 for it to be fed to the Pango
373        * layout
374        */
375       memset (buf, 0, sizeof (buf));
376       char_len = g_unichar_to_utf8 (invisible_char, buf);
377
378       if (priv->show_password_hint && priv->password_hint_visible)
379         {
380           char *last_char;
381
382           for (i = 0; i < n_chars - 1; i++)
383             g_string_append_len (str, buf, char_len);
384
385           last_char = g_utf8_offset_to_pointer (text, n_chars - 1);
386           g_string_append (str, last_char);
387         }
388       else
389         {
390           for (i = 0; i < n_chars; i++)
391             g_string_append_len (str, buf, char_len);
392         }
393
394       return g_string_free (str, FALSE);
395     }
396 }
397
398 static inline void
399 clutter_text_ensure_effective_attributes (ClutterText *self)
400 {
401   ClutterTextPrivate *priv = self->priv;
402
403   /* If we already have the effective attributes then we don't need to
404      do anything */
405   if (priv->effective_attrs != NULL)
406     return;
407
408   /* same as if we don't have any attribute at all */
409   if (priv->attrs == NULL && priv->markup_attrs == NULL)
410     return;
411
412   if (priv->attrs != NULL)
413     {
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);
418       else
419         {
420           /* Otherwise we need to merge the two lists */
421           PangoAttrIterator *iter;
422           GSList *attributes, *l;
423
424           priv->effective_attrs = pango_attr_list_copy (priv->markup_attrs);
425
426           iter = pango_attr_list_get_iterator (priv->attrs);
427           do
428             {
429               attributes = pango_attr_iterator_get_attrs (iter);
430
431               for (l = attributes; l != NULL; l = l->next)
432                 {
433                   PangoAttribute *attr = l->data;
434
435                   pango_attr_list_insert (priv->effective_attrs, attr);
436                 }
437
438               g_slist_free (attributes);
439             }
440           while (pango_attr_iterator_next (iter));
441
442           pango_attr_iterator_destroy (iter);
443         }
444     }
445   else if (priv->markup_attrs != NULL)
446     {
447       /* We can just use the markup attributes directly */
448       priv->effective_attrs = pango_attr_list_ref (priv->markup_attrs);
449     }
450 }
451
452 static PangoLayout *
453 clutter_text_create_layout_no_cache (ClutterText       *text,
454                                      gint               width,
455                                      gint               height,
456                                      PangoEllipsizeMode ellipsize)
457 {
458   ClutterTextPrivate *priv = text->priv;
459   PangoLayout *layout;
460   gchar *contents;
461   gsize contents_len;
462
463   CLUTTER_STATIC_TIMER (text_layout_timer,
464                         "Mainloop",
465                         "Text Layout",
466                         "Layout creation",
467                         0);
468
469   CLUTTER_TIMER_START (_clutter_uprof_context, text_layout_timer);
470
471   layout = clutter_actor_create_pango_layout (CLUTTER_ACTOR (text), NULL);
472   pango_layout_set_font_description (layout, priv->font_desc);
473
474   contents = clutter_text_get_display_text (text);
475   contents_len = strlen (contents);
476
477   if (priv->editable && priv->preedit_set)
478     {
479       GString *tmp = g_string_new (contents);
480       PangoAttrList *tmp_attrs = pango_attr_list_new ();
481       gint cursor_index;
482
483       if (priv->position == 0)
484         cursor_index = 0;
485       else
486         cursor_index = offset_to_bytes (contents, priv->position);
487
488       g_string_insert (tmp, cursor_index, priv->preedit_str);
489
490       pango_layout_set_text (layout, tmp->str, tmp->len);
491
492       if (priv->preedit_attrs != NULL)
493         {
494           pango_attr_list_splice (tmp_attrs, priv->preedit_attrs,
495                                   cursor_index,
496                                   strlen (priv->preedit_str));
497
498           pango_layout_set_attributes (layout, tmp_attrs);
499         }
500
501       g_string_free (tmp, TRUE);
502       pango_attr_list_unref (tmp_attrs);
503     }
504   else
505     pango_layout_set_text (layout, contents, contents_len);
506
507   if (!priv->editable)
508     {
509       /* This will merge the markup attributes and the attributes
510          property if needed */
511       clutter_text_ensure_effective_attributes (text);
512
513       if (priv->effective_attrs != NULL)
514         pango_layout_set_attributes (layout, priv->effective_attrs);
515     }
516
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);
521
522   pango_layout_set_ellipsize (layout, ellipsize);
523   pango_layout_set_width (layout, width);
524   pango_layout_set_height (layout, height);
525
526   g_free (contents);
527
528   CLUTTER_TIMER_STOP (_clutter_uprof_context, text_layout_timer);
529
530   return layout;
531 }
532
533 static void
534 clutter_text_dirty_cache (ClutterText *text)
535 {
536   ClutterTextPrivate *priv = text->priv;
537   int i;
538
539   /* Delete the cached layouts so they will be recreated the next time
540      they are needed */
541   for (i = 0; i < N_CACHED_LAYOUTS; i++)
542     if (priv->cached_layouts[i].layout)
543       {
544         g_object_unref (priv->cached_layouts[i].layout);
545         priv->cached_layouts[i].layout = NULL;
546       }
547
548   clutter_text_dirty_paint_volume (text);
549 }
550
551 /*
552  * clutter_text_set_font_description_internal:
553  * @self: a #ClutterText
554  * @desc: a #PangoFontDescription
555  *
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
559  *
560  * This function will also set the :font-name field as a side-effect
561  *
562  * This function will evict the layout cache, and queue a relayout if
563  * the #ClutterText actor has contents.
564  */
565 static inline void
566 clutter_text_set_font_description_internal (ClutterText          *self,
567                                             PangoFontDescription *desc)
568 {
569   ClutterTextPrivate *priv = self->priv;
570
571   if (priv->font_desc == desc)
572     return;
573
574   if (priv->font_desc != NULL)
575     pango_font_description_free (priv->font_desc);
576
577   priv->font_desc = desc;
578
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);
582
583   clutter_text_dirty_cache (self);
584
585   if (clutter_text_buffer_get_length (get_buffer (self)) != 0)
586     clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
587
588   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_DESCRIPTION]);
589 }
590
591 static void
592 clutter_text_settings_changed_cb (ClutterText *text)
593 {
594   ClutterTextPrivate *priv = text->priv;
595   guint password_hint_time = 0;
596   ClutterSettings *settings;
597
598   settings = clutter_settings_get_default ();
599
600   g_object_get (settings, "password-hint-time", &password_hint_time, NULL);
601
602   priv->show_password_hint = password_hint_time > 0;
603   priv->password_hint_timeout = password_hint_time;
604
605   if (priv->is_default_font)
606     {
607       PangoFontDescription *font_desc;
608       gchar *font_name = NULL;
609
610       g_object_get (settings, "font-name", &font_name, NULL);
611
612       CLUTTER_NOTE (ACTOR, "Text[%p]: default font changed to '%s'",
613                     text,
614                     font_name);
615
616       font_desc = pango_font_description_from_string (font_name);
617       clutter_text_set_font_description_internal (text, font_desc);
618
619       g_free (font_name);
620     }
621
622   clutter_text_dirty_cache (text);
623   clutter_actor_queue_relayout (CLUTTER_ACTOR (text));
624 }
625
626 static void
627 clutter_text_direction_changed_cb (GObject    *gobject,
628                                    GParamSpec *pspec)
629 {
630   clutter_text_dirty_cache (CLUTTER_TEXT (gobject));
631
632   /* no need to queue a relayout: set_text_direction() will do that for us */
633 }
634
635 /*
636  * clutter_text_create_layout:
637  * @text: a #ClutterText
638  * @allocation_width: the allocation width
639  * @allocation_height: the allocation height
640  *
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.
645  */
646 static PangoLayout *
647 clutter_text_create_layout (ClutterText *text,
648                             gfloat       allocation_width,
649                             gfloat       allocation_height)
650 {
651   ClutterTextPrivate *priv = text->priv;
652   LayoutCache *oldest_cache = priv->cached_layouts;
653   gboolean found_free_cache = FALSE;
654   gint width = -1;
655   gint height = -1;
656   PangoEllipsizeMode ellipsize = PANGO_ELLIPSIZE_NONE;
657   int i;
658
659   CLUTTER_STATIC_COUNTER (text_cache_hit_counter,
660                           "Text layout cache hit counter",
661                           "Increments for each layout cache hit",
662                           0);
663   CLUTTER_STATIC_COUNTER (text_cache_miss_counter,
664                           "Text layout cache miss counter",
665                           "Increments for each layout cache miss",
666                           0);
667
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:
671    *
672    * Cases, assuming ellipsize != NONE on actor:
673    *
674    * Width request: ellipsization can be set or not on layout,
675    * doesn't matter.
676    *
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.
680    *
681    * Allocate: ellipsization must always be set.
682    *
683    * See http://bugzilla.gnome.org/show_bug.cgi?id=560931
684    */
685
686   if (priv->ellipsize != PANGO_ELLIPSIZE_NONE)
687     {
688       if (allocation_height < 0 && priv->wrap)
689         ; /* must not set ellipsization on wrap=true height request */
690       else
691         {
692           if (!priv->editable)
693             ellipsize = priv->ellipsize;
694         }
695     }
696
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.
701    *
702    * - editable, single-line text actors, since those can
703    *   scroll the layout.
704    * - non-wrapping, non-ellipsizing actors.
705    */
706   if (allocation_width >= 0 &&
707       (allocation_height >= 0 ||
708        !((priv->editable && priv->single_line_mode) ||
709          (priv->ellipsize == PANGO_ELLIPSIZE_NONE && !priv->wrap))))
710     {
711       width = allocation_width * 1024 + 0.5f;
712     }
713
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.
719    *
720    * See http://bugzilla.gnome.org/show_bug.cgi?id=560931 if this
721    * seems odd.
722    */
723   if (allocation_height >= 0 &&
724       priv->wrap &&
725       priv->ellipsize != PANGO_ELLIPSIZE_NONE &&
726       !priv->single_line_mode)
727     {
728       height = allocation_height * 1024 + 0.5f;
729     }
730
731   /* Search for a cached layout with the same width and keep
732    * track of the oldest one
733    */
734   for (i = 0; i < N_CACHED_LAYOUTS; i++)
735     {
736       if (priv->cached_layouts[i].layout == NULL)
737         {
738           /* Always prefer free cache spaces */
739           found_free_cache = TRUE;
740           oldest_cache = priv->cached_layouts + i;
741         }
742       else
743         {
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);
748
749           if (cached_width == width &&
750               cached_height == height &&
751               cached_ellipsize == ellipsize)
752             {
753               /* If this cached layout is using the same size then we can
754                * just return that directly
755                */
756               CLUTTER_NOTE (ACTOR,
757                             "ClutterText: %p: cache hit for size %.2fx%.2f",
758                             text,
759                             allocation_width,
760                             allocation_height);
761
762               CLUTTER_COUNTER_INC (_clutter_uprof_context,
763                                    text_cache_hit_counter);
764
765               return priv->cached_layouts[i].layout;
766             }
767
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.
773            */
774           if (allocation_height < 0 &&
775               cached_width == -1 &&
776               cached_ellipsize == ellipsize)
777             {
778               PangoRectangle logical_rect;
779
780               pango_layout_get_extents (priv->cached_layouts[i].layout,
781                                         NULL,
782                                         &logical_rect);
783
784               if (logical_rect.width <= width)
785                 {
786                   /* We've been asked for our height for the width we gave as a result
787                    * of a _get_preferred_width call
788                    */
789                   CLUTTER_NOTE (ACTOR,
790                                 "ClutterText: %p: cache hit for size %.2fx%.2f "
791                                 "(unwrapped width narrower than given width)",
792                                 text,
793                                 allocation_width,
794                                 allocation_height);
795
796                   CLUTTER_COUNTER_INC (_clutter_uprof_context,
797                                        text_cache_hit_counter);
798
799                   return priv->cached_layouts[i].layout;
800                 }
801             }
802
803           if (!found_free_cache &&
804               (priv->cached_layouts[i].age < oldest_cache->age))
805             {
806               oldest_cache = priv->cached_layouts + i;
807             }
808         }
809     }
810
811   CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache miss for size %.2fx%.2f",
812                 text,
813                 allocation_width,
814                 allocation_height);
815
816   CLUTTER_COUNTER_INC (_clutter_uprof_context, text_cache_miss_counter);
817
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);
822
823   oldest_cache->layout =
824     clutter_text_create_layout_no_cache (text, width, height, ellipsize);
825
826   cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout);
827
828   /* Mark the 'time' this cache was created and advance the time */
829   oldest_cache->age = priv->cache_age++;
830   return oldest_cache->layout;
831 }
832
833 /**
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
838  *
839  * Retrieves the position of the character at the given coordinates.
840  *
841  * Return: the position of the character
842  *
843  * Since: 1.10
844  */
845 gint
846 clutter_text_coords_to_position (ClutterText *self,
847                                  gfloat       x,
848                                  gfloat       y)
849 {
850   gint index_;
851   gint px, py;
852   gint trailing;
853
854   g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
855
856   /* Take any offset due to scrolling into account, and normalize
857    * the coordinates to PangoScale units
858    */
859   px = (x - self->priv->text_x) * PANGO_SCALE;
860   py = (y - self->priv->text_y) * PANGO_SCALE;
861
862   pango_layout_xy_to_index (clutter_text_get_layout (self),
863                             px, py,
864                             &index_, &trailing);
865
866   return index_ + trailing;
867 }
868
869 /**
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
876  *
877  * Retrieves the coordinates of the given @position.
878  *
879  * Return value: %TRUE if the conversion was successful
880  *
881  * Since: 1.0
882  */
883 gboolean
884 clutter_text_position_to_coords (ClutterText *self,
885                                  gint         position,
886                                  gfloat      *x,
887                                  gfloat      *y,
888                                  gfloat      *line_height)
889 {
890   ClutterTextPrivate *priv;
891   PangoRectangle rect;
892   gint n_chars;
893   gint password_char_bytes = 1;
894   gint index_;
895   gsize n_bytes;
896
897   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
898
899   priv = self->priv;
900
901   n_chars = clutter_text_buffer_get_length (get_buffer (self));
902   if (priv->preedit_set)
903     n_chars += priv->preedit_n_chars;
904
905   if (position < -1 || position > n_chars)
906     return FALSE;
907
908   if (priv->password_char != 0)
909     password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL);
910
911   if (position == -1)
912     {
913       if (priv->password_char == 0)
914         {
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);
918           else
919             index_ = n_bytes;
920         }
921       else
922         index_ = n_chars * password_char_bytes;
923     }
924   else if (position == 0)
925     {
926       index_ = 0;
927     }
928   else
929     {
930       gchar *text = clutter_text_get_display_text (self);
931       GString *tmp = g_string_new (text);
932       gint cursor_index;
933
934       cursor_index = offset_to_bytes (text, priv->position);
935
936       if (priv->preedit_str != NULL)
937         g_string_insert (tmp, cursor_index, priv->preedit_str);
938
939       if (priv->password_char == 0)
940         index_ = offset_to_bytes (tmp->str, position);
941       else
942         index_ = position * password_char_bytes;
943
944       g_free (text);
945       g_string_free (tmp, TRUE);
946     }
947
948   pango_layout_get_cursor_pos (clutter_text_get_layout (self),
949                                index_,
950                                &rect, NULL);
951
952   if (x)
953     {
954       *x = (gfloat) rect.x / 1024.0f;
955
956       /* Take any offset due to scrolling into account */
957       if (priv->single_line_mode)
958         *x += priv->text_x;
959     }
960
961   if (y)
962     *y = (gfloat) rect.y / 1024.0f;
963
964   if (line_height)
965     *line_height = (gfloat) rect.height / 1024.0f;
966
967   return TRUE;
968 }
969
970 static inline void
971 clutter_text_ensure_cursor_position (ClutterText *self)
972 {
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;
978   gint position;
979
980   position = priv->position;
981
982   if (priv->editable && priv->preedit_set)
983     {
984       if (position == -1)
985         position = clutter_text_buffer_get_length (get_buffer (self));
986       position += priv->preedit_cursor_pos;
987     }
988
989   CLUTTER_NOTE (MISC, "Cursor at %d (preedit %s at pos: %d)",
990                 position,
991                 priv->preedit_set ? "set" : "unset",
992                 priv->preedit_set ? priv->preedit_cursor_pos : 0);
993
994   x = y = cursor_height = 0;
995   clutter_text_position_to_coords (self, position,
996                                    &x, &y,
997                                    &cursor_height);
998
999   cursor_pos.x      = x;
1000   cursor_pos.y      = y + 2;
1001   cursor_pos.width  = priv->cursor_size;
1002   cursor_pos.height = cursor_height - 4;
1003
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;
1008
1009   if (x_changed || y_changed || width_changed || height_changed)
1010     {
1011       priv->cursor_pos = cursor_pos;
1012
1013       g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos);
1014     }
1015 }
1016
1017 /**
1018  * clutter_text_delete_selection:
1019  * @self: a #ClutterText
1020  *
1021  * Deletes the currently selected text
1022  *
1023  * This function is only useful in subclasses of #ClutterText
1024  *
1025  * Return value: %TRUE if text was deleted or if the text actor
1026  *   is empty, and %FALSE otherwise
1027  *
1028  * Since: 1.0
1029  */
1030 gboolean
1031 clutter_text_delete_selection (ClutterText *self)
1032 {
1033   ClutterTextPrivate *priv;
1034   gint start_index;
1035   gint end_index;
1036   gint old_position, old_selection;
1037   guint n_chars;
1038
1039   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
1040
1041   priv = self->priv;
1042
1043   n_chars = clutter_text_buffer_get_length (get_buffer (self));
1044   if (n_chars == 0)
1045     return TRUE;
1046
1047   start_index = priv->position == -1 ? n_chars : priv->position;
1048   end_index = priv->selection_bound == -1 ? n_chars : priv->selection_bound;
1049
1050   if (end_index == start_index)
1051     return FALSE;
1052
1053   if (end_index < start_index)
1054     {
1055       gint temp = start_index;
1056       start_index = end_index;
1057       end_index = temp;
1058     }
1059
1060   old_position = priv->position;
1061   old_selection = priv->selection_bound;
1062
1063   clutter_text_delete_text (self, start_index, end_index);
1064
1065   priv->position = start_index;
1066   priv->selection_bound = start_index;
1067
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]);
1071
1072   if (priv->selection_bound != old_selection)
1073     g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
1074
1075   return TRUE;
1076 }
1077
1078 /*
1079  * Utility function to update both cursor position and selection bound
1080  * at once
1081  */
1082 static inline void
1083 clutter_text_set_positions (ClutterText *self,
1084                             gint         new_pos,
1085                             gint         new_bound)
1086 {
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));
1091 }
1092
1093 static inline void
1094 clutter_text_set_markup_internal (ClutterText *self,
1095                                   const gchar *str)
1096 {
1097   ClutterTextPrivate *priv = self->priv;
1098   GError *error;
1099   gchar *text = NULL;
1100   PangoAttrList *attrs = NULL;
1101   gboolean res;
1102
1103   g_assert (str != NULL);
1104
1105   error = NULL;
1106   res = pango_parse_markup (str, -1, 0,
1107                             &attrs,
1108                             &text,
1109                             NULL,
1110                             &error);
1111
1112   if (!res)
1113     {
1114       if (G_LIKELY (error != NULL))
1115         {
1116           g_warning ("Failed to set the markup of the actor '%s': %s",
1117                      _clutter_actor_get_debug_name (CLUTTER_ACTOR (self)),
1118                      error->message);
1119           g_error_free (error);
1120         }
1121       else
1122         g_warning ("Failed to set the markup of the actor '%s'",
1123                    _clutter_actor_get_debug_name (CLUTTER_ACTOR (self)));
1124
1125       return;
1126     }
1127
1128   if (text)
1129     {
1130       clutter_text_buffer_set_text (get_buffer (self), text, -1);
1131       g_free (text);
1132     }
1133
1134   /* Store the new markup attributes */
1135   if (priv->markup_attrs != NULL)
1136     pango_attr_list_unref (priv->markup_attrs);
1137
1138   priv->markup_attrs = attrs;
1139
1140   /* Clear the effective attributes so they will be regenerated when a
1141      layout is created */
1142   if (priv->effective_attrs != NULL)
1143     {
1144       pango_attr_list_unref (priv->effective_attrs);
1145       priv->effective_attrs = NULL;
1146     }
1147 }
1148
1149 static void
1150 clutter_text_set_property (GObject      *gobject,
1151                            guint         prop_id,
1152                            const GValue *value,
1153                            GParamSpec   *pspec)
1154 {
1155   ClutterText *self = CLUTTER_TEXT (gobject);
1156
1157   switch (prop_id)
1158     {
1159     case PROP_BUFFER:
1160       clutter_text_set_buffer (self, g_value_get_object (value));
1161       break;
1162
1163     case PROP_TEXT:
1164       {
1165         const char *str = g_value_get_string (value);
1166         if (self->priv->use_markup)
1167           clutter_text_set_markup_internal (self, str ? str : "");
1168         else
1169           clutter_text_buffer_set_text (get_buffer (self), str ? str : "", -1);
1170       }
1171       break;
1172
1173     case PROP_COLOR:
1174       clutter_text_set_color (self, clutter_value_get_color (value));
1175       break;
1176
1177     case PROP_FONT_NAME:
1178       clutter_text_set_font_name (self, g_value_get_string (value));
1179       break;
1180
1181     case PROP_FONT_DESCRIPTION:
1182       clutter_text_set_font_description (self, g_value_get_boxed (value));
1183       break;
1184
1185     case PROP_USE_MARKUP:
1186       clutter_text_set_use_markup (self, g_value_get_boolean (value));
1187       break;
1188
1189     case PROP_ATTRIBUTES:
1190       clutter_text_set_attributes (self, g_value_get_boxed (value));
1191       break;
1192
1193     case PROP_LINE_ALIGNMENT:
1194       clutter_text_set_line_alignment (self, g_value_get_enum (value));
1195       break;
1196
1197     case PROP_LINE_WRAP:
1198       clutter_text_set_line_wrap (self, g_value_get_boolean (value));
1199       break;
1200
1201     case PROP_LINE_WRAP_MODE:
1202       clutter_text_set_line_wrap_mode (self, g_value_get_enum (value));
1203       break;
1204
1205     case PROP_JUSTIFY:
1206       clutter_text_set_justify (self, g_value_get_boolean (value));
1207       break;
1208
1209     case PROP_ELLIPSIZE:
1210       clutter_text_set_ellipsize (self, g_value_get_enum (value));
1211       break;
1212
1213     case PROP_POSITION:
1214       clutter_text_set_cursor_position (self, g_value_get_int (value));
1215       break;
1216
1217     case PROP_SELECTION_BOUND:
1218       clutter_text_set_selection_bound (self, g_value_get_int (value));
1219       break;
1220
1221     case PROP_SELECTION_COLOR:
1222       clutter_text_set_selection_color (self, g_value_get_boxed (value));
1223       break;
1224
1225     case PROP_CURSOR_VISIBLE:
1226       clutter_text_set_cursor_visible (self, g_value_get_boolean (value));
1227       break;
1228
1229     case PROP_CURSOR_COLOR:
1230       clutter_text_set_cursor_color (self, g_value_get_boxed (value));
1231       break;
1232
1233     case PROP_CURSOR_SIZE:
1234       clutter_text_set_cursor_size (self, g_value_get_int (value));
1235       break;
1236
1237     case PROP_EDITABLE:
1238       clutter_text_set_editable (self, g_value_get_boolean (value));
1239       break;
1240
1241     case PROP_ACTIVATABLE:
1242       clutter_text_set_activatable (self, g_value_get_boolean (value));
1243       break;
1244
1245     case PROP_SELECTABLE:
1246       clutter_text_set_selectable (self, g_value_get_boolean (value));
1247       break;
1248
1249     case PROP_PASSWORD_CHAR:
1250       clutter_text_set_password_char (self, g_value_get_uint (value));
1251       break;
1252
1253     case PROP_MAX_LENGTH:
1254       clutter_text_set_max_length (self, g_value_get_int (value));
1255       break;
1256
1257     case PROP_SINGLE_LINE_MODE:
1258       clutter_text_set_single_line_mode (self, g_value_get_boolean (value));
1259       break;
1260
1261     case PROP_SELECTED_TEXT_COLOR:
1262       clutter_text_set_selected_text_color (self, clutter_value_get_color (value));
1263       break;
1264
1265     default:
1266       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1267     }
1268 }
1269
1270 static void
1271 clutter_text_get_property (GObject    *gobject,
1272                            guint       prop_id,
1273                            GValue     *value,
1274                            GParamSpec *pspec)
1275 {
1276   ClutterText *self = CLUTTER_TEXT (gobject);
1277   ClutterTextPrivate *priv = self->priv;
1278
1279   switch (prop_id)
1280     {
1281     case PROP_BUFFER:
1282       g_value_set_object (value, clutter_text_get_buffer (self));
1283       break;
1284
1285     case PROP_TEXT:
1286       g_value_set_string (value, clutter_text_buffer_get_text (get_buffer (self)));
1287       break;
1288
1289     case PROP_FONT_NAME:
1290       g_value_set_string (value, priv->font_name);
1291       break;
1292
1293     case PROP_FONT_DESCRIPTION:
1294       g_value_set_boxed (value, priv->font_desc);
1295       break;
1296
1297     case PROP_USE_MARKUP:
1298       g_value_set_boolean (value, priv->use_markup);
1299       break;
1300
1301     case PROP_COLOR:
1302       clutter_value_set_color (value, &priv->text_color);
1303       break;
1304
1305     case PROP_CURSOR_VISIBLE:
1306       g_value_set_boolean (value, priv->cursor_visible);
1307       break;
1308
1309     case PROP_CURSOR_COLOR:
1310       clutter_value_set_color (value, &priv->cursor_color);
1311       break;
1312
1313     case PROP_CURSOR_COLOR_SET:
1314       g_value_set_boolean (value, priv->cursor_color_set);
1315       break;
1316
1317     case PROP_CURSOR_SIZE:
1318       g_value_set_int (value, priv->cursor_size);
1319       break;
1320
1321     case PROP_POSITION:
1322       g_value_set_int (value, priv->position);
1323       break;
1324
1325     case PROP_SELECTION_BOUND:
1326       g_value_set_int (value, priv->selection_bound);
1327       break;
1328
1329     case PROP_EDITABLE:
1330       g_value_set_boolean (value, priv->editable);
1331       break;
1332
1333     case PROP_SELECTABLE:
1334       g_value_set_boolean (value, priv->selectable);
1335       break;
1336
1337     case PROP_SELECTION_COLOR:
1338       clutter_value_set_color (value, &priv->selection_color);
1339       break;
1340
1341     case PROP_SELECTION_COLOR_SET:
1342       g_value_set_boolean (value, priv->selection_color_set);
1343       break;
1344
1345     case PROP_ACTIVATABLE:
1346       g_value_set_boolean (value, priv->activatable);
1347       break;
1348
1349     case PROP_PASSWORD_CHAR:
1350       g_value_set_uint (value, priv->password_char);
1351       break;
1352
1353     case PROP_MAX_LENGTH:
1354       g_value_set_int (value, clutter_text_buffer_get_max_length (get_buffer (self)));
1355       break;
1356
1357     case PROP_SINGLE_LINE_MODE:
1358       g_value_set_boolean (value, priv->single_line_mode);
1359       break;
1360
1361     case PROP_ELLIPSIZE:
1362       g_value_set_enum (value, priv->ellipsize);
1363       break;
1364
1365     case PROP_LINE_WRAP:
1366       g_value_set_boolean (value, priv->wrap);
1367       break;
1368
1369     case PROP_LINE_WRAP_MODE:
1370       g_value_set_enum (value, priv->wrap_mode);
1371       break;
1372
1373     case PROP_LINE_ALIGNMENT:
1374       g_value_set_enum (value, priv->alignment);
1375       break;
1376
1377     case PROP_JUSTIFY:
1378       g_value_set_boolean (value, priv->justify);
1379       break;
1380
1381     case PROP_ATTRIBUTES:
1382       g_value_set_boxed (value, priv->attrs);
1383       break;
1384
1385     case PROP_SELECTED_TEXT_COLOR:
1386       clutter_value_set_color (value, &priv->selected_text_color);
1387       break;
1388
1389     case PROP_SELECTED_TEXT_COLOR_SET:
1390       g_value_set_boolean (value, priv->selected_text_color_set);
1391       break;
1392
1393     default:
1394       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1395     }
1396 }
1397
1398 static void
1399 clutter_text_dispose (GObject *gobject)
1400 {
1401   ClutterText *self = CLUTTER_TEXT (gobject);
1402   ClutterTextPrivate *priv = self->priv;
1403
1404   /* get rid of the entire cache */
1405   clutter_text_dirty_cache (self);
1406
1407   if (priv->direction_changed_id)
1408     {
1409       g_signal_handler_disconnect (self, priv->direction_changed_id);
1410       priv->direction_changed_id = 0;
1411     }
1412
1413   if (priv->settings_changed_id)
1414     {
1415       g_signal_handler_disconnect (clutter_get_default_backend (),
1416                                    priv->settings_changed_id);
1417       priv->settings_changed_id = 0;
1418     }
1419
1420   if (priv->password_hint_id)
1421     {
1422       g_source_remove (priv->password_hint_id);
1423       priv->password_hint_id = 0;
1424     }
1425
1426   clutter_text_set_buffer (self, NULL);
1427
1428   G_OBJECT_CLASS (clutter_text_parent_class)->dispose (gobject);
1429 }
1430
1431 static void
1432 clutter_text_finalize (GObject *gobject)
1433 {
1434   ClutterText *self = CLUTTER_TEXT (gobject);
1435   ClutterTextPrivate *priv = self->priv;
1436
1437   if (priv->font_desc)
1438     pango_font_description_free (priv->font_desc);
1439
1440   if (priv->attrs)
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);
1448
1449   clutter_text_dirty_paint_volume (self);
1450
1451   clutter_text_set_buffer (self, NULL);
1452   g_free (priv->font_name);
1453
1454   G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject);
1455 }
1456
1457 typedef void (* ClutterTextSelectionFunc) (ClutterText           *text,
1458                                            const ClutterActorBox *box,
1459                                            gpointer               user_data);
1460
1461 static void
1462 clutter_text_foreach_selection_rectangle (ClutterText              *self,
1463                                           ClutterTextSelectionFunc  func,
1464                                           gpointer                  user_data)
1465 {
1466   ClutterTextPrivate *priv = self->priv;
1467   PangoLayout *layout = clutter_text_get_layout (self);
1468   gchar *utf8 = clutter_text_get_display_text (self);
1469   gint lines;
1470   gint start_index;
1471   gint end_index;
1472   gint line_no;
1473
1474   if (priv->position == 0)
1475     start_index = 0;
1476   else
1477     start_index = offset_to_bytes (utf8, priv->position);
1478
1479   if (priv->selection_bound == 0)
1480     end_index = 0;
1481   else
1482     end_index = offset_to_bytes (utf8, priv->selection_bound);
1483
1484   if (start_index > end_index)
1485     {
1486       gint temp = start_index;
1487       start_index = end_index;
1488       end_index = temp;
1489     }
1490
1491   lines = pango_layout_get_line_count (layout);
1492
1493   for (line_no = 0; line_no < lines; line_no++)
1494     {
1495       PangoLayoutLine *line;
1496       gint n_ranges;
1497       gint *ranges;
1498       gint i;
1499       gint index_;
1500       gint maxindex;
1501       ClutterActorBox box;
1502       gfloat y, height;
1503
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)
1507         continue;
1508
1509       pango_layout_line_get_x_ranges (line, start_index, end_index,
1510                                       &ranges,
1511                                       &n_ranges);
1512       pango_layout_line_x_to_index (line, 0, &index_, NULL);
1513
1514       clutter_text_position_to_coords (self,
1515                                        bytes_to_offset (utf8, index_),
1516                                        NULL, &y, &height);
1517
1518       box.y1 = y;
1519       box.y2 = y + height;
1520
1521       for (i = 0; i < n_ranges; i++)
1522         {
1523           gfloat range_x;
1524           gfloat range_width;
1525
1526           range_x = ranges[i * 2] / PANGO_SCALE;
1527
1528           /* Account for any scrolling in single line mode */
1529           if (priv->single_line_mode)
1530             range_x += priv->text_x;
1531
1532
1533           range_width = ((gfloat) ranges[i * 2 + 1] - (gfloat) ranges[i * 2])
1534                       / PANGO_SCALE;
1535
1536           box.x1 = range_x;
1537           box.x2 = ceilf (range_x + range_width + .5f);
1538
1539           func (self, &box, user_data);
1540         }
1541
1542       g_free (ranges);
1543     }
1544
1545   g_free (utf8);
1546 }
1547
1548 static void
1549 add_selection_rectangle_to_path (ClutterText           *text,
1550                                  const ClutterActorBox *box,
1551                                  gpointer               user_data)
1552 {
1553   cogl_path_rectangle (user_data, box->x1, box->y1, box->x2, box->y2);
1554 }
1555
1556 /* Draws the selected text, its background, and the cursor */
1557 static void
1558 selection_paint (ClutterText *self)
1559 {
1560   ClutterTextPrivate *priv = self->priv;
1561   ClutterActor *actor = CLUTTER_ACTOR (self);
1562   guint8 paint_opacity = clutter_actor_get_paint_opacity (actor);
1563
1564   if (!priv->has_focus)
1565     return;
1566
1567   if (priv->editable && priv->cursor_visible)
1568     {
1569       const ClutterColor *color;
1570       gint position;
1571
1572       position = priv->position;
1573
1574       if (position == priv->selection_bound)
1575         {
1576           /* No selection, just draw the cursor */
1577           if (priv->cursor_color_set)
1578             color = &priv->cursor_color;
1579           else
1580             color = &priv->text_color;
1581
1582           cogl_set_source_color4ub (color->red,
1583                                     color->green,
1584                                     color->blue,
1585                                     paint_opacity
1586                                     * color->alpha
1587                                     / 255);
1588
1589           cogl_rectangle (priv->cursor_pos.x,
1590                           priv->cursor_pos.y,
1591                           priv->cursor_pos.x + priv->cursor_pos.width,
1592                           priv->cursor_pos.y + priv->cursor_pos.height);
1593         }
1594       else
1595         {
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, };
1600
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;
1606           else
1607             color = &priv->text_color;
1608
1609           cogl_set_source_color4ub (color->red,
1610                                     color->green,
1611                                     color->blue,
1612                                     paint_opacity * color->alpha / 255);
1613
1614           clutter_text_foreach_selection_rectangle (self,
1615                                                     add_selection_rectangle_to_path,
1616                                                     selection_path);
1617
1618           cogl_path_fill (selection_path);
1619
1620           /* Paint selected text */
1621           cogl_framebuffer_push_path_clip (cogl_get_draw_framebuffer (),
1622                                            selection_path);
1623           cogl_object_unref (selection_path);
1624
1625           if (priv->selected_text_color_set)
1626             color = &priv->selected_text_color;
1627           else
1628             color = &priv->text_color;
1629
1630           cogl_color_init_from_4ub (&cogl_color,
1631                                     color->red,
1632                                     color->green,
1633                                     color->blue,
1634                                     paint_opacity * color->alpha / 255);
1635
1636           cogl_pango_render_layout (layout, priv->text_x, 0, &cogl_color, 0);
1637
1638           cogl_clip_pop ();
1639         }
1640     }
1641 }
1642
1643 static gint
1644 clutter_text_move_word_backward (ClutterText *self,
1645                                  gint         start)
1646 {
1647   gint retval = start;
1648
1649   if (clutter_text_buffer_get_length (get_buffer (self)) > 0 && start > 0)
1650     {
1651       PangoLayout *layout = clutter_text_get_layout (self);
1652       PangoLogAttr *log_attrs = NULL;
1653       gint n_attrs = 0;
1654
1655       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
1656
1657       retval = start - 1;
1658       while (retval > 0 && !log_attrs[retval].is_word_start)
1659         retval -= 1;
1660
1661       g_free (log_attrs);
1662     }
1663
1664   return retval;
1665 }
1666
1667 static gint
1668 clutter_text_move_word_forward (ClutterText *self,
1669                                 gint         start)
1670 {
1671   gint retval = start;
1672   guint n_chars;
1673
1674   n_chars = clutter_text_buffer_get_length (get_buffer (self));
1675   if (n_chars > 0 && start < n_chars)
1676     {
1677       PangoLayout *layout = clutter_text_get_layout (self);
1678       PangoLogAttr *log_attrs = NULL;
1679       gint n_attrs = 0;
1680
1681       pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
1682
1683       retval = start + 1;
1684       while (retval < n_chars && !log_attrs[retval].is_word_end)
1685         retval += 1;
1686
1687       g_free (log_attrs);
1688     }
1689
1690   return retval;
1691 }
1692
1693 static gint
1694 clutter_text_move_line_start (ClutterText *self,
1695                               gint         start)
1696 {
1697   PangoLayoutLine *layout_line;
1698   PangoLayout *layout;
1699   gint line_no;
1700   gint index_;
1701   gint position;
1702   const gchar *text;
1703
1704   layout = clutter_text_get_layout (self);
1705   text = clutter_text_buffer_get_text (get_buffer (self));
1706
1707   if (start == 0)
1708     index_ = 0;
1709   else
1710     index_ = offset_to_bytes (text, start);
1711
1712   pango_layout_index_to_line_x (layout, index_,
1713                                 0,
1714                                 &line_no, NULL);
1715
1716   layout_line = pango_layout_get_line_readonly (layout, line_no);
1717   if (!layout_line)
1718     return FALSE;
1719
1720   pango_layout_line_x_to_index (layout_line, 0, &index_, NULL);
1721
1722   position = bytes_to_offset (text, index_);
1723
1724   return position;
1725 }
1726
1727 static gint
1728 clutter_text_move_line_end (ClutterText *self,
1729                             gint         start)
1730 {
1731   ClutterTextPrivate *priv = self->priv;
1732   PangoLayoutLine *layout_line;
1733   PangoLayout *layout;
1734   gint line_no;
1735   gint index_;
1736   gint trailing;
1737   gint position;
1738   const gchar *text;
1739
1740   layout = clutter_text_get_layout (self);
1741   text = clutter_text_buffer_get_text (get_buffer (self));
1742
1743   if (start == 0)
1744     index_ = 0;
1745   else
1746     index_ = offset_to_bytes (text, priv->position);
1747
1748   pango_layout_index_to_line_x (layout, index_,
1749                                 0,
1750                                 &line_no, NULL);
1751
1752   layout_line = pango_layout_get_line_readonly (layout, line_no);
1753   if (!layout_line)
1754     return FALSE;
1755
1756   pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing);
1757   index_ += trailing;
1758
1759   position = bytes_to_offset (text, index_);
1760
1761   return position;
1762 }
1763
1764 static void
1765 clutter_text_select_word (ClutterText *self)
1766 {
1767   gint cursor_pos = self->priv->position;
1768   gint start_pos, end_pos;
1769
1770   start_pos = clutter_text_move_word_backward (self, cursor_pos);
1771   end_pos   = clutter_text_move_word_forward (self, cursor_pos);
1772
1773   clutter_text_set_selection (self, start_pos, end_pos);
1774 }
1775
1776 static void
1777 clutter_text_select_line (ClutterText *self)
1778 {
1779   ClutterTextPrivate *priv = self->priv;
1780   gint cursor_pos = priv->position;
1781   gint start_pos, end_pos;
1782
1783   if (priv->single_line_mode)
1784     {
1785       start_pos = 0;
1786       end_pos   = -1;
1787     }
1788   else
1789     {
1790       start_pos = clutter_text_move_line_start (self, cursor_pos);
1791       end_pos   = clutter_text_move_line_end (self, cursor_pos);
1792     }
1793
1794   clutter_text_set_selection (self, start_pos, end_pos);
1795 }
1796
1797 static gboolean
1798 clutter_text_button_press (ClutterActor       *actor,
1799                            ClutterButtonEvent *event)
1800 {
1801   ClutterText *self = CLUTTER_TEXT (actor);
1802   ClutterTextPrivate *priv = self->priv;
1803   gboolean res = FALSE;
1804   gfloat x, y;
1805   gint index_;
1806
1807   /* we'll steal keyfocus if we need it */
1808   if (priv->editable || priv->selectable)
1809     clutter_actor_grab_key_focus (actor);
1810   else
1811     return CLUTTER_EVENT_PROPAGATE;
1812
1813   /* if the actor is empty we just reset everything and not
1814    * set up the dragging of the selection since there's nothing
1815    * to select
1816    */
1817   if (clutter_text_buffer_get_length (get_buffer (self)) == 0)
1818     {
1819       clutter_text_set_positions (self, -1, -1);
1820
1821       return CLUTTER_EVENT_STOP;
1822     }
1823
1824   res = clutter_actor_transform_stage_point (actor,
1825                                              event->x,
1826                                              event->y,
1827                                              &x, &y);
1828   if (res)
1829     {
1830       gint offset;
1831       const char *text;
1832
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_);
1836
1837       /* what we select depends on the number of button clicks we
1838        * receive:
1839        *
1840        *   1: just position the cursor and the selection
1841        *   2: select the current word
1842        *   3: select the contents of the whole actor
1843        */
1844       if (event->click_count == 1)
1845         {
1846           clutter_text_set_positions (self, offset, offset);
1847         }
1848       else if (event->click_count == 2)
1849         {
1850           clutter_text_select_word (self);
1851         }
1852       else if (event->click_count == 3)
1853         {
1854           clutter_text_select_line (self);
1855         }
1856     }
1857
1858   /* grab the pointer */
1859   priv->in_select_drag = TRUE;
1860   clutter_grab_pointer (actor);
1861
1862   return CLUTTER_EVENT_STOP;
1863 }
1864
1865 static gboolean
1866 clutter_text_motion (ClutterActor       *actor,
1867                      ClutterMotionEvent *mev)
1868 {
1869   ClutterText *self = CLUTTER_TEXT (actor);
1870   ClutterTextPrivate *priv = self->priv;
1871   gfloat x, y;
1872   gint index_, offset;
1873   gboolean res;
1874   const gchar *text;
1875
1876   if (!priv->in_select_drag)
1877     return CLUTTER_EVENT_PROPAGATE;
1878
1879   res = clutter_actor_transform_stage_point (actor,
1880                                              mev->x, mev->y,
1881                                              &x, &y);
1882   if (!res)
1883     return CLUTTER_EVENT_PROPAGATE;
1884
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_);
1888
1889   if (priv->selectable)
1890     clutter_text_set_cursor_position (self, offset);
1891   else
1892     clutter_text_set_positions (self, offset, offset);
1893
1894   return CLUTTER_EVENT_STOP;
1895 }
1896
1897 static gboolean
1898 clutter_text_button_release (ClutterActor       *actor,
1899                              ClutterButtonEvent *bev)
1900 {
1901   ClutterText *self = CLUTTER_TEXT (actor);
1902   ClutterTextPrivate *priv = self->priv;
1903
1904   if (priv->in_select_drag)
1905     {
1906       clutter_ungrab_pointer ();
1907       priv->in_select_drag = FALSE;
1908
1909       return CLUTTER_EVENT_STOP;
1910     }
1911
1912   return CLUTTER_EVENT_PROPAGATE;
1913 }
1914
1915 static gboolean
1916 clutter_text_remove_password_hint (gpointer data)
1917 {
1918   ClutterText *self = data;
1919
1920   self->priv->password_hint_visible = FALSE;
1921   self->priv->password_hint_id = 0;
1922
1923   clutter_text_dirty_cache (data);
1924   clutter_text_queue_redraw (data);
1925
1926   return G_SOURCE_REMOVE;
1927 }
1928
1929 static gboolean
1930 clutter_text_key_press (ClutterActor    *actor,
1931                         ClutterKeyEvent *event)
1932 {
1933   ClutterText *self = CLUTTER_TEXT (actor);
1934   ClutterTextPrivate *priv = self->priv;
1935   ClutterBindingPool *pool;
1936   gboolean res;
1937
1938   if (!priv->editable)
1939     return CLUTTER_EVENT_PROPAGATE;
1940
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
1944    */
1945   pool = clutter_binding_pool_find (g_type_name (CLUTTER_TYPE_TEXT));
1946   g_assert (pool != NULL);
1947
1948   /* we allow passing synthetic events that only contain
1949    * the Unicode value and not the key symbol
1950    */
1951   if (event->keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC))
1952     res = FALSE;
1953   else
1954     res = clutter_binding_pool_activate (pool, event->keyval,
1955                                          event->modifier_state,
1956                                          G_OBJECT (actor));
1957
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
1961    * actor
1962    */
1963   if (res)
1964     return CLUTTER_EVENT_STOP;
1965   else if ((event->modifier_state & CLUTTER_CONTROL_MASK) == 0)
1966     {
1967       gunichar key_unichar;
1968
1969       /* Skip keys when control is pressed */
1970       key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) event);
1971
1972       /* return is reported as CR, but we want LF */
1973       if (key_unichar == '\r')
1974         key_unichar = '\n';
1975
1976       if (key_unichar == '\n' ||
1977           (g_unichar_validate (key_unichar) &&
1978            !g_unichar_iscntrl (key_unichar)))
1979         {
1980           /* truncate the eventual selection so that the
1981            * Unicode character can replace it
1982            */
1983           clutter_text_delete_selection (self);
1984           clutter_text_insert_unichar (self, key_unichar);
1985
1986           if (priv->show_password_hint)
1987             {
1988               if (priv->password_hint_id != 0)
1989                 g_source_remove (priv->password_hint_id);
1990
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,
1995                                              self);
1996             }
1997
1998           return CLUTTER_EVENT_STOP;
1999         }
2000     }
2001
2002   return CLUTTER_EVENT_PROPAGATE;
2003 }
2004
2005 static void
2006 clutter_text_compute_layout_offsets (ClutterText           *self,
2007                                      PangoLayout           *layout,
2008                                      const ClutterActorBox *alloc,
2009                                      int                   *text_x,
2010                                      int                   *text_y)
2011 {
2012   ClutterActor *actor = CLUTTER_ACTOR (self);
2013   ClutterActorAlign x_align, y_align;
2014   PangoRectangle logical_rect;
2015   float alloc_width, alloc_height;
2016   float x, y;
2017
2018   clutter_actor_box_get_size (alloc, &alloc_width, &alloc_height);
2019   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2020
2021   if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_HORIZONTAL))
2022     x_align = _clutter_actor_get_effective_x_align (actor);
2023   else
2024     x_align = CLUTTER_ACTOR_ALIGN_FILL;
2025
2026   if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_VERTICAL))
2027     y_align = clutter_actor_get_y_align (actor);
2028   else
2029     y_align = CLUTTER_ACTOR_ALIGN_FILL;
2030
2031   x = 0.f;
2032   switch (x_align)
2033     {
2034     case CLUTTER_ACTOR_ALIGN_FILL:
2035     case CLUTTER_ACTOR_ALIGN_START:
2036       break;
2037
2038     case CLUTTER_ACTOR_ALIGN_END:
2039       if (alloc_width > logical_rect.width)
2040         x = alloc_width - logical_rect.width;
2041       break;
2042
2043     case CLUTTER_ACTOR_ALIGN_CENTER:
2044       if (alloc_width > logical_rect.width)
2045         x = (alloc_width - logical_rect.width) / 2.f;
2046       break;
2047     }
2048
2049   y = 0.f;
2050   switch (y_align)
2051     {
2052     case CLUTTER_ACTOR_ALIGN_FILL:
2053     case CLUTTER_ACTOR_ALIGN_START:
2054       break;
2055
2056     case CLUTTER_ACTOR_ALIGN_END:
2057       if (alloc_height > logical_rect.height)
2058         y = alloc_height - logical_rect.height;
2059       break;
2060
2061     case CLUTTER_ACTOR_ALIGN_CENTER:
2062       if (alloc_height > logical_rect.height)
2063         y = (alloc_height - logical_rect.height) / 2.f;
2064       break;
2065     }
2066
2067   if (text_x != NULL)
2068     *text_x = floorf (x);
2069
2070   if (text_y != NULL)
2071     *text_y = floorf (y);
2072 }
2073
2074 #define TEXT_PADDING    2
2075
2076 static void
2077 clutter_text_paint (ClutterActor *self)
2078 {
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;
2089   guint n_chars;
2090
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));
2095
2096   /* don't bother painting an empty text actor, unless it's
2097    * editable, in which case we want to paint at least the
2098    * cursor
2099    */
2100   if (n_chars == 0 && (!priv->editable || !priv->cursor_visible))
2101     return;
2102
2103   clutter_actor_get_allocation_box (self, &alloc);
2104
2105   g_object_get (self, "background-color-set", &bg_color_set, NULL);
2106   if (bg_color_set)
2107     {
2108       ClutterColor bg_color;
2109
2110       clutter_actor_get_background_color (self, &bg_color);
2111       bg_color.alpha = clutter_actor_get_paint_opacity (self)
2112                      * bg_color.alpha
2113                      / 255;
2114
2115       cogl_set_source_color4ub (bg_color.red,
2116                                 bg_color.green,
2117                                 bg_color.blue,
2118                                 bg_color.alpha);
2119       cogl_rectangle (0, 0, alloc.x2 - alloc.x1, alloc.y2 - alloc.y1);
2120     }
2121
2122   if (priv->editable && priv->single_line_mode)
2123     layout = clutter_text_create_layout (text, -1, -1);
2124   else
2125     {
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
2128        * and ellipsize
2129        */
2130       if (priv->wrap && priv->ellipsize)
2131         {
2132           layout = clutter_text_create_layout (text,
2133                                                alloc.x2 - alloc.x1,
2134                                                alloc.y2 - alloc.y1);
2135         }
2136       else
2137         {
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
2141            * useless
2142            *
2143            * see bug:
2144            *
2145            *   http://bugzilla.clutter-project.org/show_bug.cgi?id=2339
2146            *
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.
2150            */
2151           layout = clutter_text_create_layout (text,
2152                                                alloc.x2 - alloc.x1,
2153                                                -1);
2154         }
2155     }
2156
2157   if (priv->editable && priv->cursor_visible)
2158     clutter_text_ensure_cursor_position (text);
2159
2160   if (priv->editable && priv->single_line_mode)
2161     {
2162       PangoRectangle logical_rect = { 0, };
2163       gint actor_width, text_width;
2164
2165       pango_layout_get_extents (layout, NULL, &logical_rect);
2166
2167       cogl_clip_push_rectangle (0, 0,
2168                                 (alloc.x2 - alloc.x1),
2169                                 (alloc.y2 - alloc.y1));
2170       clip_set = TRUE;
2171
2172       actor_width = (alloc.x2 - alloc.x1)
2173                   - 2 * TEXT_PADDING;
2174       text_width  = logical_rect.width / PANGO_SCALE;
2175
2176       if (actor_width < text_width)
2177         {
2178           gint cursor_x = priv->cursor_pos.x;
2179
2180           if (priv->position == -1)
2181             {
2182               text_x = actor_width - text_width;
2183             }
2184           else if (priv->position == 0)
2185             {
2186               text_x = TEXT_PADDING;
2187             }
2188           else
2189             {
2190               if (cursor_x < 0)
2191                 {
2192                   text_x = text_x - cursor_x - TEXT_PADDING;
2193                 }
2194               else if (cursor_x > actor_width)
2195                 {
2196                   text_x = text_x + (actor_width - cursor_x) - TEXT_PADDING;
2197                 }
2198             }
2199         }
2200       else
2201         {
2202           text_x = TEXT_PADDING;
2203         }
2204     }
2205   else if (!priv->editable && !(priv->wrap && priv->ellipsize))
2206     {
2207       PangoRectangle logical_rect = { 0, };
2208
2209       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2210
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))
2214         {
2215           cogl_clip_push_rectangle (0, 0,
2216                                     alloc.x2 - alloc.x1,
2217                                     alloc.y2 - alloc.y1);
2218           clip_set = TRUE;
2219         }
2220
2221       clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y);
2222     }
2223   else
2224     clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y);
2225
2226   if (priv->text_x != text_x ||
2227       priv->text_y != text_y)
2228     {
2229       priv->text_x = text_x;
2230       priv->text_y = text_y;
2231
2232       clutter_text_ensure_cursor_position (text);
2233     }
2234
2235   real_opacity = clutter_actor_get_paint_opacity (self)
2236                * priv->text_color.alpha
2237                / 255;
2238
2239   CLUTTER_NOTE (PAINT, "painting text (text: '%s')",
2240                 clutter_text_buffer_get_text (get_buffer (text)));
2241
2242   cogl_color_init_from_4ub (&color,
2243                             priv->text_color.red,
2244                             priv->text_color.green,
2245                             priv->text_color.blue,
2246                             real_opacity);
2247   cogl_pango_render_layout (layout, priv->text_x, priv->text_y, &color, 0);
2248
2249   selection_paint (text);
2250
2251   if (clip_set)
2252     cogl_clip_pop ();
2253 }
2254
2255 static void
2256 add_selection_to_paint_volume (ClutterText           *text,
2257                                const ClutterActorBox *box,
2258                                gpointer               user_data)
2259 {
2260   ClutterPaintVolume *total_volume = user_data;
2261   ClutterPaintVolume rect_volume;
2262   ClutterVertex vertex;
2263
2264   _clutter_paint_volume_init_static (&rect_volume, CLUTTER_ACTOR (text));
2265
2266   vertex.x = box->x1;
2267   vertex.y = box->y1;
2268   vertex.z = 0.0f;
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);
2272
2273   clutter_paint_volume_union (total_volume, &rect_volume);
2274
2275   clutter_paint_volume_free (&rect_volume);
2276 }
2277
2278 static void
2279 clutter_text_get_paint_volume_for_cursor (ClutterText        *text,
2280                                           ClutterPaintVolume *volume)
2281 {
2282   ClutterTextPrivate *priv = text->priv;
2283   ClutterVertex origin;
2284
2285   clutter_text_ensure_cursor_position (text);
2286
2287   if (priv->position == priv->selection_bound)
2288     {
2289       origin.x = priv->cursor_pos.x;
2290       origin.y = priv->cursor_pos.y;
2291       origin.z = 0;
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);
2295     }
2296   else
2297     {
2298       clutter_text_foreach_selection_rectangle (text,
2299                                                 add_selection_to_paint_volume,
2300                                                 volume);
2301     }
2302 }
2303
2304 static gboolean
2305 clutter_text_get_paint_volume (ClutterActor       *self,
2306                                ClutterPaintVolume *volume)
2307 {
2308   ClutterText *text = CLUTTER_TEXT (self);
2309   ClutterTextPrivate *priv = text->priv;
2310
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 */
2315
2316   if (!priv->paint_volume_valid)
2317     {
2318       PangoLayout *layout;
2319       PangoRectangle ink_rect;
2320       ClutterVertex origin;
2321
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,
2326                                                         CLUTTER_TYPE_TEXT,
2327                                                         volume);
2328
2329       if (G_OBJECT_TYPE (self) != CLUTTER_TYPE_TEXT)
2330         return FALSE;
2331
2332       if (!clutter_actor_has_allocation (self))
2333         return FALSE;
2334
2335       _clutter_paint_volume_init_static (&priv->paint_volume, self);
2336
2337       layout = clutter_text_get_layout (text);
2338       pango_layout_get_extents (layout, &ink_rect, NULL);
2339
2340       origin.x = ink_rect.x / (float) PANGO_SCALE;
2341       origin.y = ink_rect.y / (float) PANGO_SCALE;
2342       origin.z = 0;
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);
2348
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)
2352         {
2353           ClutterPaintVolume cursor_paint_volume;
2354
2355           _clutter_paint_volume_init_static (&cursor_paint_volume,
2356                                              self);
2357
2358           clutter_text_get_paint_volume_for_cursor (text, &cursor_paint_volume);
2359
2360           clutter_paint_volume_union (&priv->paint_volume,
2361                                       &cursor_paint_volume);
2362
2363           clutter_paint_volume_free (&cursor_paint_volume);
2364         }
2365
2366       priv->paint_volume_valid = TRUE;
2367     }
2368
2369   _clutter_paint_volume_copy_static (&priv->paint_volume, volume);
2370
2371   return TRUE;
2372 }
2373
2374 static void
2375 clutter_text_get_preferred_width (ClutterActor *self,
2376                                   gfloat        for_height,
2377                                   gfloat       *min_width_p,
2378                                   gfloat       *natural_width_p)
2379 {
2380   ClutterText *text = CLUTTER_TEXT (self);
2381   ClutterTextPrivate *priv = text->priv;
2382   PangoRectangle logical_rect = { 0, };
2383   PangoLayout *layout;
2384   gint logical_width;
2385   gfloat layout_width;
2386
2387   layout = clutter_text_create_layout (text, -1, -1);
2388
2389   pango_layout_get_extents (layout, NULL, &logical_rect);
2390
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
2394    */
2395   logical_width = logical_rect.x + logical_rect.width;
2396
2397   layout_width = logical_width > 0
2398     ? ceilf (logical_width / 1024.0f)
2399     : 1;
2400
2401   if (min_width_p)
2402     {
2403       if (priv->wrap || priv->ellipsize || priv->editable)
2404         *min_width_p = 1;
2405       else
2406         *min_width_p = layout_width;
2407     }
2408
2409   if (natural_width_p)
2410     {
2411       if (priv->editable && priv->single_line_mode)
2412         *natural_width_p = layout_width + TEXT_PADDING * 2;
2413       else
2414         *natural_width_p = layout_width;
2415     }
2416 }
2417
2418 static void
2419 clutter_text_get_preferred_height (ClutterActor *self,
2420                                    gfloat        for_width,
2421                                    gfloat       *min_height_p,
2422                                    gfloat       *natural_height_p)
2423 {
2424   ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
2425
2426   if (for_width == 0)
2427     {
2428       if (min_height_p)
2429         *min_height_p = 0;
2430
2431       if (natural_height_p)
2432         *natural_height_p = 0;
2433     }
2434   else
2435     {
2436       PangoLayout *layout;
2437       PangoRectangle logical_rect = { 0, };
2438       gint logical_height;
2439       gfloat layout_height;
2440
2441       if (priv->single_line_mode)
2442         for_width = -1;
2443
2444       layout = clutter_text_create_layout (CLUTTER_TEXT (self),
2445                                            for_width, -1);
2446
2447       pango_layout_get_extents (layout, NULL, &logical_rect);
2448
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
2452        */
2453       logical_height = logical_rect.y + logical_rect.height;
2454       layout_height = ceilf (logical_height / 1024.0f);
2455
2456       if (min_height_p)
2457         {
2458           /* if we wrap and ellipsize then the minimum height is
2459            * going to be at least the size of the first line
2460            */
2461           if ((priv->ellipsize && priv->wrap) && !priv->single_line_mode)
2462             {
2463               PangoLayoutLine *line;
2464               gfloat line_height;
2465
2466               line = pango_layout_get_line_readonly (layout, 0);
2467               pango_layout_line_get_extents (line, NULL, &logical_rect);
2468
2469               logical_height = logical_rect.y + logical_rect.height;
2470               line_height = ceilf (logical_height / 1024.0f);
2471
2472               *min_height_p = line_height;
2473             }
2474           else
2475             *min_height_p = layout_height;
2476         }
2477
2478       if (natural_height_p)
2479         *natural_height_p = layout_height;
2480     }
2481 }
2482
2483 static void
2484 clutter_text_allocate (ClutterActor           *self,
2485                        const ClutterActorBox  *box,
2486                        ClutterAllocationFlags  flags)
2487 {
2488   ClutterText *text = CLUTTER_TEXT (self);
2489   ClutterActorClass *parent_class;
2490
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
2493    *
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
2497    */
2498   if (text->priv->editable && text->priv->single_line_mode)
2499     clutter_text_create_layout (text, -1, -1);
2500   else
2501     clutter_text_create_layout (text,
2502                                 box->x2 - box->x1,
2503                                 box->y2 - box->y1);
2504
2505   parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class);
2506   parent_class->allocate (self, box, flags);
2507 }
2508
2509 static gboolean
2510 clutter_text_has_overlaps (ClutterActor *self)
2511 {
2512   ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
2513
2514   return priv->editable ||
2515          priv->selectable ||
2516          priv->cursor_visible;
2517 }
2518
2519 static void
2520 clutter_text_key_focus_in (ClutterActor *actor)
2521 {
2522   ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
2523
2524   priv->has_focus = TRUE;
2525
2526   clutter_text_queue_redraw (actor);
2527 }
2528
2529 static void
2530 clutter_text_key_focus_out (ClutterActor *actor)
2531 {
2532   ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
2533
2534   priv->has_focus = FALSE;
2535
2536   clutter_text_queue_redraw (actor);
2537 }
2538
2539 static gboolean
2540 clutter_text_real_move_left (ClutterText         *self,
2541                              const gchar         *action,
2542                              guint                keyval,
2543                              ClutterModifierType  modifiers)
2544 {
2545   ClutterTextPrivate *priv = self->priv;
2546   gint pos = priv->position;
2547   gint new_pos = 0;
2548   gint len;
2549
2550   len = clutter_text_buffer_get_length (get_buffer (self));
2551
2552   g_object_freeze_notify (G_OBJECT (self));
2553
2554   if (pos != 0 && len != 0)
2555     {
2556       if (modifiers & CLUTTER_CONTROL_MASK)
2557         {
2558           if (pos == -1)
2559             new_pos = clutter_text_move_word_backward (self, len);
2560           else
2561             new_pos = clutter_text_move_word_backward (self, pos);
2562         }
2563       else
2564         {
2565           if (pos == -1)
2566             new_pos = len - 1;
2567           else
2568             new_pos = pos - 1;
2569         }
2570
2571       clutter_text_set_cursor_position (self, new_pos);
2572     }
2573
2574   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2575     clutter_text_clear_selection (self);
2576
2577   g_object_thaw_notify (G_OBJECT (self));
2578
2579   return TRUE;
2580 }
2581
2582 static gboolean
2583 clutter_text_real_move_right (ClutterText         *self,
2584                               const gchar         *action,
2585                               guint                keyval,
2586                               ClutterModifierType  modifiers)
2587 {
2588   ClutterTextPrivate *priv = self->priv;
2589   gint pos = priv->position;
2590   gint len = clutter_text_buffer_get_length (get_buffer (self));
2591   gint new_pos = 0;
2592
2593   g_object_freeze_notify (G_OBJECT (self));
2594
2595   if (pos != -1 && len !=0)
2596     {
2597       if (modifiers & CLUTTER_CONTROL_MASK)
2598         {
2599           if (pos != len)
2600             new_pos = clutter_text_move_word_forward (self, pos);
2601         }
2602       else
2603         {
2604           if (pos != len)
2605             new_pos = pos + 1;
2606         }
2607
2608       clutter_text_set_cursor_position (self, new_pos);
2609     }
2610
2611   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2612     clutter_text_clear_selection (self);
2613
2614   g_object_thaw_notify (G_OBJECT (self));
2615
2616   return TRUE;
2617 }
2618
2619 static gboolean
2620 clutter_text_real_move_up (ClutterText         *self,
2621                            const gchar         *action,
2622                            guint                keyval,
2623                            ClutterModifierType  modifiers)
2624 {
2625   ClutterTextPrivate *priv = self->priv;
2626   PangoLayoutLine *layout_line;
2627   PangoLayout *layout;
2628   gint line_no;
2629   gint index_, trailing;
2630   gint pos;
2631   gint x;
2632   const gchar *text;
2633
2634   layout = clutter_text_get_layout (self);
2635   text = clutter_text_buffer_get_text (get_buffer (self));
2636
2637   if (priv->position == 0)
2638     index_ = 0;
2639   else
2640     index_ = offset_to_bytes (text, priv->position);
2641
2642   pango_layout_index_to_line_x (layout, index_,
2643                                 0,
2644                                 &line_no, &x);
2645
2646   line_no -= 1;
2647   if (line_no < 0)
2648     return FALSE;
2649
2650   if (priv->x_pos != -1)
2651     x = priv->x_pos;
2652
2653   layout_line = pango_layout_get_line_readonly (layout, line_no);
2654   if (!layout_line)
2655     return FALSE;
2656
2657   pango_layout_line_x_to_index (layout_line, x, &index_, &trailing);
2658
2659   g_object_freeze_notify (G_OBJECT (self));
2660
2661   pos = bytes_to_offset (text, index_);
2662   clutter_text_set_cursor_position (self, pos + trailing);
2663
2664   /* Store the target x position to avoid drifting left and right when
2665      moving the cursor up and down */
2666   priv->x_pos = x;
2667
2668   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2669     clutter_text_clear_selection (self);
2670
2671   g_object_thaw_notify (G_OBJECT (self));
2672
2673   return TRUE;
2674 }
2675
2676 static gboolean
2677 clutter_text_real_move_down (ClutterText         *self,
2678                              const gchar         *action,
2679                              guint                keyval,
2680                              ClutterModifierType  modifiers)
2681 {
2682   ClutterTextPrivate *priv = self->priv;
2683   PangoLayoutLine *layout_line;
2684   PangoLayout *layout;
2685   gint line_no;
2686   gint index_, trailing;
2687   gint x;
2688   gint pos;
2689   const gchar *text;
2690
2691   layout = clutter_text_get_layout (self);
2692   text = clutter_text_buffer_get_text (get_buffer (self));
2693
2694   if (priv->position == 0)
2695     index_ = 0;
2696   else
2697     index_ = offset_to_bytes (text, priv->position);
2698
2699   pango_layout_index_to_line_x (layout, index_,
2700                                 0,
2701                                 &line_no, &x);
2702
2703   if (priv->x_pos != -1)
2704     x = priv->x_pos;
2705
2706   layout_line = pango_layout_get_line_readonly (layout, line_no + 1);
2707   if (!layout_line)
2708     return FALSE;
2709
2710   pango_layout_line_x_to_index (layout_line, x, &index_, &trailing);
2711
2712   g_object_freeze_notify (G_OBJECT (self));
2713
2714   pos = bytes_to_offset (text, index_);
2715   clutter_text_set_cursor_position (self, pos + trailing);
2716
2717   /* Store the target x position to avoid drifting left and right when
2718      moving the cursor up and down */
2719   priv->x_pos = x;
2720
2721   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2722     clutter_text_clear_selection (self);
2723
2724   g_object_thaw_notify (G_OBJECT (self));
2725
2726   return TRUE;
2727 }
2728
2729 static gboolean
2730 clutter_text_real_line_start (ClutterText         *self,
2731                               const gchar         *action,
2732                               guint                keyval,
2733                               ClutterModifierType  modifiers)
2734 {
2735   ClutterTextPrivate *priv = self->priv;
2736   gint position;
2737
2738   g_object_freeze_notify (G_OBJECT (self));
2739
2740   position = clutter_text_move_line_start (self, priv->position);
2741   clutter_text_set_cursor_position (self, position);
2742
2743   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2744     clutter_text_clear_selection (self);
2745
2746   g_object_thaw_notify (G_OBJECT (self));
2747
2748   return TRUE;
2749 }
2750
2751 static gboolean
2752 clutter_text_real_line_end (ClutterText         *self,
2753                             const gchar         *action,
2754                             guint                keyval,
2755                             ClutterModifierType  modifiers)
2756 {
2757   ClutterTextPrivate *priv = self->priv;
2758   gint position;
2759
2760   g_object_freeze_notify (G_OBJECT (self));
2761
2762   position = clutter_text_move_line_end (self, priv->position);
2763   clutter_text_set_cursor_position (self, position);
2764
2765   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
2766     clutter_text_clear_selection (self);
2767
2768   g_object_thaw_notify (G_OBJECT (self));
2769
2770   return TRUE;
2771 }
2772
2773 static gboolean
2774 clutter_text_real_select_all (ClutterText         *self,
2775                               const gchar         *action,
2776                               guint                keyval,
2777                               ClutterModifierType  modifiers)
2778 {
2779   guint n_chars = clutter_text_buffer_get_length (get_buffer (self));
2780   clutter_text_set_positions (self, 0, n_chars);
2781
2782   return TRUE;
2783 }
2784
2785 static gboolean
2786 clutter_text_real_del_next (ClutterText         *self,
2787                             const gchar         *action,
2788                             guint                keyval,
2789                             ClutterModifierType  modifiers)
2790 {
2791   ClutterTextPrivate *priv = self->priv;
2792   gint pos;
2793   gint len;
2794
2795   if (clutter_text_delete_selection (self))
2796     return TRUE;
2797
2798   pos = priv->position;
2799   len = clutter_text_buffer_get_length (get_buffer (self));
2800
2801   if (len && pos != -1 && pos < len)
2802     clutter_text_delete_text (self, pos, pos + 1);
2803
2804   return TRUE;
2805 }
2806
2807 static gboolean
2808 clutter_text_real_del_word_next (ClutterText         *self,
2809                                  const gchar         *action,
2810                                  guint                keyval,
2811                                  ClutterModifierType  modifiers)
2812 {
2813   ClutterTextPrivate *priv = self->priv;
2814   gint pos;
2815   gint len;
2816
2817   pos = priv->position;
2818   len = clutter_text_buffer_get_length (get_buffer (self));
2819
2820   if (len && pos != -1 && pos < len)
2821     {
2822       gint end;
2823
2824       end = clutter_text_move_word_forward (self, pos);
2825       clutter_text_delete_text (self, pos, end);
2826
2827       if (priv->selection_bound >= end)
2828         {
2829           gint new_bound;
2830
2831           new_bound = priv->selection_bound - (end - pos);
2832           clutter_text_set_selection_bound (self, new_bound);
2833         }
2834       else if (priv->selection_bound > pos)
2835         {
2836           clutter_text_set_selection_bound (self, pos);
2837         }
2838     }
2839
2840   return TRUE;
2841 }
2842
2843 static gboolean
2844 clutter_text_real_del_prev (ClutterText         *self,
2845                             const gchar         *action,
2846                             guint                keyval,
2847                             ClutterModifierType  modifiers)
2848 {
2849   ClutterTextPrivate *priv = self->priv;
2850   gint pos;
2851   gint len;
2852
2853   if (clutter_text_delete_selection (self))
2854     return TRUE;
2855
2856   pos = priv->position;
2857   len = clutter_text_buffer_get_length (get_buffer (self));
2858
2859   if (pos != 0 && len != 0)
2860     {
2861       if (pos == -1)
2862         {
2863           clutter_text_delete_text (self, len - 1, len);
2864
2865           clutter_text_set_positions (self, -1, -1);
2866         }
2867       else
2868         {
2869           clutter_text_delete_text (self, pos - 1, pos);
2870
2871           clutter_text_set_positions (self, pos - 1, pos - 1);
2872         }
2873     }
2874
2875   return TRUE;
2876 }
2877
2878 static gboolean
2879 clutter_text_real_del_word_prev (ClutterText         *self,
2880                                  const gchar         *action,
2881                                  guint                keyval,
2882                                  ClutterModifierType  modifiers)
2883 {
2884   ClutterTextPrivate *priv = self->priv;
2885   gint pos;
2886   gint len;
2887
2888   pos = priv->position;
2889   len = clutter_text_buffer_get_length (get_buffer (self));
2890
2891   if (pos != 0 && len != 0)
2892     {
2893       gint new_pos;
2894
2895       if (pos == -1)
2896         {
2897           new_pos = clutter_text_move_word_backward (self, len);
2898           clutter_text_delete_text (self, new_pos, len);
2899
2900           clutter_text_set_positions (self, -1, -1);
2901         }
2902       else
2903         {
2904           new_pos = clutter_text_move_word_backward (self, pos);
2905           clutter_text_delete_text (self, new_pos, pos);
2906
2907           clutter_text_set_cursor_position (self, new_pos);
2908           if (priv->selection_bound >= pos)
2909             {
2910               gint new_bound;
2911
2912               new_bound = priv->selection_bound - (pos - new_pos);
2913               clutter_text_set_selection_bound (self, new_bound);
2914             }
2915           else if (priv->selection_bound >= new_pos)
2916             {
2917               clutter_text_set_selection_bound (self, new_pos);
2918             }
2919         }
2920     }
2921
2922   return TRUE;
2923 }
2924
2925 static gboolean
2926 clutter_text_real_activate (ClutterText         *self,
2927                             const gchar         *action,
2928                             guint                keyval,
2929                             ClutterModifierType  modifiers)
2930 {
2931   return clutter_text_activate (self);
2932 }
2933
2934 static inline void
2935 clutter_text_add_move_binding (ClutterBindingPool  *pool,
2936                                const gchar         *action,
2937                                guint                key_val,
2938                                ClutterModifierType  additional_modifiers,
2939                                GCallback            callback)
2940 {
2941   clutter_binding_pool_install_action (pool, action,
2942                                        key_val,
2943                                        0,
2944                                        callback,
2945                                        NULL, NULL);
2946   clutter_binding_pool_install_action (pool, action,
2947                                        key_val,
2948                                        CLUTTER_SHIFT_MASK,
2949                                        callback,
2950                                        NULL, NULL);
2951
2952   if (additional_modifiers != 0)
2953     {
2954       clutter_binding_pool_install_action (pool, action,
2955                                            key_val,
2956                                            additional_modifiers,
2957                                            callback,
2958                                            NULL, NULL);
2959       clutter_binding_pool_install_action (pool, action,
2960                                            key_val,
2961                                            CLUTTER_SHIFT_MASK |
2962                                              additional_modifiers,
2963                                            callback,
2964                                            NULL, NULL);
2965     }
2966 }
2967
2968 static gboolean
2969 clutter_text_parse_custom_node (ClutterScriptable *scriptable,
2970                                 ClutterScript     *script,
2971                                 GValue            *value,
2972                                 const gchar       *name,
2973                                 JsonNode          *node)
2974 {
2975   if (strncmp (name, "font-description", 16) == 0)
2976     {
2977       g_value_init (value, G_TYPE_STRING);
2978       g_value_set_string (value, json_node_get_string (node));
2979
2980       return TRUE;
2981     }
2982
2983   return FALSE;
2984 }
2985
2986 static void
2987 clutter_text_set_color_internal (ClutterText        *self,
2988                                  GParamSpec         *pspec,
2989                                  const ClutterColor *color)
2990 {
2991   ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
2992   GParamSpec *other = NULL;
2993
2994   switch (pspec->param_id)
2995     {
2996     case PROP_COLOR:
2997       priv->text_color = *color;
2998       break;
2999
3000     case PROP_CURSOR_COLOR:
3001       if (color)
3002         {
3003           priv->cursor_color = *color;
3004           priv->cursor_color_set = TRUE;
3005         }
3006       else
3007         priv->cursor_color_set = FALSE;
3008
3009       other = obj_props[PROP_CURSOR_COLOR_SET];
3010       break;
3011
3012     case PROP_SELECTION_COLOR:
3013       if (color)
3014         {
3015           priv->selection_color = *color;
3016           priv->selection_color_set = TRUE;
3017         }
3018       else
3019         priv->selection_color_set = FALSE;
3020
3021       other = obj_props[PROP_SELECTION_COLOR_SET];
3022       break;
3023
3024     case PROP_SELECTED_TEXT_COLOR:
3025       if (color)
3026         {
3027           priv->selected_text_color = *color;
3028           priv->selected_text_color_set = TRUE;
3029         }
3030       else
3031         priv->selected_text_color_set = FALSE;
3032
3033       other = obj_props[PROP_SELECTED_TEXT_COLOR_SET];
3034       break;
3035
3036     default:
3037       g_assert_not_reached ();
3038       break;
3039     }
3040
3041   clutter_text_queue_redraw (CLUTTER_ACTOR (self));
3042   g_object_notify_by_pspec (G_OBJECT (self), pspec);
3043   if (other)
3044     g_object_notify_by_pspec (G_OBJECT (self), other);
3045 }
3046
3047 static void
3048 clutter_text_set_color_animated (ClutterText        *self,
3049                                  GParamSpec         *pspec,
3050                                  const ClutterColor *color)
3051 {
3052   ClutterActor *actor = CLUTTER_ACTOR (self);
3053   ClutterTextPrivate *priv = self->priv;
3054   const ClutterAnimationInfo *info;
3055   ClutterTransition *transition;
3056
3057   info = _clutter_actor_get_animation_info (actor);
3058   transition = clutter_actor_get_transition (actor, pspec->name);
3059
3060   /* jump to the end if there is no easing state, or if the easing
3061    * state has a duration of 0 msecs
3062    */
3063   if (info->cur_state == NULL ||
3064       info->cur_state->easing_duration == 0)
3065     {
3066       /* ensure that we remove any currently running transition */
3067       if (transition != NULL)
3068         {
3069           clutter_actor_remove_transition (actor, pspec->name);
3070           transition = NULL;
3071         }
3072
3073       clutter_text_set_color_internal (self, pspec, color);
3074
3075       return;
3076     }
3077
3078   if (transition == NULL)
3079     {
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);
3084
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);
3088
3089       clutter_actor_add_transition (actor, pspec->name, transition);
3090
3091       /* the actor now owns the transition */
3092       g_object_unref (transition);
3093     }
3094
3095   /* if a transition already exist, update its bounds */
3096   switch (pspec->param_id)
3097     {
3098     case PROP_COLOR:
3099       clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3100                                    &priv->text_color);
3101       break;
3102
3103     case PROP_CURSOR_COLOR:
3104       clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3105                                    &priv->cursor_color);
3106       break;
3107
3108     case PROP_SELECTION_COLOR:
3109       clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3110                                    &priv->selection_color);
3111       break;
3112
3113     case PROP_SELECTED_TEXT_COLOR:
3114       clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
3115                                    &priv->selected_text_color);
3116       break;
3117
3118     default:
3119       g_assert_not_reached ();
3120     }
3121
3122   clutter_transition_set_to (transition, CLUTTER_TYPE_COLOR, color);
3123
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);
3129
3130   /* ensure that we start from the beginning */
3131   clutter_timeline_rewind (CLUTTER_TIMELINE (transition));
3132   clutter_timeline_start (CLUTTER_TIMELINE (transition));
3133 }
3134
3135 static void
3136 clutter_text_set_custom_property (ClutterScriptable *scriptable,
3137                                   ClutterScript     *script,
3138                                   const gchar       *name,
3139                                   const GValue      *value)
3140 {
3141   if (strncmp (name, "font-description", 16) == 0)
3142     {
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));
3147     }
3148   else
3149     g_object_set_property (G_OBJECT (scriptable), name, value);
3150 }
3151
3152 static void
3153 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
3154 {
3155   iface->parse_custom_node = clutter_text_parse_custom_node;
3156   iface->set_custom_property = clutter_text_set_custom_property;
3157 }
3158
3159 static void
3160 clutter_text_set_final_state (ClutterAnimatable *animatable,
3161                               const char        *property_name,
3162                               const GValue      *value)
3163 {
3164   if (strcmp (property_name, "color") == 0)
3165     {
3166       const ClutterColor *color = clutter_value_get_color (value);
3167       clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3168                                        obj_props[PROP_COLOR], color);
3169     }
3170   else if (strcmp (property_name, "cursor-color") == 0)
3171     {
3172       const ClutterColor *color = clutter_value_get_color (value);
3173       clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3174                                        obj_props[PROP_CURSOR_COLOR],
3175                                        color);
3176     }
3177   else if (strcmp (property_name, "selected-text-color") == 0)
3178     {
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],
3182                                        color);
3183     }
3184   else if (strcmp (property_name, "selection-color") == 0)
3185     {
3186       const ClutterColor *color = clutter_value_get_color (value);
3187       clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
3188                                        obj_props[PROP_SELECTION_COLOR],
3189                                        color);
3190     }
3191   else
3192     parent_animatable_iface->set_final_state (animatable, property_name, value);
3193 }
3194
3195 static void
3196 clutter_animatable_iface_init (ClutterAnimatableIface *iface)
3197 {
3198   parent_animatable_iface = g_type_interface_peek_parent (iface);
3199
3200   iface->set_final_state = clutter_text_set_final_state;
3201 }
3202
3203 static void
3204 clutter_text_class_init (ClutterTextClass *klass)
3205 {
3206   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3207   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
3208   ClutterBindingPool *binding_pool;
3209   GParamSpec *pspec;
3210
3211   g_type_class_add_private (klass, sizeof (ClutterTextPrivate));
3212
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;
3217
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;
3230
3231   /**
3232    * ClutterText:buffer:
3233    *
3234    * The buffer which stores the text for this #ClutterText.
3235    *
3236    * If set to %NULL, a default buffer will be created.
3237    *
3238    * Since: 1.8
3239    */
3240   pspec = g_param_spec_object ("buffer",
3241                                P_("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);
3247
3248   /**
3249    * ClutterText:font-name:
3250    *
3251    * The font to be used by the #ClutterText, as a string
3252    * that can be parsed by pango_font_description_from_string().
3253    *
3254    * If set to %NULL, the default system font will be used instead.
3255    *
3256    * Since: 1.0
3257    */
3258   pspec = g_param_spec_string ("font-name",
3259                                P_("Font Name"),
3260                                P_("The font to be used by the text"),
3261                                NULL,
3262                                CLUTTER_PARAM_READWRITE);
3263   obj_props[PROP_FONT_NAME] = pspec;
3264   g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec);
3265
3266   /**
3267    * ClutterText:font-description:
3268    *
3269    * The #PangoFontDescription that should be used by the #ClutterText
3270    *
3271    * If you have a string describing the font then you should look at
3272    * #ClutterText:font-name instead
3273    *
3274    * Since: 1.2
3275    */
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,
3284                                    pspec);
3285
3286   /**
3287    * ClutterText:text:
3288    *
3289    * The text to render inside the actor.
3290    *
3291    * Since: 1.0
3292    */
3293   pspec = g_param_spec_string ("text",
3294                                P_("Text"),
3295                                P_("The text to render"),
3296                                "",
3297                                CLUTTER_PARAM_READWRITE);
3298   obj_props[PROP_TEXT] = pspec;
3299   g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
3300
3301   /**
3302    * ClutterText:color:
3303    *
3304    * The color used to render the text.
3305    *
3306    * Since: 1.0
3307    */
3308   pspec = clutter_param_spec_color ("color",
3309                                     P_("Font 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);
3316
3317   /**
3318    * ClutterText:editable:
3319    *
3320    * Whether key events delivered to the actor causes editing.
3321    *
3322    * Since: 1.0
3323    */
3324   pspec = g_param_spec_boolean ("editable",
3325                                 P_("Editable"),
3326                                 P_("Whether the text is editable"),
3327                                 FALSE,
3328                                 G_PARAM_READWRITE);
3329   obj_props[PROP_EDITABLE] = pspec;
3330   g_object_class_install_property (gobject_class, PROP_EDITABLE, pspec);
3331
3332   /**
3333    * ClutterText:selectable:
3334    *
3335    * Whether it is possible to select text, either using the pointer
3336    * or the keyboard.
3337    *
3338    * Since: 1.0
3339    */
3340   pspec = g_param_spec_boolean ("selectable",
3341                                 P_("Selectable"),
3342                                 P_("Whether the text is selectable"),
3343                                 TRUE,
3344                                 G_PARAM_READWRITE);
3345   obj_props[PROP_SELECTABLE] = pspec;
3346   g_object_class_install_property (gobject_class, PROP_SELECTABLE, pspec);
3347
3348   /**
3349    * ClutterText:activatable:
3350    *
3351    * Toggles whether return invokes the activate signal or not.
3352    *
3353    * Since: 1.0
3354    */
3355   pspec = g_param_spec_boolean ("activatable",
3356                                 P_("Activatable"),
3357                                 P_("Whether pressing return causes the activate signal to be emitted"),
3358                                 TRUE,
3359                                 G_PARAM_READWRITE);
3360   obj_props[PROP_ACTIVATABLE] = pspec;
3361   g_object_class_install_property (gobject_class, PROP_ACTIVATABLE, pspec);
3362
3363   /**
3364    * ClutterText:cursor-visible:
3365    *
3366    * Whether the input cursor is visible or not, it will only be visible
3367    * if both #ClutterText:cursor-visible and #ClutterText:editable are
3368    * set to %TRUE.
3369    *
3370    * Since: 1.0
3371    */
3372   pspec = g_param_spec_boolean ("cursor-visible",
3373                                 P_("Cursor Visible"),
3374                                 P_("Whether the input cursor is visible"),
3375                                 TRUE,
3376                                 CLUTTER_PARAM_READWRITE);
3377   obj_props[PROP_CURSOR_VISIBLE] = pspec;
3378   g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec);
3379
3380   /**
3381    * ClutterText:cursor-color:
3382    *
3383    * The color of the cursor.
3384    *
3385    * Since: 1.0
3386    */
3387   pspec = clutter_param_spec_color ("cursor-color",
3388                                     P_("Cursor Color"),
3389                                     P_("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);
3395
3396   /**
3397    * ClutterText:cursor-color-set:
3398    *
3399    * Will be set to %TRUE if #ClutterText:cursor-color has been set.
3400    *
3401    * Since: 1.0
3402    */
3403   pspec = g_param_spec_boolean ("cursor-color-set",
3404                                 P_("Cursor Color Set"),
3405                                 P_("Whether the cursor color has been set"),
3406                                 FALSE,
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);
3410
3411   /**
3412    * ClutterText:cursor-size:
3413    *
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.
3416    *
3417    * Since: 1.0
3418    */
3419   pspec = g_param_spec_int ("cursor-size",
3420                             P_("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);
3426
3427   /**
3428    * ClutterText:position:
3429    *
3430    * The current input cursor position. -1 is taken to be the end of the text
3431    *
3432    * Since: 1.0
3433    */
3434   pspec = g_param_spec_int ("position",
3435                             P_("Cursor Position"),
3436                             P_("The cursor position"),
3437                             -1, G_MAXINT,
3438                             -1,
3439                             CLUTTER_PARAM_READWRITE);
3440   obj_props[PROP_POSITION] = pspec;
3441   g_object_class_install_property (gobject_class, PROP_POSITION, pspec);
3442
3443   /**
3444    * ClutterText:selection-bound:
3445    *
3446    * The current input cursor position. -1 is taken to be the end of the text
3447    *
3448    * Since: 1.0
3449    */
3450   pspec = g_param_spec_int ("selection-bound",
3451                             P_("Selection-bound"),
3452                             P_("The cursor position of the other end of the selection"),
3453                             -1, G_MAXINT,
3454                             -1,
3455                             CLUTTER_PARAM_READWRITE);
3456   obj_props[PROP_SELECTION_BOUND] = pspec;
3457   g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec);
3458
3459   /**
3460    * ClutterText:selection-color:
3461    *
3462    * The color of the selection.
3463    *
3464    * Since: 1.0
3465    */
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);
3474
3475   /**
3476    * ClutterText:selection-color-set:
3477    *
3478    * Will be set to %TRUE if #ClutterText:selection-color has been set.
3479    *
3480    * Since: 1.0
3481    */
3482   pspec = g_param_spec_boolean ("selection-color-set",
3483                                 P_("Selection Color Set"),
3484                                 P_("Whether the selection color has been set"),
3485                                 FALSE,
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);
3489
3490   /**
3491    * ClutterText:attributes:
3492    *
3493    * A list of #PangoStyleAttribute<!-- -->s to be applied to the
3494    * contents of the #ClutterText actor.
3495    *
3496    * Since: 1.0
3497    */
3498   pspec = g_param_spec_boxed ("attributes",
3499                               P_("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);
3505
3506   /**
3507    * ClutterText:use-markup:
3508    *
3509    * Whether the text includes Pango markup.
3510    *
3511    * For more informations about the Pango markup format, see
3512    * pango_layout_set_markup() in the Pango documentation.
3513    *
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>
3518    *
3519    * Since: 1.0
3520    */
3521   pspec = g_param_spec_boolean ("use-markup",
3522                                 P_("Use markup"),
3523                                 P_("Whether or not the text includes Pango markup"),
3524                                 FALSE,
3525                                 CLUTTER_PARAM_READWRITE);
3526   obj_props[PROP_USE_MARKUP] = pspec;
3527   g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec);
3528
3529   /**
3530    * ClutterText:line-wrap:
3531    *
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.
3535    *
3536    * Since: 1.0
3537    */
3538   pspec = g_param_spec_boolean ("line-wrap",
3539                                 P_("Line wrap"),
3540                                 P_("If set, wrap the lines if the text becomes too wide"),
3541                                 FALSE,
3542                                 CLUTTER_PARAM_READWRITE);
3543   obj_props[PROP_LINE_WRAP] = pspec;
3544   g_object_class_install_property (gobject_class, PROP_LINE_WRAP, pspec);
3545
3546   /**
3547    * ClutterText:line-wrap-mode:
3548    *
3549    * If #ClutterText:line-wrap is set to %TRUE, this property will
3550    * control how the text is wrapped.
3551    *
3552    * Since: 1.0
3553    */
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,
3558                              PANGO_WRAP_WORD,
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);
3562
3563   /**
3564    * ClutterText:ellipsize:
3565    *
3566    * The preferred place to ellipsize the contents of the #ClutterText actor
3567    *
3568    * Since: 1.0
3569    */
3570   pspec = g_param_spec_enum ("ellipsize",
3571                              P_("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);
3578
3579   /**
3580    * ClutterText:line-alignment:
3581    *
3582    * The preferred alignment for the text. This property controls
3583    * the alignment of multi-line paragraphs.
3584    *
3585    * Since: 1.0
3586    */
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,
3591                              PANGO_ALIGN_LEFT,
3592                              CLUTTER_PARAM_READWRITE);
3593   obj_props[PROP_LINE_ALIGNMENT] = pspec;
3594   g_object_class_install_property (gobject_class, PROP_LINE_ALIGNMENT, pspec);
3595
3596   /**
3597    * ClutterText:justify:
3598    *
3599    * Whether the contents of the #ClutterText should be justified
3600    * on both margins.
3601    *
3602    * Since: 1.0
3603    */
3604   pspec = g_param_spec_boolean ("justify",
3605                                 P_("Justify"),
3606                                 P_("Whether the text should be justified"),
3607                                 FALSE,
3608                                 CLUTTER_PARAM_READWRITE);
3609   obj_props[PROP_JUSTIFY] = pspec;
3610   g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec);
3611
3612   /**
3613    * ClutterText:password-char:
3614    *
3615    * If non-zero, the character that should be used in place of
3616    * the actual text in a password text actor.
3617    *
3618    * Since: 1.0
3619    */
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"),
3623                                 0,
3624                                 CLUTTER_PARAM_READWRITE);
3625   obj_props[PROP_PASSWORD_CHAR] = pspec;
3626   g_object_class_install_property (gobject_class, PROP_PASSWORD_CHAR, pspec);
3627
3628   /**
3629    * ClutterText:max-length:
3630    *
3631    * The maximum length of the contents of the #ClutterText actor.
3632    *
3633    * Since: 1.0
3634    */
3635   pspec = g_param_spec_int ("max-length",
3636                             P_("Max Length"),
3637                             P_("Maximum length of the text inside the actor"),
3638                             -1, G_MAXINT, 0,
3639                             CLUTTER_PARAM_READWRITE);
3640   obj_props[PROP_MAX_LENGTH] = pspec;
3641   g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec);
3642
3643   /**
3644    * ClutterText:single-line-mode:
3645    *
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.
3650    *
3651    * Setting this property will also set the #ClutterText:activatable
3652    * property as a side-effect.
3653    *
3654    * The #ClutterText:single-line-mode property is used only if the
3655    * #ClutterText:editable property is set to %TRUE.
3656    *
3657    * Since: 1.0
3658    */
3659   pspec = g_param_spec_boolean ("single-line-mode",
3660                                 P_("Single Line Mode"),
3661                                 P_("Whether the text should be a single line"),
3662                                 FALSE,
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);
3666
3667   /**
3668    * ClutterText:selected-text-color:
3669    *
3670    * The color of selected text.
3671    *
3672    * Since: 1.8
3673    */
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);
3682
3683   /**
3684    * ClutterText:selected-text-color-set:
3685    *
3686    * Will be set to %TRUE if #ClutterText:selected-text-color has been set.
3687    *
3688    * Since: 1.8
3689    */
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"),
3693                                 FALSE,
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);
3697
3698   /**
3699    * ClutterText::text-changed:
3700    * @self: the #ClutterText that emitted the signal
3701    *
3702    * The ::text-changed signal is emitted after @actor's text changes
3703    *
3704    * Since: 1.0
3705    */
3706   text_signals[TEXT_CHANGED] =
3707     g_signal_new (I_("text-changed"),
3708                   G_TYPE_FROM_CLASS (gobject_class),
3709                   G_SIGNAL_RUN_LAST,
3710                   G_STRUCT_OFFSET (ClutterTextClass, text_changed),
3711                   NULL, NULL,
3712                   _clutter_marshal_VOID__VOID,
3713                   G_TYPE_NONE, 0);
3714
3715   /**
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
3724    *     inserted text.
3725    *
3726    * This signal is emitted when text is inserted into the actor by
3727    * the user. It is emitted before @self text changes.
3728    *
3729    * Since: 1.2
3730    */
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,
3735                   0,
3736                   NULL, NULL,
3737                   _clutter_marshal_VOID__STRING_INT_POINTER,
3738                   G_TYPE_NONE, 3,
3739                   G_TYPE_STRING,
3740                   G_TYPE_INT,
3741                   G_TYPE_POINTER);
3742
3743   /**
3744    * ClutterText::delete-text:
3745    * @self: the #ClutterText that emitted the signal
3746    * @start_pos: the starting position
3747    * @end_pos: the end position
3748    *
3749    * This signal is emitted when text is deleted from the actor by
3750    * the user. It is emitted before @self text changes.
3751    *
3752    * Since: 1.2
3753    */
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,
3758                   0,
3759                   NULL, NULL,
3760                   _clutter_marshal_VOID__INT_INT,
3761                   G_TYPE_NONE, 2,
3762                   G_TYPE_INT,
3763                   G_TYPE_INT);
3764
3765   /**
3766    * ClutterText::cursor-event:
3767    * @self: the #ClutterText that emitted the signal
3768    * @geometry: the coordinates of the cursor
3769    *
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
3773    * itself.
3774    *
3775    * Since: 1.0
3776    */
3777   text_signals[CURSOR_EVENT] =
3778     g_signal_new (I_("cursor-event"),
3779                   G_TYPE_FROM_CLASS (gobject_class),
3780                   G_SIGNAL_RUN_LAST,
3781                   G_STRUCT_OFFSET (ClutterTextClass, cursor_event),
3782                   NULL, NULL,
3783                   _clutter_marshal_VOID__BOXED,
3784                   G_TYPE_NONE, 1,
3785                   CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE);
3786
3787   /**
3788    * ClutterText::activate:
3789    * @self: the #ClutterText that emitted the signal
3790    *
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.
3794    *
3795    * Since: 1.0
3796    */
3797   text_signals[ACTIVATE] =
3798     g_signal_new (I_("activate"),
3799                   G_TYPE_FROM_CLASS (gobject_class),
3800                   G_SIGNAL_RUN_LAST,
3801                   G_STRUCT_OFFSET (ClutterTextClass, activate),
3802                   NULL, NULL,
3803                   _clutter_marshal_VOID__VOID,
3804                   G_TYPE_NONE, 0);
3805
3806   binding_pool = clutter_binding_pool_get_for_class (klass);
3807
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",
3821                                  CLUTTER_KEY_Up, 0,
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));
3832
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",
3843                                  CLUTTER_KEY_End, 0,
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));
3848
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),
3852                                        NULL, NULL);
3853
3854   clutter_binding_pool_install_action (binding_pool, "delete-next",
3855                                        CLUTTER_KEY_Delete, 0,
3856                                        G_CALLBACK (clutter_text_real_del_next),
3857                                        NULL, NULL);
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),
3861                                        NULL, NULL);
3862   clutter_binding_pool_install_action (binding_pool, "delete-next",
3863                                        CLUTTER_KEY_KP_Delete, 0,
3864                                        G_CALLBACK (clutter_text_real_del_next),
3865                                        NULL, NULL);
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),
3869                                        NULL, NULL);
3870   clutter_binding_pool_install_action (binding_pool, "delete-prev",
3871                                        CLUTTER_KEY_BackSpace, 0,
3872                                        G_CALLBACK (clutter_text_real_del_prev),
3873                                        NULL, NULL);
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),
3877                                        NULL, NULL);
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),
3881                                        NULL, NULL);
3882
3883   clutter_binding_pool_install_action (binding_pool, "activate",
3884                                        CLUTTER_KEY_Return, 0,
3885                                        G_CALLBACK (clutter_text_real_activate),
3886                                        NULL, NULL);
3887   clutter_binding_pool_install_action (binding_pool, "activate",
3888                                        CLUTTER_KEY_KP_Enter, 0,
3889                                        G_CALLBACK (clutter_text_real_activate),
3890                                        NULL, NULL);
3891   clutter_binding_pool_install_action (binding_pool, "activate",
3892                                        CLUTTER_KEY_ISO_Enter, 0,
3893                                        G_CALLBACK (clutter_text_real_activate),
3894                                        NULL, NULL);
3895 }
3896
3897 static void
3898 clutter_text_init (ClutterText *self)
3899 {
3900   ClutterSettings *settings;
3901   ClutterTextPrivate *priv;
3902   gchar *font_name;
3903   int i, password_hint_time;
3904
3905   self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self);
3906
3907   priv->alignment     = PANGO_ALIGN_LEFT;
3908   priv->wrap          = FALSE;
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;
3914
3915   for (i = 0; i < N_CACHED_LAYOUTS; i++)
3916     priv->cached_layouts[i].layout = NULL;
3917
3918   /* default to "" so that clutter_text_get_text() will
3919    * return a valid string and we can safely call strlen()
3920    * or strcmp() on it
3921    */
3922   priv->buffer = NULL;
3923
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;
3928
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
3932    */
3933   settings = clutter_settings_get_default ();
3934   g_object_get (settings,
3935                 "font-name", &font_name,
3936                 "password-hint-time", &password_hint_time,
3937                 NULL);
3938
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;
3942
3943   priv->position = -1;
3944   priv->selection_bound = -1;
3945
3946   priv->x_pos = -1;
3947   priv->cursor_visible = TRUE;
3948   priv->editable = FALSE;
3949   priv->selectable = TRUE;
3950
3951   priv->selection_color_set = FALSE;
3952   priv->cursor_color_set = FALSE;
3953   priv->selected_text_color_set = FALSE;
3954   priv->preedit_set = FALSE;
3955
3956   priv->password_char = 0;
3957   priv->show_password_hint = password_hint_time > 0;
3958   priv->password_hint_timeout = password_hint_time;
3959
3960   priv->text_y = 0;
3961
3962   priv->cursor_size = DEFAULT_CURSOR_SIZE;
3963   memset (&priv->cursor_pos, 0, sizeof (ClutterGeometry));
3964
3965   priv->settings_changed_id =
3966     g_signal_connect_swapped (clutter_get_default_backend (),
3967                               "settings-changed",
3968                               G_CALLBACK (clutter_text_settings_changed_cb),
3969                               self);
3970
3971   priv->direction_changed_id =
3972     g_signal_connect (self, "notify::text-direction",
3973                       G_CALLBACK (clutter_text_direction_changed_cb),
3974                       NULL);
3975 }
3976
3977 /**
3978  * clutter_text_new:
3979  *
3980  * Creates a new #ClutterText actor. This actor can be used to
3981  * display and edit text.
3982  *
3983  * Return value: the newly created #ClutterText actor
3984  *
3985  * Since: 1.0
3986  */
3987 ClutterActor *
3988 clutter_text_new (void)
3989 {
3990   return g_object_new (CLUTTER_TYPE_TEXT, NULL);
3991 }
3992
3993 /**
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
3998  *
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.
4002  *
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().
4006  *
4007  * Return value: the newly created #ClutterText actor
4008  *
4009  * Since: 1.0
4010  */
4011 ClutterActor *
4012 clutter_text_new_full (const gchar        *font_name,
4013                        const gchar        *text,
4014                        const ClutterColor *color)
4015 {
4016   return g_object_new (CLUTTER_TYPE_TEXT,
4017                        "font-name", font_name,
4018                        "text", text,
4019                        "color", color,
4020                        NULL);
4021 }
4022
4023 /**
4024  * clutter_text_new_with_text:
4025  * @font_name: (allow-none): a string with a font description
4026  * @text: the contents of the actor
4027  *
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.
4030  *
4031  * This function is equivalent to calling clutter_text_new(),
4032  * clutter_text_set_font_name(), and clutter_text_set_text().
4033  *
4034  * Return value: the newly created #ClutterText actor
4035  *
4036  * Since: 1.0
4037  */
4038 ClutterActor *
4039 clutter_text_new_with_text (const gchar *font_name,
4040                             const gchar *text)
4041 {
4042   return g_object_new (CLUTTER_TYPE_TEXT,
4043                        "font-name", font_name,
4044                        "text", text,
4045                        NULL);
4046 }
4047
4048 static ClutterTextBuffer*
4049 get_buffer (ClutterText *self)
4050 {
4051   ClutterTextPrivate *priv = self->priv;
4052
4053   if (priv->buffer == NULL)
4054     {
4055       ClutterTextBuffer *buffer;
4056       buffer = clutter_text_buffer_new ();
4057       clutter_text_set_buffer (self, buffer);
4058       g_object_unref (buffer);
4059     }
4060
4061   return priv->buffer;
4062 }
4063
4064 /* GtkEntryBuffer signal handlers
4065  */
4066 static void
4067 buffer_inserted_text (ClutterTextBuffer *buffer,
4068                       guint              position,
4069                       const gchar       *chars,
4070                       guint              n_chars,
4071                       ClutterText       *self)
4072 {
4073   ClutterTextPrivate *priv;
4074   gint new_position;
4075   gint new_selection_bound;
4076   gsize n_bytes;
4077
4078   priv = self->priv;
4079   if (priv->position >= 0 || priv->selection_bound >= 0)
4080     {
4081       new_position = priv->position;
4082       new_selection_bound = priv->selection_bound;
4083
4084       if (position <= new_position)
4085         new_position += n_chars;
4086       if (position <= new_selection_bound)
4087         new_selection_bound += n_chars;
4088
4089       if (priv->position != new_position || priv->selection_bound != new_selection_bound)
4090         clutter_text_set_positions (self, new_position, new_selection_bound);
4091     }
4092
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);
4096
4097   /* TODO: What are we supposed to with the out value of position? */
4098 }
4099
4100 static void
4101 buffer_deleted_text (ClutterTextBuffer *buffer,
4102                      guint              position,
4103                      guint              n_chars,
4104                      ClutterText       *self)
4105 {
4106   ClutterTextPrivate *priv;
4107   gint new_position;
4108   gint new_selection_bound;
4109
4110   priv = self->priv;
4111   if (priv->position >= 0 || priv->selection_bound >= 0)
4112     {
4113       new_position = priv->position;
4114       new_selection_bound = priv->selection_bound;
4115
4116       if (position < new_position)
4117         new_position -= n_chars;
4118       if (position < new_selection_bound)
4119         new_selection_bound -= n_chars;
4120
4121       if (priv->position != new_position || priv->selection_bound != new_selection_bound)
4122         clutter_text_set_positions (self, new_position, new_selection_bound);
4123     }
4124
4125   g_signal_emit (self, text_signals[DELETE_TEXT], 0, position, position + n_chars);
4126 }
4127
4128 static void
4129 buffer_notify_text (ClutterTextBuffer *buffer,
4130                     GParamSpec        *spec,
4131                     ClutterText       *self)
4132 {
4133   g_object_freeze_notify (G_OBJECT (self));
4134
4135   clutter_text_dirty_cache (self);
4136
4137   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
4138
4139   g_signal_emit (self, text_signals[TEXT_CHANGED], 0);
4140   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT]);
4141
4142   g_object_thaw_notify (G_OBJECT (self));
4143 }
4144
4145 static void
4146 buffer_notify_max_length (ClutterTextBuffer *buffer,
4147                           GParamSpec        *spec,
4148                           ClutterText       *self)
4149 {
4150   g_object_notify (G_OBJECT (self), "max-length");
4151 }
4152
4153 static void
4154 buffer_connect_signals (ClutterText *self)
4155 {
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);
4161 }
4162
4163 static void
4164 buffer_disconnect_signals (ClutterText *self)
4165 {
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);
4171 }
4172
4173 /**
4174  * clutter_text_new_with_buffer:
4175  * @buffer: The buffer to use for the new #ClutterText.
4176  *
4177  * Creates a new entry with the specified text buffer.
4178  *
4179  * Return value: a new #ClutterText
4180  *
4181  * Since: 1.10
4182  */
4183 ClutterActor *
4184 clutter_text_new_with_buffer (ClutterTextBuffer *buffer)
4185 {
4186   g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), NULL);
4187   return g_object_new (CLUTTER_TYPE_TEXT, "buffer", buffer, NULL);
4188 }
4189
4190 /**
4191  * clutter_text_get_buffer:
4192  * @self: a #ClutterText
4193  *
4194  * Get the #ClutterTextBuffer object which holds the text for
4195  * this widget.
4196  *
4197  * Returns: (transfer none): A #GtkEntryBuffer object.
4198  *
4199  * Since: 1.10
4200  */
4201 ClutterTextBuffer*
4202 clutter_text_get_buffer (ClutterText *self)
4203 {
4204   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4205
4206   return get_buffer (self);
4207 }
4208
4209 /**
4210  * clutter_text_set_buffer:
4211  * @self: a #ClutterText
4212  * @buffer: a #ClutterTextBuffer
4213  *
4214  * Set the #ClutterTextBuffer object which holds the text for
4215  * this widget.
4216  *
4217  * Since: 1.10
4218  */
4219 void
4220 clutter_text_set_buffer (ClutterText       *self,
4221                          ClutterTextBuffer *buffer)
4222 {
4223   ClutterTextPrivate *priv;
4224   GObject *obj;
4225
4226   g_return_if_fail (CLUTTER_IS_TEXT (self));
4227
4228   priv = self->priv;
4229
4230   if (buffer)
4231     {
4232       g_return_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer));
4233       g_object_ref (buffer);
4234     }
4235
4236   if (priv->buffer)
4237     {
4238       buffer_disconnect_signals (self);
4239       g_object_unref (priv->buffer);
4240     }
4241
4242   priv->buffer = buffer;
4243
4244   if (priv->buffer)
4245      buffer_connect_signals (self);
4246
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);
4253 }
4254
4255 /**
4256  * clutter_text_set_editable:
4257  * @self: a #ClutterText
4258  * @editable: whether the #ClutterText should be editable
4259  *
4260  * Sets whether the #ClutterText actor should be editable.
4261  *
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.
4265  *
4266  * Since: 1.0
4267  */
4268 void
4269 clutter_text_set_editable (ClutterText *self,
4270                            gboolean     editable)
4271 {
4272   ClutterTextPrivate *priv;
4273
4274   g_return_if_fail (CLUTTER_IS_TEXT (self));
4275
4276   priv = self->priv;
4277
4278   if (priv->editable != editable)
4279     {
4280       priv->editable = editable;
4281
4282       clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4283
4284       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EDITABLE]);
4285     }
4286 }
4287
4288 /**
4289  * clutter_text_get_editable:
4290  * @self: a #ClutterText
4291  *
4292  * Retrieves whether a #ClutterText is editable or not.
4293  *
4294  * Return value: %TRUE if the actor is editable
4295  *
4296  * Since: 1.0
4297  */
4298 gboolean
4299 clutter_text_get_editable (ClutterText *self)
4300 {
4301   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
4302
4303   return self->priv->editable;
4304 }
4305
4306 /**
4307  * clutter_text_set_selectable:
4308  * @self: a #ClutterText
4309  * @selectable: whether the #ClutterText actor should be selectable
4310  *
4311  * Sets whether a #ClutterText actor should be selectable.
4312  *
4313  * A selectable #ClutterText will allow selecting its contents using
4314  * the pointer or the keyboard.
4315  *
4316  * Since: 1.0
4317  */
4318 void
4319 clutter_text_set_selectable (ClutterText *self,
4320                              gboolean     selectable)
4321 {
4322   ClutterTextPrivate *priv;
4323
4324   g_return_if_fail (CLUTTER_IS_TEXT (self));
4325
4326   priv = self->priv;
4327
4328   if (priv->selectable != selectable)
4329     {
4330       priv->selectable = selectable;
4331
4332       clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4333
4334       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTABLE]);
4335     }
4336 }
4337
4338 /**
4339  * clutter_text_get_selectable:
4340  * @self: a #ClutterText
4341  *
4342  * Retrieves whether a #ClutterText is selectable or not.
4343  *
4344  * Return value: %TRUE if the actor is selectable
4345  *
4346  * Since: 1.0
4347  */
4348 gboolean
4349 clutter_text_get_selectable (ClutterText *self)
4350 {
4351   g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
4352
4353   return self->priv->selectable;
4354 }
4355
4356 /**
4357  * clutter_text_set_activatable:
4358  * @self: a #ClutterText
4359  * @activatable: whether the #ClutterText actor should be activatable
4360  *
4361  * Sets whether a #ClutterText actor should be activatable.
4362  *
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.
4366  *
4367  * An activatable #ClutterText must also be set as editable using
4368  * clutter_text_set_editable().
4369  *
4370  * Since: 1.0
4371  */
4372 void
4373 clutter_text_set_activatable (ClutterText *self,
4374                               gboolean     activatable)
4375 {
4376   ClutterTextPrivate *priv;
4377
4378   g_return_if_fail (CLUTTER_IS_TEXT (self));
4379
4380   priv = self->priv;
4381
4382   if (priv->activatable != activatable)
4383     {
4384       priv->activatable = activatable;
4385
4386       clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4387
4388       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]);
4389     }
4390 }
4391
4392 /**
4393  * clutter_text_get_activatable:
4394  * @self: a #ClutterText
4395  *
4396  * Retrieves whether a #ClutterText is activatable or not.
4397  *
4398  * Return value: %TRUE if the actor is activatable
4399  *
4400  * Since: 1.0
4401  */
4402 gboolean
4403 clutter_text_get_activatable (ClutterText *self)
4404 {
4405   g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
4406
4407   return self->priv->activatable;
4408 }
4409
4410 /**
4411  * clutter_text_activate:
4412  * @self: a #ClutterText
4413  *
4414  * Emits the #ClutterText::activate signal, if @self has been set
4415  * as activatable using clutter_text_set_activatable().
4416  *
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.
4421  *
4422  * Return value: %TRUE if the ::activate signal has been emitted,
4423  *   and %FALSE otherwise
4424  *
4425  * Since: 1.0
4426  */
4427 gboolean
4428 clutter_text_activate (ClutterText *self)
4429 {
4430   ClutterTextPrivate *priv;
4431
4432   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
4433
4434   priv = self->priv;
4435
4436   if (priv->activatable)
4437     {
4438       g_signal_emit (self, text_signals[ACTIVATE], 0);
4439       return TRUE;
4440     }
4441
4442   return FALSE;
4443 }
4444
4445 /**
4446  * clutter_text_set_cursor_visible:
4447  * @self: a #ClutterText
4448  * @cursor_visible: whether the cursor should be visible
4449  *
4450  * Sets whether the cursor of a #ClutterText actor should be
4451  * visible or not.
4452  *
4453  * The color of the cursor will be the same as the text color
4454  * unless clutter_text_set_cursor_color() has been called.
4455  *
4456  * The size of the cursor can be set using clutter_text_set_cursor_size().
4457  *
4458  * The position of the cursor can be changed programmatically using
4459  * clutter_text_set_cursor_position().
4460  *
4461  * Since: 1.0
4462  */
4463 void
4464 clutter_text_set_cursor_visible (ClutterText *self,
4465                                  gboolean     cursor_visible)
4466 {
4467   ClutterTextPrivate *priv;
4468
4469   g_return_if_fail (CLUTTER_IS_TEXT (self));
4470
4471   priv = self->priv;
4472
4473   if (priv->cursor_visible != cursor_visible)
4474     {
4475       priv->cursor_visible = cursor_visible;
4476
4477       clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4478
4479       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_VISIBLE]);
4480     }
4481 }
4482
4483 /**
4484  * clutter_text_get_cursor_visible:
4485  * @self: a #ClutterText
4486  *
4487  * Retrieves whether the cursor of a #ClutterText actor is visible.
4488  *
4489  * Return value: %TRUE if the cursor is visible
4490  *
4491  * Since: 1.0
4492  */
4493 gboolean
4494 clutter_text_get_cursor_visible (ClutterText *self)
4495 {
4496   g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
4497
4498   return self->priv->cursor_visible;
4499 }
4500
4501 /**
4502  * clutter_text_set_cursor_color:
4503  * @self: a #ClutterText
4504  * @color: (allow-none): the color of the cursor, or %NULL to unset it
4505  *
4506  * Sets the color of the cursor of a #ClutterText actor.
4507  *
4508  * If @color is %NULL, the cursor color will be the same as the
4509  * text color.
4510  *
4511  * Since: 1.0
4512  */
4513 void
4514 clutter_text_set_cursor_color (ClutterText        *self,
4515                                const ClutterColor *color)
4516 {
4517   g_return_if_fail (CLUTTER_IS_TEXT (self));
4518
4519   clutter_text_set_color_animated (self, obj_props[PROP_CURSOR_COLOR], color);
4520 }
4521
4522 /**
4523  * clutter_text_get_cursor_color:
4524  * @self: a #ClutterText
4525  * @color: (out): return location for a #ClutterColor
4526  *
4527  * Retrieves the color of the cursor of a #ClutterText actor.
4528  *
4529  * Since: 1.0
4530  */
4531 void
4532 clutter_text_get_cursor_color (ClutterText  *self,
4533                                ClutterColor *color)
4534 {
4535   ClutterTextPrivate *priv;
4536
4537   g_return_if_fail (CLUTTER_IS_TEXT (self));
4538   g_return_if_fail (color != NULL);
4539
4540   priv = self->priv;
4541
4542   *color = priv->cursor_color;
4543 }
4544
4545 /**
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
4550  *
4551  * Selects the region of text between @start_pos and @end_pos.
4552  *
4553  * This function changes the position of the cursor to match
4554  * @start_pos and the selection bound to match @end_pos.
4555  *
4556  * Since: 1.0
4557  */
4558 void
4559 clutter_text_set_selection (ClutterText *self,
4560                             gssize       start_pos,
4561                             gssize       end_pos)
4562 {
4563   guint n_chars;
4564
4565   g_return_if_fail (CLUTTER_IS_TEXT (self));
4566
4567   n_chars = clutter_text_buffer_get_length (get_buffer (self));
4568   if (end_pos < 0)
4569     end_pos = n_chars;
4570
4571   start_pos = MIN (n_chars, start_pos);
4572   end_pos = MIN (n_chars, end_pos);
4573
4574   clutter_text_set_positions (self, start_pos, end_pos);
4575 }
4576
4577 /**
4578  * clutter_text_get_selection:
4579  * @self: a #ClutterText
4580  *
4581  * Retrieves the currently selected text.
4582  *
4583  * Return value: a newly allocated string containing the currently
4584  *   selected text, or %NULL. Use g_free() to free the returned
4585  *   string.
4586  *
4587  * Since: 1.0
4588  */
4589 gchar *
4590 clutter_text_get_selection (ClutterText *self)
4591 {
4592   ClutterTextPrivate *priv;
4593   gchar *str;
4594   gint len;
4595   gint start_index, end_index;
4596   gint start_offset, end_offset;
4597   const gchar *text;
4598
4599   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4600
4601   priv = self->priv;
4602
4603   start_index = priv->position;
4604   end_index = priv->selection_bound;
4605
4606   if (end_index == start_index)
4607     return g_strdup ("");
4608
4609   if ((end_index != -1 && end_index < start_index) ||
4610       start_index == -1)
4611     {
4612       gint temp = start_index;
4613       start_index = end_index;
4614       end_index = temp;
4615     }
4616
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;
4621
4622   str = g_malloc (len + 1);
4623   g_utf8_strncpy (str, text + start_offset, end_index - start_index);
4624
4625   return str;
4626 }
4627
4628 /**
4629  * clutter_text_set_selection_bound:
4630  * @self: a #ClutterText
4631  * @selection_bound: the position of the end of the selection, in characters
4632  *
4633  * Sets the other end of the selection, starting from the current
4634  * cursor position.
4635  *
4636  * If @selection_bound is -1, the selection unset.
4637  *
4638  * Since: 1.0
4639  */
4640 void
4641 clutter_text_set_selection_bound (ClutterText *self,
4642                                   gint         selection_bound)
4643 {
4644   ClutterTextPrivate *priv;
4645
4646   g_return_if_fail (CLUTTER_IS_TEXT (self));
4647
4648   priv = self->priv;
4649
4650   if (priv->selection_bound != selection_bound)
4651     {
4652       gint len = clutter_text_buffer_get_length (get_buffer (self));;
4653
4654       if (selection_bound < 0 || selection_bound >= len)
4655         priv->selection_bound = -1;
4656       else
4657         priv->selection_bound = selection_bound;
4658
4659       clutter_text_queue_redraw (CLUTTER_ACTOR (self));
4660
4661       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
4662     }
4663 }
4664
4665 /**
4666  * clutter_text_get_selection_bound:
4667  * @self: a #ClutterText
4668  *
4669  * Retrieves the other end of the selection of a #ClutterText actor,
4670  * in characters from the current cursor position.
4671  *
4672  * Return value: the position of the other end of the selection
4673  *
4674  * Since: 1.0
4675  */
4676 gint
4677 clutter_text_get_selection_bound (ClutterText *self)
4678 {
4679   g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1);
4680
4681   return self->priv->selection_bound;
4682 }
4683
4684 /**
4685  * clutter_text_set_selection_color:
4686  * @self: a #ClutterText
4687  * @color: (allow-none): the color of the selection, or %NULL to unset it
4688  *
4689  * Sets the color of the selection of a #ClutterText actor.
4690  *
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.
4694  *
4695  * Since: 1.0
4696  */
4697 void
4698 clutter_text_set_selection_color (ClutterText        *self,
4699                                   const ClutterColor *color)
4700 {
4701   g_return_if_fail (CLUTTER_IS_TEXT (self));
4702
4703   clutter_text_set_color_animated (self, obj_props[PROP_SELECTION_COLOR],
4704                                    color);
4705 }
4706
4707 /**
4708  * clutter_text_get_selection_color:
4709  * @self: a #ClutterText
4710  * @color: (out caller-allocates): return location for a #ClutterColor
4711  *
4712  * Retrieves the color of the selection of a #ClutterText actor.
4713  *
4714  * Since: 1.0
4715  */
4716 void
4717 clutter_text_get_selection_color (ClutterText  *self,
4718                                   ClutterColor *color)
4719 {
4720   ClutterTextPrivate *priv;
4721
4722   g_return_if_fail (CLUTTER_IS_TEXT (self));
4723   g_return_if_fail (color != NULL);
4724
4725   priv = self->priv;
4726
4727   *color = priv->selection_color;
4728 }
4729
4730 /**
4731  * clutter_text_set_selected_text_color:
4732  * @self: a #ClutterText
4733  * @color: (allow-none): the selected text color, or %NULL to unset it
4734  *
4735  * Sets the selected text color of a #ClutterText actor.
4736  *
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.
4739  *
4740  * Since: 1.8
4741  */
4742 void
4743 clutter_text_set_selected_text_color (ClutterText        *self,
4744                                       const ClutterColor *color)
4745 {
4746   g_return_if_fail (CLUTTER_IS_TEXT (self));
4747
4748   clutter_text_set_color_animated (self, obj_props[PROP_SELECTED_TEXT_COLOR],
4749                                    color);
4750 }
4751
4752 /**
4753  * clutter_text_get_selected_text_color:
4754  * @self: a #ClutterText
4755  * @color: (out caller-allocates): return location for a #ClutterColor
4756  *
4757  * Retrieves the color of selected text of a #ClutterText actor.
4758  *
4759  * Since: 1.8
4760  */
4761 void
4762 clutter_text_get_selected_text_color (ClutterText  *self,
4763                                       ClutterColor *color)
4764 {
4765   ClutterTextPrivate *priv;
4766
4767   g_return_if_fail (CLUTTER_IS_TEXT (self));
4768   g_return_if_fail (color != NULL);
4769
4770   priv = self->priv;
4771
4772   *color = priv->selected_text_color;
4773 }
4774
4775 /**
4776  * clutter_text_set_font_description:
4777  * @self: a #ClutterText
4778  * @font_desc: a #PangoFontDescription
4779  *
4780  * Sets @font_desc as the font description for a #ClutterText
4781  *
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.
4785  *
4786  * Since: 1.2
4787  */
4788 void
4789 clutter_text_set_font_description (ClutterText          *self,
4790                                    PangoFontDescription *font_desc)
4791 {
4792   PangoFontDescription *copy;
4793
4794   g_return_if_fail (CLUTTER_IS_TEXT (self));
4795
4796   copy = pango_font_description_copy (font_desc);
4797   clutter_text_set_font_description_internal (self, copy);
4798 }
4799
4800 /**
4801  * clutter_text_get_font_description:
4802  * @self: a #ClutterText
4803  *
4804  * Retrieves the #PangoFontDescription used by @self
4805  *
4806  * Return value: a #PangoFontDescription. The returned value is owned
4807  *   by the #ClutterText actor and it should not be modified or freed
4808  *
4809  * Since: 1.2
4810  */
4811 PangoFontDescription *
4812 clutter_text_get_font_description (ClutterText *self)
4813 {
4814   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4815
4816   return self->priv->font_desc;
4817 }
4818
4819 /**
4820  * clutter_text_get_font_name:
4821  * @self: a #ClutterText
4822  *
4823  * Retrieves the font name as set by clutter_text_set_font_name().
4824  *
4825  * Return value: a string containing the font name. The returned
4826  *   string is owned by the #ClutterText actor and should not be
4827  *   modified or freed
4828  *
4829  * Since: 1.0
4830  */
4831 const gchar *
4832 clutter_text_get_font_name (ClutterText *text)
4833 {
4834   g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL);
4835
4836   return text->priv->font_name;
4837 }
4838
4839 /**
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
4843  *
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,
4848  * like:
4849  *
4850  * |[
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");
4854  * ]|
4855  *
4856  * Since: 1.0
4857  */
4858 void
4859 clutter_text_set_font_name (ClutterText *self,
4860                             const gchar *font_name)
4861 {
4862   ClutterTextPrivate *priv;
4863   PangoFontDescription *desc;
4864   gboolean is_default_font;
4865
4866   g_return_if_fail (CLUTTER_IS_TEXT (self));
4867
4868   /* get the default font name from the backend */
4869   if (font_name == NULL || font_name[0] == '\0')
4870     {
4871       ClutterSettings *settings = clutter_settings_get_default ();
4872       gchar *default_font_name = NULL;
4873
4874       g_object_get (settings, "font-name", &default_font_name, NULL);
4875
4876       if (default_font_name != NULL)
4877         font_name = default_font_name;
4878       else
4879         {
4880           /* last fallback */
4881           default_font_name = g_strdup ("Sans 12");
4882         }
4883
4884       is_default_font = TRUE;
4885     }
4886   else
4887     is_default_font = FALSE;
4888
4889   priv = self->priv;
4890
4891   if (g_strcmp0 (priv->font_name, font_name) == 0)
4892     goto out;
4893
4894   desc = pango_font_description_from_string (font_name);
4895   if (!desc)
4896     {
4897       g_warning ("Attempting to create a PangoFontDescription for "
4898                  "font name '%s', but failed.",
4899                  font_name);
4900       goto out;
4901     }
4902
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;
4906
4907   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_NAME]);
4908
4909 out:
4910   if (is_default_font)
4911     g_free ((gchar *) font_name);
4912 }
4913
4914 /**
4915  * clutter_text_get_text:
4916  * @self: a #ClutterText
4917  *
4918  * Retrieves a pointer to the current contents of a #ClutterText
4919  * actor.
4920  *
4921  * If you need a copy of the contents for manipulating, either
4922  * use g_strdup() on the returned string, or use:
4923  *
4924  * |[
4925  *    copy = clutter_text_get_chars (text, 0, -1);
4926  * ]|
4927  *
4928  * Which will return a newly allocated string.
4929  *
4930  * If the #ClutterText actor is empty, this function will return
4931  * an empty string, and not %NULL.
4932  *
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
4935  *   or freed
4936  *
4937  * Since: 1.0
4938  */
4939 const gchar *
4940 clutter_text_get_text (ClutterText *self)
4941 {
4942   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
4943
4944   return clutter_text_buffer_get_text (get_buffer (self));
4945 }
4946
4947 static inline void
4948 clutter_text_set_use_markup_internal (ClutterText *self,
4949                                       gboolean     use_markup)
4950 {
4951   ClutterTextPrivate *priv = self->priv;
4952
4953   if (priv->use_markup != use_markup)
4954     {
4955       priv->use_markup = use_markup;
4956
4957       /* reset the attributes lists so that they can be
4958        * re-generated
4959        */
4960       if (priv->effective_attrs != NULL)
4961         {
4962           pango_attr_list_unref (priv->effective_attrs);
4963           priv->effective_attrs = NULL;
4964         }
4965
4966       if (priv->markup_attrs)
4967         {
4968           pango_attr_list_unref (priv->markup_attrs);
4969           priv->markup_attrs = NULL;
4970         }
4971
4972       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_USE_MARKUP]);
4973     }
4974 }
4975
4976 /**
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)
4981  *
4982  * Sets the contents of a #ClutterText actor.
4983  *
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
4988  *
4989  * Since: 1.0
4990  */
4991 void
4992 clutter_text_set_text (ClutterText *self,
4993                        const gchar *text)
4994 {
4995   g_return_if_fail (CLUTTER_IS_TEXT (self));
4996
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
4999    * we should avoid
5000    */
5001   if (self->priv->editable)
5002     {
5003       if (g_strcmp0 (clutter_text_buffer_get_text (get_buffer (self)), text) == 0)
5004         return;
5005     }
5006
5007   clutter_text_set_use_markup_internal (self, FALSE);
5008   clutter_text_buffer_set_text (get_buffer (self), text ? text : "", -1);
5009 }
5010
5011 /**
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)
5016  *
5017  * Sets @markup as the contents of a #ClutterText.
5018  *
5019  * This is a convenience function for setting a string containing
5020  * Pango markup, and it is logically equivalent to:
5021  *
5022  * |[
5023  *   /&ast; the order is important &ast;/
5024  *   clutter_text_set_text (CLUTTER_TEXT (actor), markup);
5025  *   clutter_text_set_use_markup (CLUTTER_TEXT (actor), TRUE);
5026  * ]|
5027  *
5028  * Since: 1.0
5029  */
5030 void
5031 clutter_text_set_markup (ClutterText *self,
5032                          const gchar *markup)
5033 {
5034   g_return_if_fail (CLUTTER_IS_TEXT (self));
5035
5036   clutter_text_set_use_markup_internal (self, TRUE);
5037   if (markup != NULL && *markup != '\0')
5038     clutter_text_set_markup_internal (self, markup);
5039   else
5040     clutter_text_buffer_set_text (get_buffer (self), "", 0);
5041 }
5042
5043 /**
5044  * clutter_text_get_layout:
5045  * @self: a #ClutterText
5046  *
5047  * Retrieves the current #PangoLayout used by a #ClutterText actor.
5048  *
5049  * Return value: (transfer none): a #PangoLayout. The returned object is owned by
5050  *   the #ClutterText actor and should not be modified or freed
5051  *
5052  * Since: 1.0
5053  */
5054 PangoLayout *
5055 clutter_text_get_layout (ClutterText *self)
5056 {
5057   gfloat width, height;
5058
5059   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
5060
5061   if (self->priv->editable && self->priv->single_line_mode)
5062     return clutter_text_create_layout (self, -1, -1);
5063
5064   clutter_actor_get_size (CLUTTER_ACTOR (self), &width, &height);
5065
5066   return clutter_text_create_layout (self, width, height);
5067 }
5068
5069 /**
5070  * clutter_text_set_color:
5071  * @self: a #ClutterText
5072  * @color: a #ClutterColor
5073  *
5074  * Sets the color of the contents of a #ClutterText actor.
5075  *
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().
5080  *
5081  * Since: 1.0
5082  */
5083 void
5084 clutter_text_set_color (ClutterText        *self,
5085                         const ClutterColor *color)
5086 {
5087   g_return_if_fail (CLUTTER_IS_TEXT (self));
5088   g_return_if_fail (color != NULL);
5089
5090   clutter_text_set_color_animated (self, obj_props[PROP_COLOR], color);
5091 }
5092
5093 /**
5094  * clutter_text_get_color:
5095  * @self: a #ClutterText
5096  * @color: (out caller-allocates): return location for a #ClutterColor
5097  *
5098  * Retrieves the text color as set by clutter_text_set_color().
5099  *
5100  * Since: 1.0
5101  */
5102 void
5103 clutter_text_get_color (ClutterText  *self,
5104                         ClutterColor *color)
5105 {
5106   ClutterTextPrivate *priv;
5107
5108   g_return_if_fail (CLUTTER_IS_TEXT (self));
5109   g_return_if_fail (color != NULL);
5110
5111   priv = self->priv;
5112
5113   *color = priv->text_color;
5114 }
5115
5116 /**
5117  * clutter_text_set_ellipsize:
5118  * @self: a #ClutterText
5119  * @mode: a #PangoEllipsizeMode
5120  *
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
5124  *
5125  * Since: 1.0
5126  */
5127 void
5128 clutter_text_set_ellipsize (ClutterText        *self,
5129                             PangoEllipsizeMode  mode)
5130 {
5131   ClutterTextPrivate *priv;
5132
5133   g_return_if_fail (CLUTTER_IS_TEXT (self));
5134   g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE &&
5135                     mode <= PANGO_ELLIPSIZE_END);
5136
5137   priv = self->priv;
5138
5139   if ((PangoEllipsizeMode) priv->ellipsize != mode)
5140     {
5141       priv->ellipsize = mode;
5142
5143       clutter_text_dirty_cache (self);
5144
5145       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5146
5147       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ELLIPSIZE]);
5148     }
5149 }
5150
5151 /**
5152  * clutter_text_get_ellipsize:
5153  * @self: a #ClutterText
5154  *
5155  * Returns the ellipsizing position of a #ClutterText actor, as
5156  * set by clutter_text_set_ellipsize().
5157  *
5158  * Return value: #PangoEllipsizeMode
5159  *
5160  * Since: 1.0
5161  */
5162 PangoEllipsizeMode
5163 clutter_text_get_ellipsize (ClutterText *self)
5164 {
5165   g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ELLIPSIZE_NONE);
5166
5167   return self->priv->ellipsize;
5168 }
5169
5170 /**
5171  * clutter_text_get_line_wrap:
5172  * @self: a #ClutterText
5173  *
5174  * Retrieves the value set using clutter_text_set_line_wrap().
5175  *
5176  * Return value: %TRUE if the #ClutterText actor should wrap
5177  *   its contents
5178  *
5179  * Since: 1.0
5180  */
5181 gboolean
5182 clutter_text_get_line_wrap (ClutterText *self)
5183 {
5184   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5185
5186   return self->priv->wrap;
5187 }
5188
5189 /**
5190  * clutter_text_set_line_wrap:
5191  * @self: a #ClutterText
5192  * @line_wrap: whether the contents should wrap
5193  *
5194  * Sets whether the contents of a #ClutterText actor should wrap,
5195  * if they don't fit the size assigned to the actor.
5196  *
5197  * Since: 1.0
5198  */
5199 void
5200 clutter_text_set_line_wrap (ClutterText *self,
5201                             gboolean     line_wrap)
5202 {
5203   ClutterTextPrivate *priv;
5204
5205   g_return_if_fail (CLUTTER_IS_TEXT (self));
5206
5207   priv = self->priv;
5208
5209   if (priv->wrap != line_wrap)
5210     {
5211       priv->wrap = line_wrap;
5212
5213       clutter_text_dirty_cache (self);
5214
5215       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5216
5217       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_WRAP]);
5218     }
5219 }
5220
5221 /**
5222  * clutter_text_set_line_wrap_mode:
5223  * @self: a #ClutterText
5224  * @wrap_mode: the line wrapping mode
5225  *
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.
5229  *
5230  * Since: 1.0
5231  */
5232 void
5233 clutter_text_set_line_wrap_mode (ClutterText   *self,
5234                                  PangoWrapMode  wrap_mode)
5235 {
5236   ClutterTextPrivate *priv;
5237
5238   g_return_if_fail (CLUTTER_IS_TEXT (self));
5239
5240   priv = self->priv;
5241
5242   if (priv->wrap_mode != wrap_mode)
5243     {
5244       priv->wrap_mode = wrap_mode;
5245
5246       clutter_text_dirty_cache (self);
5247
5248       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5249
5250       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_WRAP_MODE]);
5251     }
5252 }
5253
5254 /**
5255  * clutter_text_get_line_wrap_mode:
5256  * @self: a #ClutterText
5257  *
5258  * Retrieves the line wrap mode used by the #ClutterText actor.
5259  *
5260  * See clutter_text_set_line_wrap_mode ().
5261  *
5262  * Return value: the wrap mode used by the #ClutterText
5263  *
5264  * Since: 1.0
5265  */
5266 PangoWrapMode
5267 clutter_text_get_line_wrap_mode (ClutterText *self)
5268 {
5269   g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_WRAP_WORD);
5270
5271   return self->priv->wrap_mode;
5272 }
5273
5274 /**
5275  * clutter_text_set_attributes:
5276  * @self: a #ClutterText
5277  * @attrs: (allow-none): a #PangoAttrList or %NULL to unset the attributes
5278  *
5279  * Sets the attributes list that are going to be applied to the
5280  * #ClutterText contents.
5281  *
5282  * The #ClutterText actor will take a reference on the #PangoAttrList
5283  * passed to this function.
5284  *
5285  * Since: 1.0
5286  */
5287 void
5288 clutter_text_set_attributes (ClutterText   *self,
5289                              PangoAttrList *attrs)
5290 {
5291   ClutterTextPrivate *priv;
5292
5293   g_return_if_fail (CLUTTER_IS_TEXT (self));
5294
5295   priv = self->priv;
5296
5297   if (attrs)
5298     pango_attr_list_ref (attrs);
5299
5300   if (priv->attrs)
5301     pango_attr_list_unref (priv->attrs);
5302
5303   priv->attrs = attrs;
5304
5305   /* Clear the effective attributes so they will be regenerated when a
5306      layout is created */
5307   if (priv->effective_attrs)
5308     {
5309       pango_attr_list_unref (priv->effective_attrs);
5310       priv->effective_attrs = NULL;
5311     }
5312
5313   clutter_text_dirty_cache (self);
5314
5315   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ATTRIBUTES]);
5316
5317   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5318 }
5319
5320 /**
5321  * clutter_text_get_attributes:
5322  * @self: a #ClutterText
5323  *
5324  * Gets the attribute list that was set on the #ClutterText actor
5325  * clutter_text_set_attributes(), if any.
5326  *
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.
5329  *
5330  * Since: 1.0
5331  */
5332 PangoAttrList *
5333 clutter_text_get_attributes (ClutterText *self)
5334 {
5335   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
5336
5337   return self->priv->attrs;
5338 }
5339
5340 /**
5341  * clutter_text_set_line_alignment:
5342  * @self: a #ClutterText
5343  * @alignment: A #PangoAlignment
5344  *
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.
5348  *
5349  * To align a #ClutterText actor you should add it to a container
5350  * that supports alignment, or use the anchor point.
5351  *
5352  * Since: 1.0
5353  */
5354 void
5355 clutter_text_set_line_alignment (ClutterText    *self,
5356                                  PangoAlignment  alignment)
5357 {
5358   ClutterTextPrivate *priv;
5359
5360   g_return_if_fail (CLUTTER_IS_TEXT (self));
5361
5362   priv = self->priv;
5363
5364   if (priv->alignment != alignment)
5365     {
5366       priv->alignment = alignment;
5367
5368       clutter_text_dirty_cache (self);
5369
5370       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5371
5372       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_ALIGNMENT]);
5373     }
5374 }
5375
5376 /**
5377  * clutter_text_get_line_alignment:
5378  * @self: a #ClutterText
5379  *
5380  * Retrieves the alignment of a #ClutterText, as set by
5381  * clutter_text_set_line_alignment().
5382  *
5383  * Return value: a #PangoAlignment
5384  *
5385  * Since: 1.0
5386  */
5387 PangoAlignment
5388 clutter_text_get_line_alignment (ClutterText *self)
5389 {
5390   g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ALIGN_LEFT);
5391
5392   return self->priv->alignment;
5393 }
5394
5395 /**
5396  * clutter_text_set_use_markup:
5397  * @self: a #ClutterText
5398  * @setting: %TRUE if the text should be parsed for markup.
5399  *
5400  * Sets whether the contents of the #ClutterText actor contains markup
5401  * in <link linkend="PangoMarkupFormat">Pango's text markup language</link>.
5402  *
5403  * Setting #ClutterText:use-markup on an editable #ClutterText will
5404  * not have any effect except hiding the markup.
5405  *
5406  * See also #ClutterText:use-markup.
5407  *
5408  * Since: 1.0
5409  */
5410 void
5411 clutter_text_set_use_markup (ClutterText *self,
5412                              gboolean     setting)
5413 {
5414   const gchar *text;
5415
5416   g_return_if_fail (CLUTTER_IS_TEXT (self));
5417
5418   text = clutter_text_buffer_get_text (get_buffer (self));
5419
5420   clutter_text_set_use_markup_internal (self, setting);
5421
5422   if (setting)
5423     clutter_text_set_markup_internal (self, text);
5424
5425   clutter_text_dirty_cache (self);
5426
5427   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5428 }
5429
5430 /**
5431  * clutter_text_get_use_markup:
5432  * @self: a #ClutterText
5433  *
5434  * Retrieves whether the contents of the #ClutterText actor should be
5435  * parsed for the Pango text markup.
5436  *
5437  * Return value: %TRUE if the contents will be parsed for markup
5438  *
5439  * Since: 1.0
5440  */
5441 gboolean
5442 clutter_text_get_use_markup (ClutterText *self)
5443 {
5444   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5445
5446   return self->priv->use_markup;
5447 }
5448
5449 /**
5450  * clutter_text_set_justify:
5451  * @self: a #ClutterText
5452  * @justify: whether the text should be justified
5453  *
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 &lt; 1.18.
5457  *
5458  * Since: 1.0
5459  */
5460 void
5461 clutter_text_set_justify (ClutterText *self,
5462                           gboolean     justify)
5463 {
5464   ClutterTextPrivate *priv;
5465
5466   g_return_if_fail (CLUTTER_IS_TEXT (self));
5467
5468   priv = self->priv;
5469
5470   if (priv->justify != justify)
5471     {
5472       priv->justify = justify;
5473
5474       clutter_text_dirty_cache (self);
5475
5476       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5477
5478       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_JUSTIFY]);
5479     }
5480 }
5481
5482 /**
5483  * clutter_text_get_justify:
5484  * @self: a #ClutterText
5485  *
5486  * Retrieves whether the #ClutterText actor should justify its contents
5487  * on both margins.
5488  *
5489  * Return value: %TRUE if the text should be justified
5490  *
5491  * Since: 0.6
5492  */
5493 gboolean
5494 clutter_text_get_justify (ClutterText *self)
5495 {
5496   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5497
5498   return self->priv->justify;
5499 }
5500
5501 /**
5502  * clutter_text_get_cursor_position:
5503  * @self: a #ClutterText
5504  *
5505  * Retrieves the cursor position.
5506  *
5507  * Return value: the cursor position, in characters
5508  *
5509  * Since: 1.0
5510  */
5511 gint
5512 clutter_text_get_cursor_position (ClutterText *self)
5513 {
5514   g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1);
5515
5516   return self->priv->position;
5517 }
5518
5519 /**
5520  * clutter_text_set_cursor_position:
5521  * @self: a #ClutterText
5522  * @position: the new cursor position, in characters
5523  *
5524  * Sets the cursor of a #ClutterText actor at @position.
5525  *
5526  * The position is expressed in characters, not in bytes.
5527  *
5528  * Since: 1.0
5529  */
5530 void
5531 clutter_text_set_cursor_position (ClutterText *self,
5532                                   gint         position)
5533 {
5534   ClutterTextPrivate *priv;
5535   gint len;
5536
5537   g_return_if_fail (CLUTTER_IS_TEXT (self));
5538
5539   priv = self->priv;
5540
5541   if (priv->position == position)
5542     return;
5543
5544   len = clutter_text_buffer_get_length (get_buffer (self));
5545
5546   if (position < 0 || position >= len)
5547     priv->position = -1;
5548   else
5549     priv->position = position;
5550
5551   /* Forget the target x position so that it will be recalculated next
5552      time the cursor is moved up or down */
5553   priv->x_pos = -1;
5554
5555   clutter_text_queue_redraw (CLUTTER_ACTOR (self));
5556
5557   g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]);
5558 }
5559
5560 /**
5561  * clutter_text_set_cursor_size:
5562  * @self: a #ClutterText
5563  * @size: the size of the cursor, in pixels, or -1 to use the
5564  *   default value
5565  *
5566  * Sets the size of the cursor of a #ClutterText. The cursor
5567  * will only be visible if the #ClutterText:cursor-visible property
5568  * is set to %TRUE.
5569  *
5570  * Since: 1.0
5571  */
5572 void
5573 clutter_text_set_cursor_size (ClutterText *self,
5574                               gint         size)
5575 {
5576   ClutterTextPrivate *priv;
5577
5578   g_return_if_fail (CLUTTER_IS_TEXT (self));
5579
5580   priv = self->priv;
5581
5582   if (priv->cursor_size != size)
5583     {
5584       if (size < 0)
5585         size = DEFAULT_CURSOR_SIZE;
5586
5587       priv->cursor_size = size;
5588
5589       clutter_text_queue_redraw (CLUTTER_ACTOR (self));
5590
5591       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_SIZE]);
5592     }
5593 }
5594
5595 /**
5596  * clutter_text_get_cursor_size:
5597  * @self: a #ClutterText
5598  *
5599  * Retrieves the size of the cursor of a #ClutterText actor.
5600  *
5601  * Return value: the size of the cursor, in pixels
5602  *
5603  * Since: 1.0
5604  */
5605 guint
5606 clutter_text_get_cursor_size (ClutterText *self)
5607 {
5608   g_return_val_if_fail (CLUTTER_IS_TEXT (self), DEFAULT_CURSOR_SIZE);
5609
5610   return self->priv->cursor_size;
5611 }
5612
5613 /**
5614  * clutter_text_set_password_char:
5615  * @self: a #ClutterText
5616  * @wc: a Unicode character, or 0 to unset the password character
5617  *
5618  * Sets the character to use in place of the actual text in a
5619  * password text actor.
5620  *
5621  * If @wc is 0 the text will be displayed as it is entered in the
5622  * #ClutterText actor.
5623  *
5624  * Since: 1.0
5625  */
5626 void
5627 clutter_text_set_password_char (ClutterText *self,
5628                                 gunichar     wc)
5629 {
5630   ClutterTextPrivate *priv;
5631
5632   g_return_if_fail (CLUTTER_IS_TEXT (self));
5633
5634   priv = self->priv;
5635
5636   if (priv->password_char != wc)
5637     {
5638       priv->password_char = wc;
5639
5640       clutter_text_dirty_cache (self);
5641       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5642
5643       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PASSWORD_CHAR]);
5644     }
5645 }
5646
5647 /**
5648  * clutter_text_get_password_char:
5649  * @self: a #ClutterText
5650  *
5651  * Retrieves the character to use in place of the actual text
5652  * as set by clutter_text_set_password_char().
5653  *
5654  * Return value: a Unicode character or 0 if the password
5655  *   character is not set
5656  *
5657  * Since: 1.0
5658  */
5659 gunichar
5660 clutter_text_get_password_char (ClutterText *self)
5661 {
5662   g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
5663
5664   return self->priv->password_char;
5665 }
5666
5667 /**
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
5672  *
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
5675  * truncated to fit.
5676  *
5677  * Since: 1.0
5678  */
5679 void
5680 clutter_text_set_max_length (ClutterText *self,
5681                              gint         max)
5682 {
5683   g_return_if_fail (CLUTTER_IS_TEXT (self));
5684   clutter_text_buffer_set_max_length (get_buffer (self), max);
5685 }
5686
5687 /**
5688  * clutter_text_get_max_length:
5689  * @self: a #ClutterText
5690  *
5691  * Gets the maximum length of text that can be set into a text actor.
5692  *
5693  * See clutter_text_set_max_length().
5694  *
5695  * Return value: the maximum number of characters.
5696  *
5697  * Since: 1.0
5698  */
5699 gint
5700 clutter_text_get_max_length (ClutterText *self)
5701 {
5702   g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
5703
5704   return clutter_text_buffer_get_max_length (get_buffer (self));
5705 }
5706
5707 /**
5708  * clutter_text_insert_unichar:
5709  * @self: a #ClutterText
5710  * @wc: a Unicode character
5711  *
5712  * Inserts @wc at the current cursor position of a
5713  * #ClutterText actor.
5714  *
5715  * Since: 1.0
5716  */
5717 void
5718 clutter_text_insert_unichar (ClutterText *self,
5719                              gunichar     wc)
5720 {
5721   ClutterTextPrivate *priv;
5722   GString *new;
5723
5724   priv = self->priv;
5725
5726   new = g_string_new ("");
5727   g_string_append_unichar (new, wc);
5728
5729   clutter_text_buffer_insert_text (get_buffer (self), priv->position, new->str, 1);
5730
5731   g_string_free (new, TRUE);
5732 }
5733
5734 /**
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
5739  *
5740  * Inserts @text into a #ClutterActor at the given position.
5741  *
5742  * If @position is a negative number, the text will be appended
5743  * at the end of the current contents of the #ClutterText.
5744  *
5745  * The position is expressed in characters, not in bytes.
5746  *
5747  * Since: 1.0
5748  */
5749 void
5750 clutter_text_insert_text (ClutterText *self,
5751                           const gchar *text,
5752                           gssize       position)
5753 {
5754   g_return_if_fail (CLUTTER_IS_TEXT (self));
5755   g_return_if_fail (text != NULL);
5756
5757   clutter_text_buffer_insert_text (get_buffer (self), position, text,
5758                                    g_utf8_strlen (text, -1));
5759 }
5760
5761 /**
5762  * clutter_text_delete_text:
5763  * @self: a #ClutterText
5764  * @start_pos: starting position
5765  * @end_pos: ending position
5766  *
5767  * Deletes the text inside a #ClutterText actor between @start_pos
5768  * and @end_pos.
5769  *
5770  * The starting and ending positions are expressed in characters,
5771  * not in bytes.
5772  *
5773  * Since: 1.0
5774  */
5775 void
5776 clutter_text_delete_text (ClutterText *self,
5777                           gssize       start_pos,
5778                           gssize       end_pos)
5779 {
5780   g_return_if_fail (CLUTTER_IS_TEXT (self));
5781
5782   clutter_text_buffer_delete_text (get_buffer (self), start_pos, end_pos - start_pos);
5783 }
5784
5785 /**
5786  * clutter_text_delete_chars:
5787  * @self: a #ClutterText
5788  * @n_chars: the number of characters to delete
5789  *
5790  * Deletes @n_chars inside a #ClutterText actor, starting from the
5791  * current cursor position.
5792  *
5793  * Somewhat awkwardly, the cursor position is decremented by the same
5794  * number of characters you've deleted.
5795  *
5796  * Since: 1.0
5797  */
5798 void
5799 clutter_text_delete_chars (ClutterText *self,
5800                            guint        n_chars)
5801 {
5802   ClutterTextPrivate *priv;
5803
5804   g_return_if_fail (CLUTTER_IS_TEXT (self));
5805
5806   priv = self->priv;
5807
5808   clutter_text_buffer_delete_text (get_buffer (self), priv->position, n_chars);
5809
5810   if (priv->position > 0)
5811     clutter_text_set_cursor_position (self, priv->position - n_chars);
5812 }
5813
5814 /**
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
5819  *
5820  * Retrieves the contents of the #ClutterText actor between
5821  * @start_pos and @end_pos, but not including @end_pos.
5822  *
5823  * The positions are specified in characters, not in bytes.
5824  *
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
5828  *
5829  * Since: 1.0
5830  */
5831 gchar *
5832 clutter_text_get_chars (ClutterText *self,
5833                         gssize       start_pos,
5834                         gssize       end_pos)
5835 {
5836   gint start_index, end_index;
5837   guint n_chars;
5838   const gchar *text;
5839
5840   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
5841
5842   n_chars = clutter_text_buffer_get_length (get_buffer (self));
5843   text = clutter_text_buffer_get_text (get_buffer (self));
5844
5845   if (end_pos < 0)
5846     end_pos = n_chars;
5847
5848   start_pos = MIN (n_chars, start_pos);
5849   end_pos = MIN (n_chars, end_pos);
5850
5851   start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
5852   end_index   = g_utf8_offset_to_pointer (text, end_pos) - text;
5853
5854   return g_strndup (text + start_index, end_index - start_index);
5855 }
5856
5857 /**
5858  * clutter_text_set_single_line_mode:
5859  * @self: a #ClutterText
5860  * @single_line: whether to enable single line mode
5861  *
5862  * Sets whether a #ClutterText actor should be in single line mode
5863  * or not. Only editable #ClutterText<!-- -->s can be in single line
5864  * mode.
5865  *
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.
5870  *
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
5874  * signal.
5875  *
5876  * Since: 1.0
5877  */
5878 void
5879 clutter_text_set_single_line_mode (ClutterText *self,
5880                                    gboolean     single_line)
5881 {
5882   ClutterTextPrivate *priv;
5883
5884   g_return_if_fail (CLUTTER_IS_TEXT (self));
5885
5886   priv = self->priv;
5887
5888   if (priv->single_line_mode != single_line)
5889     {
5890       g_object_freeze_notify (G_OBJECT (self));
5891
5892       priv->single_line_mode = single_line;
5893
5894       if (priv->single_line_mode)
5895         {
5896           priv->activatable = TRUE;
5897
5898           g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]);
5899         }
5900
5901       clutter_text_dirty_cache (self);
5902       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5903
5904       g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SINGLE_LINE_MODE]);
5905
5906       g_object_thaw_notify (G_OBJECT (self));
5907     }
5908 }
5909
5910 /**
5911  * clutter_text_get_single_line_mode:
5912  * @self: a #ClutterText
5913  *
5914  * Retrieves whether the #ClutterText actor is in single line mode.
5915  *
5916  * Return value: %TRUE if the #ClutterText actor is in single line mode
5917  *
5918  * Since: 1.0
5919  */
5920 gboolean
5921 clutter_text_get_single_line_mode (ClutterText *self)
5922 {
5923   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
5924
5925   return self->priv->single_line_mode;
5926 }
5927
5928 /**
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
5934  *
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
5938  * buffer.
5939  *
5940  * The preedit string and attributes are ignored if the #ClutterText
5941  * actor is not editable.
5942  *
5943  * This function should not be used by applications
5944  *
5945  * Since: 1.2
5946  */
5947 void
5948 clutter_text_set_preedit_string (ClutterText   *self,
5949                                  const gchar   *preedit_str,
5950                                  PangoAttrList *preedit_attrs,
5951                                  guint          cursor_pos)
5952 {
5953   ClutterTextPrivate *priv;
5954
5955   g_return_if_fail (CLUTTER_IS_TEXT (self));
5956
5957   priv = self->priv;
5958
5959   g_free (priv->preedit_str);
5960   priv->preedit_str = NULL;
5961
5962   if (priv->preedit_attrs != NULL)
5963     {
5964       pango_attr_list_unref (priv->preedit_attrs);
5965       priv->preedit_attrs = NULL;
5966     }
5967
5968   priv->preedit_n_chars = 0;
5969   priv->preedit_cursor_pos = 0;
5970
5971   if (preedit_str == NULL || *preedit_str == '\0')
5972     priv->preedit_set = FALSE;
5973   else
5974     {
5975       priv->preedit_str = g_strdup (preedit_str);
5976
5977       if (priv->preedit_str != NULL)
5978         priv->preedit_n_chars = g_utf8_strlen (priv->preedit_str, -1);
5979       else
5980         priv->preedit_n_chars = 0;
5981
5982       if (preedit_attrs != NULL)
5983         priv->preedit_attrs = pango_attr_list_ref (preedit_attrs);
5984
5985       priv->preedit_cursor_pos =
5986         CLAMP (cursor_pos, 0, priv->preedit_n_chars);
5987
5988       priv->preedit_set = TRUE;
5989     }
5990
5991   clutter_text_dirty_cache (self);
5992   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
5993 }
5994
5995
5996 /**
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
6001  *
6002  * Obtains the coordinates where the #ClutterText will draw the #PangoLayout
6003  * representing the text.
6004  *
6005  * Since: 1.8
6006  */
6007 void
6008 clutter_text_get_layout_offsets (ClutterText *self,
6009                                  gint        *x,
6010                                  gint        *y)
6011 {
6012   ClutterTextPrivate *priv;
6013
6014   g_return_if_fail (CLUTTER_IS_TEXT (self));
6015
6016   priv = self->priv;
6017
6018   if (x != NULL)
6019     *x = priv->text_x;
6020
6021   if (y != NULL)
6022     *y = priv->text_y;
6023 }