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