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