Merge the ClutterText actor
authorEmmanuele Bassi <ebassi@linux.intel.com>
Wed, 7 Jan 2009 12:06:33 +0000 (12:06 +0000)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Wed, 7 Jan 2009 12:06:33 +0000 (12:06 +0000)
Merge branch 'text-actor'

* text-actor: (108 commits)
  Re-align ClutterText header file
  [text] Fix cursor sizing
  Comments and whitespace fixes to ClutterText
  [docs] Add newly added :single-line-mode accessors
  Update the ignore file
  [tests] Add text field interactive test
  [text] Add single-line-mode to ClutterText
  [text] Fix the deletion actions
  [text] Use cached length when possible
  [tests] Add unit for the ClutterText:password-char property
  [docs] Update the Text section
  [text] Coalesce text visibility and password character
  Allow localizations to change the text direction
  Clean up the update_pango_context() function
  Pass the PangoContext, not the MainContext
  Revert the logic of the PangoContext check
  Remove the binding pool entry from the list
  Remove BindingPool::list_actions()
  Add ClutterActor::create_pango_context()
  Rename the PangoContext creation functions
  ...

14 files changed:
1  2 
.gitignore
clutter/Makefile.am
clutter/clutter-actor.c
clutter/clutter-main.c
clutter/clutter-text.c
clutter/clutter.h
doc/reference/clutter/clutter-docs.xml
doc/reference/clutter/clutter-sections.txt
doc/reference/clutter/clutter.types
tests/conform/Makefile.am
tests/interactive/Makefile.am
tests/interactive/test-layout.c
tests/interactive/test-text.c
tests/interactive/test-unproject.c

diff --cc .gitignore
@@@ -158,9 -172,6 +159,10 @@@ stamp-h
  /clutter/x11/stamp-clutter-x11-enum-types.h
  /po/Makefile.in.in
  /po/POTFILES
+ /po/*.pot
 +*.swn
 +*.swo
  *.swp
  *~
 +*.orig
 +*.rej
Simple merge
Simple merge
@@@ -1142,16 -1234,19 +1234,19 @@@ clutter_init_real (GError **error
  
  static GOptionEntry clutter_args[] = {
    { "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps,
 -    "Show frames per second", NULL },
 +    N_("Show frames per second"), NULL },
    { "clutter-default-fps", 0, 0, G_OPTION_ARG_INT, &clutter_default_fps,
 -    "Default frame rate", "FPS" },
 +    N_("Default frame rate"), "FPS" },
    { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings,
 -    "Make all warnings fatal", NULL },
 +    N_("Make all warnings fatal"), NULL },
+   { "clutter-text-direction", 0, 0, G_OPTION_ARG_CALLBACK,
+     clutter_arg_direction_cb,
 -    "Direction for the text", "DIRECTION" },
++    N_("Direction for the text"), "DIRECTION" },
  #ifdef CLUTTER_ENABLE_DEBUG
    { "clutter-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_debug_cb,
 -    "Clutter debugging flags to set", "FLAGS" },
 +    N_("Clutter debugging flags to set"), "FLAGS" },
    { "clutter-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_no_debug_cb,
 -    "Clutter debugging flags to unset", "FLAGS" },
 +    N_("Clutter debugging flags to unset"), "FLAGS" },
  #endif /* CLUTTER_ENABLE_DEBUG */
    { NULL, },
  };
index 0000000,ee6fcc6..fa3fbb4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,3753 +1,3753 @@@
 -          
+ /*
+  * Clutter.
+  *
+  * An OpenGL based 'interactive canvas' library.
+  *
+  * Copyright (C) 2008  Intel Corporation.
+  *
+  * Authored By: Øyvind Kolås <pippin@o-hand.com>
+  *              Emmanuele Bassi <ebassi@linux.intel.com>
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+  * License as published by the Free Software Foundation; either
+  * version 2 of the License, or (at your option) any later version.
+  *
+  * This library is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * Lesser General Public License for more details.
+  *
+  * You should have received a copy of the GNU Lesser General Public
+  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+  */
+ /**
+  * SECTION:clutter-text
+  * @short_description: An actor for displaying and editing text
+  *
+  * #ClutterText is an actor that displays custom text using Pango
+  * as the text rendering engine.
+  *
+  * #ClutterText also allows inline editing of the text if the
+  * actor is set editable using clutter_text_set_editable().
+  *
+  * Selection using keyboard or pointers can be enabled using
+  * clutter_text_set_selectable().
+  *
+  * #ClutterText is available since Clutter 1.0
+  */
+ /* TODO: undo/redo hooks? */
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+ #include <string.h>
+ #include "clutter-text.h"
+ #include "clutter-binding-pool.h"
+ #include "clutter-debug.h"
+ #include "clutter-enum-types.h"
+ #include "clutter-keysyms.h"
+ #include "clutter-main.h"
+ #include "clutter-private.h"    /* includes pango/cogl-pango.h */
+ #include "clutter-units.h"
+ /* cursor width in pixels */
+ #define DEFAULT_CURSOR_SIZE     2
+ /* We need at least three cached layouts to run the allocation without
+  * regenerating a new layout. First the layout will be generated at
+  * full width to get the preferred width, then it will be generated at
+  * the preferred width to get the preferred height and then it might
+  * be regenerated at a different width to get the height for the
+  * actual allocated width
+  */
+ #define N_CACHED_LAYOUTS        3
+ #define CLUTTER_TEXT_GET_PRIVATE(obj)   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXT, ClutterTextPrivate))
+ typedef struct _LayoutCache     LayoutCache;
+ static const ClutterColor default_cursor_color = {   0,   0,   0, 255 };
+ static const ClutterColor default_text_color   = {   0,   0,   0, 255 };
+ G_DEFINE_TYPE (ClutterText, clutter_text, CLUTTER_TYPE_ACTOR);
+ struct _LayoutCache
+ {
+   /* Cached layout. Pango internally caches the computed extents
+    * when they are requested so there is no need to cache that as
+    * well
+    */
+   PangoLayout *layout;
+   /* The width that used to generate this layout */
+   ClutterUnit  width;
+   /* A number representing the age of this cache (so that when a
+    * new layout is needed the last used cache is replaced)
+    */
+   guint        age;
+ };
+ struct _ClutterTextPrivate
+ {
+   PangoFontDescription *font_desc;
+   gchar *text;
+   gchar *font_name;
+   ClutterColor text_color;
+   LayoutCache cached_layouts[N_CACHED_LAYOUTS];
+   guint cache_age;
+   PangoAttrList *attrs;
+   PangoAttrList *effective_attrs;
+   guint alignment        : 2;
+   guint wrap             : 1;
+   guint use_underline    : 1;
+   guint use_markup       : 1;
+   guint ellipsize        : 3;
+   guint single_line_mode : 1;
+   guint wrap_mode        : 3;
+   guint justify          : 1;
+   guint editable         : 1;
+   guint cursor_visible   : 1;
+   guint activatable      : 1;
+   guint selectable       : 1;
+   guint in_select_drag   : 1;
+   guint cursor_color_set : 1;
+   /* current cursor position */
+   gint position;
+   /* current 'other end of selection' position */
+   gint selection_bound;
+   /* the x position in the PangoLayout, used to
+    * avoid drifting when repeatedly moving up|down
+    */
+   gint x_pos;
+   /* the x position of the PangoLayout when in
+    * single line mode, to scroll the contents of the
+    * text actor
+    */
+   gint text_x;
+   /* the length of the text, in bytes */
+   gint n_bytes;
+   /* the length of the text, in characters */
+   gint n_chars;
+   /* Where to draw the cursor */
+   ClutterGeometry cursor_pos;
+   ClutterColor cursor_color;
+   guint cursor_size;
+   gint max_length;
+   gunichar password_char;
+ };
+ enum
+ {
+   PROP_0,
+   PROP_FONT_NAME,
+   PROP_TEXT,
+   PROP_COLOR,
+   PROP_USE_MARKUP,
+   PROP_ATTRIBUTES,
+   PROP_ALIGNMENT,
+   PROP_LINE_WRAP,
+   PROP_LINE_WRAP_MODE,
+   PROP_JUSTIFY,
+   PROP_ELLIPSIZE,
+   PROP_POSITION,
+   PROP_SELECTION_BOUND,
+   PROP_CURSOR_VISIBLE,
+   PROP_CURSOR_COLOR,
+   PROP_CURSOR_COLOR_SET,
+   PROP_CURSOR_SIZE,
+   PROP_EDITABLE,
+   PROP_SELECTABLE,
+   PROP_ACTIVATABLE,
+   PROP_PASSWORD_CHAR,
+   PROP_MAX_LENGTH,
+   PROP_SINGLE_LINE_MODE
+ };
+ enum
+ {
+   TEXT_CHANGED,
+   CURSOR_EVENT,
+   ACTIVATE,
+   LAST_SIGNAL
+ };
+ static guint text_signals[LAST_SIGNAL] = { 0, };
+ #define offset_real(t,p)        ((p) == -1 ? g_utf8_strlen ((t), -1) : (p))
+ static gint
+ offset_to_bytes (const gchar *text,
+                  gint         pos)
+ {
+   gchar *c = NULL;
+   gint i, j, len;
+   if (pos < 0)
+     return strlen (text);
+   c = g_utf8_next_char (text);
+   j = 1;
+   len = strlen (text);
+   for (i = 0; i < len; i++)
+     {
+       if (&text[i] == c)
+         {
+           if (j == pos)
+             break;
+           else
+             {
+               c = g_utf8_next_char (c);
+               j++;
+             }
+         }
+     }
+   return i;
+ }
+ #define bytes_to_offset(t,p)    (g_utf8_pointer_to_offset ((t), (t) + (p)))
+ static inline void
+ clutter_text_clear_selection (ClutterText *self)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   priv->selection_bound = priv->position;
+ }
+ static PangoLayout *
+ clutter_text_create_layout_no_cache (ClutterText *text,
+                                      ClutterUnit  allocation_width)
+ {
+   ClutterTextPrivate *priv = text->priv;
+   PangoContext *context;
+   PangoLayout *layout;
+   context = clutter_actor_get_pango_context (CLUTTER_ACTOR (text));
+   layout = pango_layout_new (context);
+   pango_layout_set_font_description (layout, priv->font_desc);
+   if (priv->effective_attrs)
+     pango_layout_set_attributes (layout, priv->effective_attrs);
+   pango_layout_set_alignment (layout, priv->alignment);
+   pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode);
+   pango_layout_set_justify (layout, priv->justify);
+   if (priv->text)
+     {
+       if (!priv->use_markup)
+         {
+           if (G_LIKELY (priv->password_char == 0))
+             pango_layout_set_text (layout, priv->text, priv->n_bytes);
+           else
+             {
+               GString *str = g_string_sized_new (priv->n_bytes);
+               gunichar invisible_char;
+               gchar buf[7];
+               gint char_len, i;
+               invisible_char = priv->password_char;
+               /* we need to convert the string built of invisible
+                * characters into UTF-8 for it to be fed to the Pango
+                * layout
+                */
+               memset (buf, 0, sizeof (buf));
+               char_len = g_unichar_to_utf8 (invisible_char, buf);
+               for (i = 0; i < priv->n_chars; i++)
+                 g_string_append_len (str, buf, char_len);
+               pango_layout_set_text (layout, str->str, str->len);
+               g_string_free (str, TRUE);
+             }
+         }
+       else
+         pango_layout_set_markup (layout, priv->text, -1);
+     }
+   if (allocation_width > 0 &&
+       (priv->ellipsize != PANGO_ELLIPSIZE_NONE || priv->wrap))
+     {
+       int layout_width, layout_height;
+       pango_layout_get_size (layout, &layout_width, &layout_height);
+       /* No need to set ellipsize or wrap if we already have enough
+        * space, since we don't want to make the layout wider than it
+        * would be otherwise.
+        */
+       if (CLUTTER_UNITS_FROM_PANGO_UNIT (layout_width) > allocation_width)
+         {
+           if (priv->ellipsize != PANGO_ELLIPSIZE_NONE)
+             {
+               gint width;
+               width = allocation_width > 0
+                 ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width)
+                 : -1;
+               pango_layout_set_ellipsize (layout, priv->ellipsize);
+               pango_layout_set_width (layout, width);
+             }
+           else if (priv->wrap)
+             {
+               gint width;
+               width = allocation_width > 0
+                 ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_width)
+                 : -1;
+               pango_layout_set_wrap (layout, priv->wrap_mode);
+               pango_layout_set_width (layout, width);
+             }
+         }
+     }
+   return layout;
+ }
+ static void
+ clutter_text_dirty_cache (ClutterText *text)
+ {
+   ClutterTextPrivate *priv = text->priv;
+   int i;
+   /* Delete the cached layouts so they will be recreated the next time
+      they are needed */
+   for (i = 0; i < N_CACHED_LAYOUTS; i++)
+     if (priv->cached_layouts[i].layout)
+       {
+       g_object_unref (priv->cached_layouts[i].layout);
+       priv->cached_layouts[i].layout = NULL;
+       }
+ }
+ /*
+  * clutter_text_create_layout:
+  * @text: a #ClutterText
+  * @allocation_width: the allocation width
+  *
+  * Like clutter_text_create_layout_no_cache(), but will also ensure
+  * the glyphs cache. If a previously cached layout generated using the
+  * same width is available then that will be used instead of
+  * generating a new one.
+  */
+ static PangoLayout *
+ clutter_text_create_layout (ClutterText *text,
+                             ClutterUnit   allocation_width)
+ {
+   ClutterTextPrivate *priv = text->priv;
+   LayoutCache *oldest_cache = priv->cached_layouts;
+   gboolean found_free_cache = FALSE;
+   int i;
+   /* Search for a cached layout with the same width and keep track of
+      the oldest one */
+   for (i = 0; i < N_CACHED_LAYOUTS; i++)
+     {
+       if (priv->cached_layouts[i].layout == NULL)
+       {
+         /* Always prefer free cache spaces */
+         found_free_cache = TRUE;
+         oldest_cache = priv->cached_layouts + i;
+       }
+       /* If this cached layout is using the same width then we can
+        just return that directly */
+       else if (priv->cached_layouts[i].width == allocation_width)
+       {
+         CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache hit for width %i",
+                       text,
+                         CLUTTER_UNITS_TO_DEVICE (allocation_width));
+         return priv->cached_layouts[i].layout;
+       }
+       else if (!found_free_cache &&
+                (priv->cached_layouts[i].age < oldest_cache->age))
+         {
+         oldest_cache = priv->cached_layouts + i;
+         }
+     }
+   CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache miss for width %i",
+               text,
+                 CLUTTER_UNITS_TO_DEVICE (allocation_width));
+   /* If we make it here then we didn't have a cached version so we
+      need to recreate the layout */
+   if (oldest_cache->layout)
+     g_object_unref (oldest_cache->layout);
+   oldest_cache->layout =
+     clutter_text_create_layout_no_cache (text, allocation_width);
+   cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout);
+   /* Mark the 'time' this cache was created and advance the time */
+   oldest_cache->age = priv->cache_age++;
+   oldest_cache->width = allocation_width;
+   return oldest_cache->layout;
+ }
+ static gint
+ clutter_text_coords_to_position (ClutterText *text,
+                                  gint         x,
+                                  gint         y)
+ {
+   gint index_;
+   gint px, py;
+   gint trailing;
+   px = x * PANGO_SCALE;
+   py = y * PANGO_SCALE;
+   pango_layout_xy_to_index (clutter_text_get_layout (text),
+                             px, py,
+                             &index_, &trailing);
+   return index_ + trailing;
+ }
+ /*
+  * clutter_text_position_to_coords:
+  * @self: a #ClutterText
+  * @position: position in characters
+  * @x: return location for the X coordinate, or %NULL
+  * @y: return location for the Y coordinate, or %NULL
+  * @line_height: return location for the line height, or %NULL
+  *
+  * Retrieves the coordinates of the given @position.
+  *
+  * Return value: %TRUE if the conversion was successful
+  */
+ static gboolean
+ clutter_text_position_to_coords (ClutterText *self,
+                                  gint         position,
+                                  ClutterUnit *x,
+                                  ClutterUnit *y,
+                                  ClutterUnit *line_height)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   PangoRectangle rect;
+   gint password_char_bytes = 1;
+   gint index_;
+   if (priv->password_char != 0)
+     password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL);
+   if (position == -1)
+     {
+       if (priv->password_char == 0)
+         index_ = priv->n_bytes;
+       else
+         index_ = priv->n_chars * password_char_bytes;
+     }
+   else if (position == 0)
+     {
+       index_ = 0;
+     }
+   else
+     {
+       if (priv->password_char == 0)
+         index_ = offset_to_bytes (priv->text, position);
+       else
+         index_ = priv->position * password_char_bytes;
+     }
+   pango_layout_get_cursor_pos (clutter_text_get_layout (self), index_,
+                                &rect, NULL);
+   if (x)
+     *x = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.x);
+   if (y)
+     *y = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.y);
+   if (line_height)
+     *line_height = CLUTTER_UNITS_FROM_PANGO_UNIT (rect.height);
+   /* FIXME: should return false if coords were outside text */
+   return TRUE;
+ }
+ static inline void
+ clutter_text_ensure_cursor_position (ClutterText *self)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   ClutterUnit x, y, cursor_height;
+   x = y = cursor_height = 0;
+   clutter_text_position_to_coords (self, priv->position,
+                                    &x, &y,
+                                    &cursor_height);
+   priv->cursor_pos.x      = CLUTTER_UNITS_TO_DEVICE (x);
+   priv->cursor_pos.y      = CLUTTER_UNITS_TO_DEVICE (y);
+   priv->cursor_pos.width  = priv->cursor_size;
+   priv->cursor_pos.height = CLUTTER_UNITS_TO_DEVICE (cursor_height) - 2;
+   g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &priv->cursor_pos);
+ }
+ static gboolean
+ clutter_text_truncate_selection (ClutterText *self)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   gint start_index;
+   gint end_index;
+   if (!priv->text)
+     return TRUE;
+   start_index = offset_real (priv->text, priv->position);
+   end_index = offset_real (priv->text, priv->selection_bound);
+   if (end_index == start_index)
+     return FALSE;
+   if (end_index < start_index)
+     {
+       gint temp = start_index;
+       start_index = end_index;
+       end_index = temp;
+     }
+   clutter_text_delete_text (self, start_index, end_index);
+   priv->position = start_index;
+   priv->selection_bound = start_index;
+   return TRUE;
+ }
+ static void
+ clutter_text_set_property (GObject      *gobject,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+ {
+   ClutterText *self = CLUTTER_TEXT (gobject);
+   switch (prop_id)
+     {
+     case PROP_TEXT:
+       clutter_text_set_text (self, g_value_get_string (value));
+       break;
+     case PROP_COLOR:
+       clutter_text_set_color (self, clutter_value_get_color (value));
+       break;
+     case PROP_FONT_NAME:
+       clutter_text_set_font_name (self, g_value_get_string (value));
+       break;
+     case PROP_USE_MARKUP:
+       clutter_text_set_use_markup (self, g_value_get_boolean (value));
+       break;
+     case PROP_ATTRIBUTES:
+       clutter_text_set_attributes (self, g_value_get_boxed (value));
+       break;
+     case PROP_ALIGNMENT:
+       clutter_text_set_alignment (self, g_value_get_enum (value));
+       break;
+     case PROP_LINE_WRAP:
+       clutter_text_set_line_wrap (self, g_value_get_boolean (value));
+       break;
+     case PROP_LINE_WRAP_MODE:
+       clutter_text_set_line_wrap_mode (self, g_value_get_enum (value));
+       break;
+     case PROP_JUSTIFY:
+       clutter_text_set_justify (self, g_value_get_boolean (value));
+       break;
+     case PROP_ELLIPSIZE:
+       clutter_text_set_ellipsize (self, g_value_get_enum (value));
+       break;
+     case PROP_POSITION:
+       clutter_text_set_cursor_position (self, g_value_get_int (value));
+       break;
+     case PROP_SELECTION_BOUND:
+       clutter_text_set_selection_bound (self, g_value_get_int (value));
+       break;
+     case PROP_CURSOR_VISIBLE:
+       clutter_text_set_cursor_visible (self, g_value_get_boolean (value));
+       break;
+     case PROP_CURSOR_COLOR:
+       clutter_text_set_cursor_color (self, g_value_get_boxed (value));
+       break;
+     case PROP_CURSOR_SIZE:
+       clutter_text_set_cursor_size (self, g_value_get_int (value));
+       break;
+     case PROP_EDITABLE:
+       clutter_text_set_editable (self, g_value_get_boolean (value));
+       break;
+     case PROP_ACTIVATABLE:
+       clutter_text_set_activatable (self, g_value_get_boolean (value));
+       break;
+     case PROP_SELECTABLE:
+       clutter_text_set_selectable (self, g_value_get_boolean (value));
+       break;
+     case PROP_PASSWORD_CHAR:
+       clutter_text_set_password_char (self, g_value_get_uint (value));
+       break;
+     case PROP_MAX_LENGTH:
+       clutter_text_set_max_length (self, g_value_get_int (value));
+       break;
+     case PROP_SINGLE_LINE_MODE:
+       clutter_text_set_single_line_mode (self, g_value_get_boolean (value));
+       break;
+     default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+     }
+ }
+ static void
+ clutter_text_get_property (GObject    *gobject,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+ {
+   ClutterTextPrivate *priv = CLUTTER_TEXT (gobject)->priv;
+   switch (prop_id)
+     {
+     case PROP_TEXT:
+       g_value_set_string (value, priv->text);
+       break;
+     case PROP_FONT_NAME:
+       g_value_set_string (value, priv->font_name);
+       break;
+     case PROP_COLOR:
+       clutter_value_set_color (value, &priv->text_color);
+       break;
+     case PROP_CURSOR_VISIBLE:
+       g_value_set_boolean (value, priv->cursor_visible);
+       break;
+     case PROP_CURSOR_COLOR:
+       clutter_value_set_color (value, &priv->cursor_color);
+       break;
+     case PROP_CURSOR_COLOR_SET:
+       g_value_set_boolean (value, priv->cursor_color_set);
+       break;
+     case PROP_CURSOR_SIZE:
+       g_value_set_int (value, priv->cursor_size);
+       break;
+     case PROP_POSITION:
+       g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->position));
+       break;
+     case PROP_SELECTION_BOUND:
+       g_value_set_int (value, CLUTTER_FIXED_TO_FLOAT (priv->selection_bound));
+       break;
+     case PROP_EDITABLE:
+       g_value_set_boolean (value, priv->editable);
+       break;
+     case PROP_SELECTABLE:
+       g_value_set_boolean (value, priv->selectable);
+       break;
+     case PROP_ACTIVATABLE:
+       g_value_set_boolean (value, priv->activatable);
+       break;
+     case PROP_PASSWORD_CHAR:
+       g_value_set_uint (value, priv->password_char);
+       break;
+     case PROP_MAX_LENGTH:
+       g_value_set_int (value, priv->max_length);
+       break;
+     case PROP_SINGLE_LINE_MODE:
+       g_value_set_boolean (value, priv->single_line_mode);
+       break;
+     default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+     }
+ }
+ static void
+ clutter_text_dispose (GObject *gobject)
+ {
+   ClutterText *self = CLUTTER_TEXT (gobject);
+   /* get rid of the entire cache */
+   clutter_text_dirty_cache (self);
+   G_OBJECT_CLASS (clutter_text_parent_class)->dispose (gobject);
+ }
+ static void
+ clutter_text_finalize (GObject *gobject)
+ {
+   ClutterText *self = CLUTTER_TEXT (gobject);
+   ClutterTextPrivate *priv = self->priv;
+   if (priv->font_desc)
+     pango_font_description_free (priv->font_desc);
+   g_free (priv->text);
+   g_free (priv->font_name);
+   G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject);
+ }
+ static void
+ cursor_paint (ClutterText *self)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   ClutterActor *actor = CLUTTER_ACTOR (self);
+   guint8 real_opacity;
+   if (priv->editable && priv->cursor_visible)
+     {
+       if (priv->cursor_color_set)
+         {
+           real_opacity = clutter_actor_get_paint_opacity (actor)
+                        * priv->cursor_color.alpha
+                        / 255;
+           cogl_set_source_color4ub (priv->cursor_color.red,
+                                     priv->cursor_color.green,
+                                     priv->cursor_color.blue,
+                                     real_opacity);
+         }
+       else
+         {
+           real_opacity = clutter_actor_get_paint_opacity (actor)
+                        * priv->text_color.alpha
+                        / 255;
+           cogl_set_source_color4ub (priv->text_color.red,
+                                     priv->text_color.green,
+                                     priv->text_color.blue,
+                                     real_opacity);
+         }
+       if (priv->position == 0)
+         priv->cursor_pos.x -= priv->cursor_size;
+       if (priv->position == priv->selection_bound)
+         {
+           cogl_rectangle (priv->cursor_pos.x,
+                           priv->cursor_pos.y,
+                           priv->cursor_pos.width,
+                           priv->cursor_pos.height);
+         }
+       else
+         {
+           PangoLayout *layout = clutter_text_get_layout (self);
+           const gchar *utf8 = priv->text;
+           gint lines;
+           gint start_index;
+           gint end_index;
+           gint line_no;
+           if (priv->position == 0)
+             start_index = 0;
+           else
+             start_index = offset_to_bytes (utf8, priv->position);
+           if (priv->selection_bound == 0)
+             end_index = 0;
+           else
+             end_index = offset_to_bytes (utf8, priv->selection_bound);
+           if (start_index > end_index)
+             {
+               gint temp = start_index;
+               start_index = end_index;
+               end_index = temp;
+             }
++
+           lines = pango_layout_get_line_count (layout);
+           for (line_no = 0; line_no < lines; line_no++)
+             {
+               PangoLayoutLine *line;
+               gint n_ranges;
+               gint *ranges;
+               gint i;
+               gint index;
+               gint maxindex;
+               ClutterUnit y, height;
+               line = pango_layout_get_line_readonly (layout, line_no);
+               pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL);
+               if (maxindex < start_index)
+                 continue;
+               pango_layout_line_get_x_ranges (line, start_index, end_index,
+                                               &ranges,
+                                               &n_ranges);
+               pango_layout_line_x_to_index (line, 0, &index, NULL);
+               clutter_text_position_to_coords (self,
+                                                bytes_to_offset (utf8, index),
+                                                NULL, &y, &height);
+               for (i = 0; i < n_ranges; i++)
+                 {
+                   gint range_x;
+                   gint range_width;
+                   range_x     = ranges[i * 2]
+                               / PANGO_SCALE;
+                   range_width = (ranges[i * 2 + 1] - ranges[i * 2])
+                               / PANGO_SCALE;
+                   cogl_rectangle (range_x,
+                                   CLUTTER_UNITS_TO_DEVICE (y),
+                                   range_width,
+                                   CLUTTER_UNITS_TO_DEVICE (height));
+                 }
+               g_free (ranges);
+             }
+         }
+     }
+ }
+ static gboolean
+ clutter_text_button_press (ClutterActor       *actor,
+                            ClutterButtonEvent *event)
+ {
+   ClutterText *self = CLUTTER_TEXT (actor);
+   ClutterTextPrivate *priv = self->priv;
+   ClutterUnit x, y;
+   gint index_;
+   x = CLUTTER_UNITS_FROM_INT (event->x);
+   y = CLUTTER_UNITS_FROM_INT (event->y);
+   clutter_actor_transform_stage_point (actor, x, y, &x, &y);
+   index_ = clutter_text_coords_to_position (self,
+                                             CLUTTER_UNITS_TO_INT (x),
+                                             CLUTTER_UNITS_TO_INT (y));
+   clutter_text_set_cursor_position (self, bytes_to_offset (priv->text, index_));
+   clutter_text_set_selection_bound (self, bytes_to_offset (priv->text, index_));
+   /* grab the pointer */
+   priv->in_select_drag = TRUE;
+   clutter_grab_pointer (actor);
+   /* we'll steal keyfocus if we do not have it */
+   clutter_actor_grab_key_focus (actor);
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_motion (ClutterActor       *actor,
+                      ClutterMotionEvent *mev)
+ {
+   ClutterText *ttext = CLUTTER_TEXT (actor);
+   ClutterTextPrivate *priv = ttext->priv;
+   ClutterUnit           x, y;
+   gint                  index_;
+   const gchar          *text;
+   if (!priv->in_select_drag)
+     return FALSE;
+   text = clutter_text_get_text (ttext);
+   x = CLUTTER_UNITS_FROM_INT (mev->x);
+   y = CLUTTER_UNITS_FROM_INT (mev->y);
+   clutter_actor_transform_stage_point (actor, x, y, &x, &y);
+   index_ = clutter_text_coords_to_position (ttext,
+                                             CLUTTER_UNITS_TO_INT (x),
+                                             CLUTTER_UNITS_TO_INT (y));
+   if (priv->selectable)
+     clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_));
+   else
+     {
+       clutter_text_set_cursor_position (ttext, bytes_to_offset (text, index_));
+       clutter_text_set_selection_bound (ttext, bytes_to_offset (text, index_));
+     }
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_button_release (ClutterActor       *actor,
+                              ClutterButtonEvent *bev)
+ {
+   ClutterText *ttext = CLUTTER_TEXT (actor);
+   ClutterTextPrivate *priv = ttext->priv;
+   if (priv->in_select_drag)
+     {
+       clutter_ungrab_pointer ();
+       priv->in_select_drag = FALSE;
+       return TRUE;
+     }
+   return FALSE;
+ }
+ static gboolean
+ clutter_text_key_press (ClutterActor    *actor,
+                         ClutterKeyEvent *event)
+ {
+   ClutterText *self = CLUTTER_TEXT (actor);
+   ClutterTextPrivate *priv = self->priv;
+   ClutterBindingPool *pool;
+   gboolean res;
+   gint keyval;
+   if (!priv->editable)
+     return FALSE;
+   keyval = clutter_key_event_symbol (event);
+   pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor));
+   g_assert (pool != NULL);
+   /* we allow passing synthetic events that only contain
+    * the Unicode value and not the key symbol
+    */
+   if (keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC))
+     res = FALSE;
+   else
+     res = clutter_binding_pool_activate (pool, keyval,
+                                          event->modifier_state,
+                                          G_OBJECT (actor));
+   /* if the key binding has handled the event we bail out
+    * as fast as we can; otherwise, we try to insert the
+    * Unicode character inside the key event into the text
+    * actor
+    */
+   if (res)
+     return TRUE;
+   else
+     {
+       gunichar key_unichar = clutter_key_event_unicode (event);
+       /* return is reported as CR, but we want LF */
+       if (key_unichar == '\r')
+         key_unichar = '\n';
+       if (g_unichar_validate (key_unichar))
+         {
+           /* truncate the eventual selection so that the
+            * Unicode character can replace it
+            */
+           clutter_text_truncate_selection (self);
+           clutter_text_insert_unichar (self, key_unichar);
+           return TRUE;
+         }
+     }
+   return FALSE;
+ }
+ #define TEXT_PADDING    2
+ static void
+ clutter_text_paint (ClutterActor *self)
+ {
+   ClutterText *text = CLUTTER_TEXT (self);
+   ClutterTextPrivate *priv = text->priv;
+   PangoLayout *layout;
+   ClutterActorBox alloc = { 0, };
+   CoglColor color = { 0, };
+   guint8 real_opacity;
+   gint text_x = priv->text_x;
+   gboolean clip_set = FALSE;
+   if (G_UNLIKELY (priv->font_desc == NULL || priv->text == NULL))
+     {
+       CLUTTER_NOTE (ACTOR, "desc: %p, text %p",
+                   priv->font_desc ? priv->font_desc : 0x0,
+                   priv->text ? priv->text : 0x0);
+       return;
+     }
+   clutter_text_ensure_cursor_position (text);
+   clutter_actor_get_allocation_box (self, &alloc);
+   layout = clutter_text_create_layout (text, alloc.x2 - alloc.x1);
+   if (priv->single_line_mode)
+     {
+       PangoRectangle logical_rect = { 0, };
+       gint actor_width, text_width;
+       pango_layout_get_extents (layout, NULL, &logical_rect);
+       cogl_clip_set (0, 0,
+                      CLUTTER_UNITS_TO_FIXED (alloc.x2 - alloc.x1),
+                      CLUTTER_UNITS_TO_FIXED (alloc.y2 - alloc.y1));
+       clip_set = TRUE;
+       actor_width = (CLUTTER_UNITS_TO_DEVICE (alloc.x2 - alloc.x1))
+                   - 2 * TEXT_PADDING;
+       text_width  = logical_rect.width / PANGO_SCALE;
+       if (actor_width < text_width)
+         {
+           gint cursor_x = priv->cursor_pos.x;
+           if (priv->position == -1)
+             {
+               text_x = actor_width - text_width;
+               priv->cursor_pos.x += text_x + TEXT_PADDING;
+             }
+           else if (priv->position == 0)
+             {
+               text_x = 0;
+             }
+           else
+             {
+               if (text_x <= 0)
+                 {
+                   gint diff = -1 * text_x;
+                   if (cursor_x < diff)
+                     text_x += diff - cursor_x;
+                   else if (cursor_x > (diff + actor_width))
+                     text_x -= cursor_x - (diff - actor_width);
+                 }
+             }
+         }
+       else
+         {
+           text_x = 0;
+           priv->cursor_pos.x += text_x + TEXT_PADDING;
+         }
+     }
+   else
+     text_x = 0;
+   cursor_paint (text);
+   real_opacity = clutter_actor_get_paint_opacity (self)
+                * priv->text_color.alpha
+                / 255;
+   CLUTTER_NOTE (PAINT, "painting text (text:`%s')", priv->text);
+   cogl_color_set_from_4ub (&color,
+                            priv->text_color.red,
+                            priv->text_color.green,
+                            priv->text_color.blue,
+                            real_opacity);
+   cogl_pango_render_layout (layout, text_x, 0, &color, 0);
+   if (clip_set)
+     cogl_clip_unset ();
+   priv->text_x = text_x;
+ }
+ static void
+ clutter_text_get_preferred_width (ClutterActor *self,
+                                   ClutterUnit   for_height,
+                                   ClutterUnit  *min_width_p,
+                                   ClutterUnit  *natural_width_p)
+ {
+   ClutterText *text = CLUTTER_TEXT (self);
+   ClutterTextPrivate *priv = text->priv;
+   PangoRectangle logical_rect = { 0, };
+   PangoLayout *layout;
+   gint logical_width;
+   ClutterUnit layout_width;
+   layout = clutter_text_create_layout (text, -1);
+   pango_layout_get_extents (layout, NULL, &logical_rect);
+   /* the X coordinate of the logical rectangle might be non-zero
+    * according to the Pango documentation; hence, we need to offset
+    * the width accordingly
+    */
+   logical_width = logical_rect.x + logical_rect.width;
+   layout_width = logical_width > 0
+     ? CLUTTER_UNITS_FROM_PANGO_UNIT (logical_width)
+     : 1;
+   if (min_width_p)
+     {
+       if (priv->wrap || priv->ellipsize)
+         *min_width_p = 1;
+       else
+         *min_width_p = layout_width;
+     }
+   if (natural_width_p)
+     *natural_width_p = layout_width;
+ }
+ static void
+ clutter_text_get_preferred_height (ClutterActor *self,
+                                    ClutterUnit   for_width,
+                                    ClutterUnit  *min_height_p,
+                                    ClutterUnit  *natural_height_p)
+ {
+   ClutterText *text = CLUTTER_TEXT (self);
+   if (for_width == 0)
+     {
+       if (min_height_p)
+         *min_height_p = 0;
+       if (natural_height_p)
+         *natural_height_p = 0;
+     }
+   else
+     {
+       PangoLayout *layout;
+       PangoRectangle logical_rect = { 0, };
+       gint logical_height;
+       ClutterUnit layout_height;
+       layout = clutter_text_create_layout (text, for_width);
+       pango_layout_get_extents (layout, NULL, &logical_rect);
+       /* the Y coordinate of the logical rectangle might be non-zero
+        * according to the Pango documentation; hence, we need to offset
+        * the height accordingly
+        */
+       logical_height = logical_rect.y + logical_rect.height;
+       layout_height = CLUTTER_UNITS_FROM_PANGO_UNIT (logical_height);
+       if (min_height_p)
+         *min_height_p = layout_height;
+       if (natural_height_p)
+         *natural_height_p = layout_height;
+     }
+ }
+ static void
+ clutter_text_allocate (ClutterActor          *self,
+                        const ClutterActorBox *box,
+                        gboolean               origin_changed)
+ {
+   ClutterText *text = CLUTTER_TEXT (self);
+   ClutterActorClass *parent_class;
+   /* Ensure that there is a cached layout with the right width so
+    * that we don't need to create the text during the paint run
+    */
+   clutter_text_create_layout (text, box->x2 - box->x1);
+   parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class);
+   parent_class->allocate (self, box, origin_changed);
+ }
+ static gboolean
+ clutter_text_real_move_left (ClutterText         *self,
+                              const gchar         *action,
+                              guint                keyval,
+                              ClutterModifierType  modifiers)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   gint pos = priv->position;
+   gint len;
+   len = priv->n_chars;
+   if (pos != 0 && len !=0)
+     {
+       if (pos == -1)
+         clutter_text_set_cursor_position (self, len - 1);
+       else
+         clutter_text_set_cursor_position (self, pos - 1);
+     }
+   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
+     clutter_text_clear_selection (self);
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_real_move_right (ClutterText         *self,
+                               const gchar         *action,
+                               guint                keyval,
+                               ClutterModifierType  modifiers)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   gint pos = priv->position;
+   gint len;
+   len = priv->n_chars;
+   if (pos != -1 && len !=0)
+     {
+       if (pos != len)
+         clutter_text_set_cursor_position (self, pos + 1);
+     }
+   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
+     clutter_text_clear_selection (self);
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_real_move_up (ClutterText         *self,
+                            const gchar         *action,
+                            guint                keyval,
+                            ClutterModifierType  modifiers)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   PangoLayoutLine *layout_line;
+   PangoLayout *layout;
+   gint line_no;
+   gint index_;
+   gint x;
+   layout = clutter_text_get_layout (self);
+   if (priv->position == 0)
+     index_ = 0;
+   else
+     index_ = offset_to_bytes (priv->text, priv->position);
+   pango_layout_index_to_line_x (layout, index_,
+                                 0,
+                                 &line_no, &x);
+   line_no -= 1;
+   if (line_no < 0)
+     return FALSE;
+   if (priv->x_pos != -1)
+     x = priv->x_pos;
+   else
+     priv->x_pos = x;
+   layout_line = pango_layout_get_line_readonly (layout, line_no);
+   if (!layout_line)
+     return FALSE;
+   pango_layout_line_x_to_index (layout_line, x, &index_, NULL);
+   {
+     gint pos = bytes_to_offset (priv->text, index_);
+     clutter_text_set_cursor_position (self, pos);
+   }
+   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
+     clutter_text_clear_selection (self);
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_real_move_down (ClutterText         *self,
+                              const gchar         *action,
+                              guint                keyval,
+                              ClutterModifierType  modifiers)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   PangoLayoutLine *layout_line;
+   PangoLayout *layout;
+   gint line_no;
+   gint index_;
+   gint x;
+   layout = clutter_text_get_layout (self);
+   if (priv->position == 0)
+     index_ = 0;
+   else
+     index_ = offset_to_bytes (priv->text, priv->position);
+   pango_layout_index_to_line_x (layout, index_,
+                                 0,
+                                 &line_no, &x);
+   if (priv->x_pos != -1)
+     x = priv->x_pos;
+   else
+     priv->x_pos = x;
+   layout_line = pango_layout_get_line_readonly (layout, line_no + 1);
+   if (!layout_line)
+     return FALSE;
+   pango_layout_line_x_to_index (layout_line, x, &index_, NULL);
+   {
+     gint pos = bytes_to_offset (priv->text, index_);
+     clutter_text_set_cursor_position (self, pos);
+   }
+   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
+     clutter_text_clear_selection (self);
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_real_line_start (ClutterText         *self,
+                               const gchar         *action,
+                               guint                keyval,
+                               ClutterModifierType  modifiers)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   PangoLayoutLine *layout_line;
+   PangoLayout *layout;
+   gint line_no;
+   gint index_;
+   gint position;
+   layout = clutter_text_get_layout (self);
+   if (priv->position == 0)
+     index_ = 0;
+   else
+     index_ = offset_to_bytes (priv->text, priv->position);
+   pango_layout_index_to_line_x (layout, index_,
+                                 0,
+                                 &line_no, NULL);
+   layout_line = pango_layout_get_line_readonly (layout, line_no);
+   if (!layout_line)
+     return FALSE;
+   pango_layout_line_x_to_index (layout_line, 0, &index_, NULL);
+   position = bytes_to_offset (priv->text, index_);
+   clutter_text_set_cursor_position (self, position);
+   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
+     clutter_text_clear_selection (self);
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_real_line_end (ClutterText         *self,
+                             const gchar         *action,
+                             guint                keyval,
+                             ClutterModifierType  modifiers)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   PangoLayoutLine *layout_line;
+   PangoLayout *layout;
+   gint line_no;
+   gint index_;
+   gint trailing;
+   gint position;
+   layout = clutter_text_get_layout (self);
+   if (priv->position == 0)
+     index_ = 0;
+   else
+     index_ = offset_to_bytes (priv->text, priv->position);
+   pango_layout_index_to_line_x (layout, index_,
+                                 0,
+                                 &line_no, NULL);
+   layout_line = pango_layout_get_line_readonly (layout, line_no);
+   if (!layout_line)
+     return FALSE;
+   pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing);
+   index_ += trailing;
+   position = bytes_to_offset (priv->text, index_);
+   clutter_text_set_cursor_position (self, position);
+   if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
+     clutter_text_clear_selection (self);
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_real_select_all (ClutterText         *self,
+                               const gchar         *action,
+                               guint                keyval,
+                               ClutterModifierType  modifiers)
+ {
+   clutter_text_set_cursor_position (self, 0);
+   clutter_text_set_selection_bound (self, self->priv->n_chars);
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_real_del_next (ClutterText         *self,
+                             const gchar         *action,
+                             guint                keyval,
+                             ClutterModifierType  modifiers)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   gint pos;
+   gint len;
+   if (clutter_text_truncate_selection (self))
+     return TRUE;
+   pos = priv->position;
+   len = priv->n_chars;
+   if (len && pos != -1 && pos < len)
+     clutter_text_delete_text (self, pos, pos + 1);
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_real_del_prev (ClutterText         *self,
+                             const gchar         *action,
+                             guint                keyval,
+                             ClutterModifierType  modifiers)
+ {
+   ClutterTextPrivate *priv = self->priv;
+   gint pos;
+   gint len;
+   if (clutter_text_truncate_selection (self))
+     return TRUE;
+   pos = priv->position;
+   len = priv->n_chars;
+   if (pos != 0 && len != 0)
+     {
+       if (pos == -1)
+         {
+           clutter_text_set_cursor_position (self, len - 1);
+           clutter_text_set_selection_bound (self, len - 1);
+           clutter_text_delete_text (self, len - 1, len);
+         }
+       else
+         {
+           clutter_text_set_cursor_position (self, pos - 1);
+           clutter_text_set_selection_bound (self, pos - 1);
+           clutter_text_delete_text (self, pos - 1, pos);
+         }
+     }
+   return TRUE;
+ }
+ static gboolean
+ clutter_text_real_activate (ClutterText         *self,
+                             const gchar         *action,
+                             guint                keyval,
+                             ClutterModifierType  modifiers)
+ {
+   return clutter_text_activate (self);
+ }
+ static inline void
+ clutter_text_add_move_binding (ClutterBindingPool *pool,
+                                const gchar        *action,
+                                guint               key_val,
+                                GCallback           callback)
+ {
+   clutter_binding_pool_install_action (pool, action,
+                                        key_val, 0,
+                                        callback,
+                                        NULL, NULL);
+   clutter_binding_pool_install_action (pool, action,
+                                        key_val, CLUTTER_SHIFT_MASK,
+                                        callback,
+                                        NULL, NULL);
+ }
+ static void
+ clutter_text_class_init (ClutterTextClass *klass)
+ {
+   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+   ClutterBindingPool *binding_pool;
+   GParamSpec *pspec;
+   g_type_class_add_private (klass, sizeof (ClutterTextPrivate));
+   gobject_class->set_property = clutter_text_set_property;
+   gobject_class->get_property = clutter_text_get_property;
+   gobject_class->dispose = clutter_text_dispose;
+   gobject_class->finalize = clutter_text_finalize;
+   actor_class->paint = clutter_text_paint;
+   actor_class->get_preferred_width = clutter_text_get_preferred_width;
+   actor_class->get_preferred_height = clutter_text_get_preferred_height;
+   actor_class->allocate = clutter_text_allocate;
+   actor_class->key_press_event = clutter_text_key_press;
+   actor_class->button_press_event = clutter_text_button_press;
+   actor_class->button_release_event = clutter_text_button_release;
+   actor_class->motion_event = clutter_text_motion;
+   /**
+    * ClutterText:font-name:
+    *
+    * The font to be used by the #ClutterText, as a string
+    * that can be parsed by pango_font_description_from_string().
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_string ("font-name",
+                                "Font Name",
+                                "The font to be used by the text",
+                                NULL,
+                                CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec);
+   /**
+    * ClutterText:text:
+    *
+    * The text to render inside the actor.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_string ("text",
+                                "Text",
+                                "The text to render",
+                                "",
+                                CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
+   /**
+    * ClutterText:color:
+    *
+    * The color used to render the text.
+    *
+    * Since: 1.0
+    */
+   pspec = clutter_param_spec_color ("color",
+                                     "Font Color",
+                                     "Color of the font used by the text",
+                                     &default_text_color,
+                                     CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_COLOR, pspec);
+   /**
+    * ClutterText:editable:
+    *
+    * Whether key events delivered to the actor causes editing.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boolean ("editable",
+                                 "Editable",
+                                 "Whether the text is editable",
+                                 TRUE,
+                                 G_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_EDITABLE, pspec);
+   /**
+    * ClutterText:selectable:
+    *
+    * Whether it is possible to select text, either using the pointer
+    * or the keyboard.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boolean ("selectable",
+                                 "Selectable",
+                                 "Whether the text is selectable",
+                                 TRUE,
+                                 G_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_SELECTABLE, pspec);
+   /**
+    * ClutterText:activatable:
+    *
+    * Toggles whether return invokes the activate signal or not.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boolean ("activatable",
+                                 "Activatable",
+                                 "Whether pressing return causes the "
+                                 "activate signal to be emitted",
+                                 TRUE,
+                                 G_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_ACTIVATABLE, pspec);
+   /**
+    * ClutterText:cursor-visible:
+    *
+    * Whether the input cursor is visible or not, it will only be visible
+    * if both #ClutterText:cursor-visible and #ClutterText:editable are
+    * set to %TRUE.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boolean ("cursor-visible",
+                                 "Cursor Visible",
+                                 "Whether the input cursor is visible",
+                                 TRUE,
+                                 CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec);
+   /**
+    * ClutterText:cursor-color:
+    *
+    * The color of the cursor.
+    *
+    * Since: 1.0
+    */
+   pspec = clutter_param_spec_color ("cursor-color",
+                                     "Cursor Color",
+                                     "Cursor Color",
+                                     &default_cursor_color,
+                                     CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec);
+   /**
+    * ClutterText:cursor-color-set:
+    *
+    * Will be set to %TRUE if #ClutterText:cursor-color has been set.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boolean ("cursor-color-set",
+                                 "Cursor Color Set",
+                                 "Whether the cursor color has been set",
+                                 FALSE,
+                                 CLUTTER_PARAM_READABLE);
+   g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR_SET, pspec);
+   /**
+    * ClutterText:cursor-size:
+    *
+    * The size of the cursor, in pixels. If set to -1 the size used will
+    * be the default cursor size of 2 pixels.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_int ("cursor-size",
+                             "Cursor Size",
+                             "The width of the cursor, in pixels",
+                             -1, G_MAXINT, DEFAULT_CURSOR_SIZE,
+                             CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_CURSOR_SIZE, pspec);
+   /**
+    * ClutterText:position:
+    *
+    * The current input cursor position. -1 is taken to be the end of the text
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_int ("position",
+                             "Position",
+                             "The cursor position",
+                             -1, G_MAXINT,
+                             -1,
+                             CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_POSITION, pspec);
+   /**
+    * ClutterText:selection-bound:
+    *
+    * The current input cursor position. -1 is taken to be the end of the text
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_int ("selection-bound",
+                             "Selection-bound",
+                             "The cursor position of the other end "
+                             "of the selection",
+                             -1, G_MAXINT,
+                             -1,
+                             CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec);
+   /**
+    * ClutterText:attributes:
+    *
+    * A list of #PangoStyleAttribute<!-- -->s to be applied to the
+    * contents of the #ClutterText actor.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boxed ("attributes",
+                               "Attributes",
+                               "A list of style attributes to apply to "
+                               "the contents of the actor",
+                               PANGO_TYPE_ATTR_LIST,
+                               CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec);
+   /**
+    * ClutterText:use-markup:
+    *
+    * Whether the text includes Pango markup. See pango_layout_set_markup()
+    * in the Pango documentation.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boolean ("use-markup",
+                                 "Use markup",
+                                 "Whether or not the text "
+                                 "includes Pango markup",
+                                 FALSE,
+                                 CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec);
+   /**
+    * ClutterText:line-wrap:
+    *
+    * Whether to wrap the lines of #ClutterText:text if the contents
+    * exceed the available allocation. The wrapping strategy is
+    * controlled by the #ClutterText:line-wrap-mode property.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boolean ("line-wrap",
+                                 "Line wrap",
+                                 "If set, wrap the lines if the text "
+                                 "becomes too wide",
+                                 FALSE,
+                                 CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_LINE_WRAP, pspec);
+   /**
+    * ClutterText:line-wrap-mode:
+    *
+    * If #ClutterText:line-wrap is set to %TRUE, this property will
+    * control how the text is wrapped.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_enum ("line-wrap-mode",
+                              "Line wrap mode",
+                              "Control how line-wrapping is done",
+                              PANGO_TYPE_WRAP_MODE,
+                              PANGO_WRAP_WORD,
+                              CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_LINE_WRAP_MODE, pspec);
+   /**
+    * ClutterText:ellipsize:
+    *
+    * The preferred place to ellipsize the contents of the #ClutterText actor
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_enum ("ellipsize",
+                              "Ellipsize",
+                              "The preferred place to ellipsize the string",
+                              PANGO_TYPE_ELLIPSIZE_MODE,
+                              PANGO_ELLIPSIZE_NONE,
+                              CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec);
+   /**
+    * ClutterText:alignment:
+    *
+    * The preferred alignment for the text. This property controls
+    * the alignment of multi-line paragraphs.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_enum ("alignment",
+                              "Alignment",
+                              "The preferred alignment for the string, "
+                              "for multi-line text",
+                              PANGO_TYPE_ALIGNMENT,
+                              PANGO_ALIGN_LEFT,
+                              CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_ALIGNMENT, pspec);
+   /**
+    * ClutterText:justify:
+    *
+    * Whether the contents of the #ClutterText should be justified
+    * on both margins.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boolean ("justify",
+                                 "Justify",
+                                 "Whether the text should be justified",
+                                 FALSE,
+                                 CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec);
+   /**
+    * ClutterText:password-char:
+    *
+    * If non-zero, the character that should be used in place of
+    * the actual text in a password text actor.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_unichar ("password-char",
+                                 "Password Character",
+                                 "If non-zero, use this character to "
+                                 "display the actor's contents",
+                                 0,
+                                 CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_PASSWORD_CHAR, pspec);
+   /**
+    * ClutterText:max-length:
+    *
+    * The maximum length of the contents of the #ClutterText actor.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_int ("max-length",
+                             "Max Length",
+                             "Maximum length of the text inside the actor",
+                             -1, G_MAXINT, 0,
+                             CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec);
+   /**
+    * ClutterText:single-line-mode:
+    *
+    * Whether the #ClutterText actor should be in single line mode
+    * or not. A single line #ClutterText actor will only contain a
+    * single line of text, scrolling it in case its length is bigger
+    * than the allocated size.
+    *
+    * Setting this property will also set the #ClutterText:activatable
+    * property as a side-effect.
+    *
+    * Since: 1.0
+    */
+   pspec = g_param_spec_boolean ("single-line-mode",
+                                 "Single Line Mode",
+                                 "Whether the text should be a single line",
+                                 FALSE,
+                                 CLUTTER_PARAM_READWRITE);
+   g_object_class_install_property (gobject_class, PROP_SINGLE_LINE_MODE, pspec);
+   /**
+    * ClutterText::text-changed:
+    * @self: the #ClutterText that emitted the signal
+    *
+    * The ::text-changed signal is emitted after @actor's text changes
+    *
+    * Since: 1.0
+    */
+   text_signals[TEXT_CHANGED] =
+     g_signal_new ("text-changed",
+                   G_TYPE_FROM_CLASS (gobject_class),
+                   G_SIGNAL_RUN_LAST,
+                   G_STRUCT_OFFSET (ClutterTextClass, text_changed),
+                   NULL, NULL,
+                   g_cclosure_marshal_VOID__VOID,
+                   G_TYPE_NONE, 0);
+   /**
+    * ClutterText::cursor-event:
+    * @self: the #ClutterText that emitted the signal
+    * @geometry: the coordinates of the cursor
+    *
+    * The ::cursor-event signal is emitted whenever the cursor position
+    * changes inside a #ClutterText actor. Inside @geometry it is stored
+    * the current position and size of the cursor, relative to the actor
+    * itself.
+    *
+    * Since: 1.0
+    */
+   text_signals[CURSOR_EVENT] =
+     g_signal_new ("cursor-event",
+                 G_TYPE_FROM_CLASS (gobject_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (ClutterTextClass, cursor_event),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__BOXED,
+                 G_TYPE_NONE, 1,
+                 CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE);
+   /**
+    * ClutterText::activate
+    * @self: the #ClutterText that emitted the signal
+    *
+    * The ::activate signal is emitted each time the actor is 'activated'
+    * by the user, normally by pressing the 'Enter' key. The signal is
+    * emitted only if #ClutterText:activatable is set to %TRUE.
+    *
+    * Since: 1.0
+    */
+   text_signals[ACTIVATE] =
+     g_signal_new ("activate",
+                   G_TYPE_FROM_CLASS (gobject_class),
+                   G_SIGNAL_RUN_LAST,
+                   G_STRUCT_OFFSET (ClutterTextClass, activate),
+                   NULL, NULL,
+                   g_cclosure_marshal_VOID__VOID,
+                   G_TYPE_NONE, 0);
+   binding_pool = clutter_binding_pool_get_for_class (klass);
+   clutter_text_add_move_binding (binding_pool, "move-left",
+                                  CLUTTER_Left,
+                                  G_CALLBACK (clutter_text_real_move_left));
+   clutter_text_add_move_binding (binding_pool, "move-left",
+                                  CLUTTER_KP_Left,
+                                  G_CALLBACK (clutter_text_real_move_left));
+   clutter_text_add_move_binding (binding_pool, "move-right",
+                                  CLUTTER_Right,
+                                  G_CALLBACK (clutter_text_real_move_right));
+   clutter_text_add_move_binding (binding_pool, "move-right",
+                                  CLUTTER_KP_Right,
+                                  G_CALLBACK (clutter_text_real_move_right));
+   clutter_text_add_move_binding (binding_pool, "move-up",
+                                  CLUTTER_Up,
+                                  G_CALLBACK (clutter_text_real_move_up));
+   clutter_text_add_move_binding (binding_pool, "move-up",
+                                  CLUTTER_KP_Up,
+                                  G_CALLBACK (clutter_text_real_move_up));
+   clutter_text_add_move_binding (binding_pool, "move-down",
+                                  CLUTTER_Down,
+                                  G_CALLBACK (clutter_text_real_move_down));
+   clutter_text_add_move_binding (binding_pool, "move-down",
+                                  CLUTTER_KP_Down,
+                                  G_CALLBACK (clutter_text_real_move_down));
+   clutter_text_add_move_binding (binding_pool, "line-start",
+                                  CLUTTER_Home,
+                                  G_CALLBACK (clutter_text_real_line_start));
+   clutter_text_add_move_binding (binding_pool, "line-start",
+                                  CLUTTER_KP_Home,
+                                  G_CALLBACK (clutter_text_real_line_start));
+   clutter_text_add_move_binding (binding_pool, "line-start",
+                                  CLUTTER_Begin,
+                                  G_CALLBACK (clutter_text_real_line_start));
+   clutter_text_add_move_binding (binding_pool, "line-end",
+                                  CLUTTER_End,
+                                  G_CALLBACK (clutter_text_real_line_end));
+   clutter_text_add_move_binding (binding_pool, "line-end",
+                                  CLUTTER_KP_End,
+                                  G_CALLBACK (clutter_text_real_line_end));
+   clutter_binding_pool_install_action (binding_pool, "select-all",
+                                        CLUTTER_a, CLUTTER_CONTROL_MASK,
+                                        G_CALLBACK (clutter_text_real_select_all),
+                                        NULL, NULL);
+   clutter_binding_pool_install_action (binding_pool, "delete-next",
+                                        CLUTTER_Delete, 0,
+                                        G_CALLBACK (clutter_text_real_del_next),
+                                        NULL, NULL);
+   clutter_binding_pool_install_action (binding_pool, "delete-next",
+                                        CLUTTER_KP_Delete, 0,
+                                        G_CALLBACK (clutter_text_real_del_next),
+                                        NULL, NULL);
+   clutter_binding_pool_install_action (binding_pool, "delete-prev",
+                                        CLUTTER_BackSpace, 0,
+                                        G_CALLBACK (clutter_text_real_del_prev),
+                                        NULL, NULL);
+   clutter_binding_pool_install_action (binding_pool, "activate",
+                                        CLUTTER_Return, 0,
+                                        G_CALLBACK (clutter_text_real_activate),
+                                        NULL, NULL);
+   clutter_binding_pool_install_action (binding_pool, "activate",
+                                        CLUTTER_KP_Enter, 0,
+                                        G_CALLBACK (clutter_text_real_activate),
+                                        NULL, NULL);
+   clutter_binding_pool_install_action (binding_pool, "activate",
+                                        CLUTTER_ISO_Enter, 0,
+                                        G_CALLBACK (clutter_text_real_activate),
+                                        NULL, NULL);
+ }
+ static void
+ clutter_text_init (ClutterText *self)
+ {
+   ClutterTextPrivate *priv;
+   const gchar *font_name;
+   int i;
+   self->priv = priv = CLUTTER_TEXT_GET_PRIVATE (self);
+   priv->alignment     = PANGO_ALIGN_LEFT;
+   priv->wrap          = FALSE;
+   priv->wrap_mode     = PANGO_WRAP_WORD;
+   priv->ellipsize     = PANGO_ELLIPSIZE_NONE;
+   priv->use_underline = FALSE;
+   priv->use_markup    = FALSE;
+   priv->justify       = FALSE;
+   for (i = 0; i < N_CACHED_LAYOUTS; i++)
+     priv->cached_layouts[i].layout = NULL;
+   /* default to "" so that clutter_text_get_text() will
+    * return a valid string and we can safely call strlen()
+    * or strcmp() on it
+    */
+   priv->text = g_strdup ("");
+   priv->text_color = default_text_color;
+   priv->cursor_color = default_cursor_color;
+   /* get the default font name from the context */
+   font_name = clutter_backend_get_font_name (clutter_get_default_backend ());
+   priv->font_name = g_strdup (font_name);
+   priv->font_desc = pango_font_description_from_string (font_name);
+   priv->position = -1;
+   priv->selection_bound = -1;
+   priv->x_pos = -1;
+   priv->cursor_visible = TRUE;
+   priv->editable = FALSE;
+   priv->selectable = TRUE;
+   priv->cursor_color_set = FALSE;
+   priv->password_char = 0;
+   priv->max_length = 0;
+   priv->cursor_size = DEFAULT_CURSOR_SIZE;
+ }
+ /**
+  * clutter_text_new:
+  *
+  * Creates a new #ClutterText actor. This actor can be used to
+  * display and edit text.
+  *
+  * Return value: the newly created #ClutterText actor
+  *
+  * Since: 1.0
+  */
+ ClutterActor *
+ clutter_text_new (void)
+ {
+   return g_object_new (CLUTTER_TYPE_TEXT, NULL);
+ }
+ /**
+  * clutter_text_new_full:
+  * @font_name: a string with a font description
+  * @text: the contents of the actor
+  * @color: the color to be used to render @text
+  *
+  * Creates a new #ClutterText actor, using @font_name as the font
+  * description; @text will be used to set the contents of the actor;
+  * and @color will be used as the color to render @text.
+  *
+  * This function is equivalent to calling clutter_text_new(),
+  * clutter_text_set_font_name(), clutter_text_set_text() and
+  * clutter_text_set_color().
+  *
+  * Return value: the newly created #ClutterText actor
+  *
+  * Since: 1.0
+  */
+ ClutterActor *
+ clutter_text_new_full (const gchar        *font_name,
+                        const gchar        *text,
+                        const ClutterColor *color)
+ {
+   return g_object_new (CLUTTER_TYPE_TEXT,
+                        "font-name", font_name,
+                        "text", text,
+                        "color", color,
+                        NULL);
+ }
+ /**
+  * clutter_text_new_with_text:
+  * @font_name: a string with a font description
+  * @text: the contents of the actor
+  *
+  * Creates a new #ClutterText actor, using @font_name as the font
+  * description; @text will be used to set the contents of the actor.
+  *
+  * This function is equivalent to calling clutter_text_new(),
+  * clutter_text_set_font_name(), and clutter_text_set_text().
+  *
+  * Return value: the newly created #ClutterText actor
+  *
+  * Since: 1.0
+  */
+ ClutterActor *
+ clutter_text_new_with_text (const gchar *font_name,
+                             const gchar *text)
+ {
+   return g_object_new (CLUTTER_TYPE_TEXT,
+                        "font-name", font_name,
+                        "text", text,
+                        NULL);
+ }
+ /**
+  * clutter_text_set_editable:
+  * @self: a #ClutterText
+  * @editable: whether the #ClutterText should be editable
+  *
+  * Sets whether the #ClutterText actor should be editable.
+  *
+  * An editable #ClutterText with key focus set using
+  * clutter_actor_grab_key_focus() or clutter_stage_take_key_focus()
+  * will receive key events and will update its contents accordingly.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_editable (ClutterText *self,
+                            gboolean     editable)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->editable != editable)
+     {
+       priv->editable = editable;
+       if (CLUTTER_ACTOR_IS_VISIBLE (self))
+         clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "editable");
+     }
+ }
+ /**
+  * clutter_text_get_editable:
+  * @self: a #ClutterText
+  *
+  * Retrieves whether a #ClutterText is editable or not.
+  *
+  * Return value: %TRUE if the actor is editable
+  *
+  * Since: 1.0
+  */
+ gboolean
+ clutter_text_get_editable (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
+   return self->priv->editable;
+ }
+ /**
+  * clutter_text_set_selectable:
+  * @self: a #ClutterText
+  * @selectable: whether the #ClutterText actor should be selectable
+  *
+  * Sets whether a #ClutterText actor should be selectable.
+  *
+  * A selectable #ClutterText will allow selecting its contents using
+  * the pointer or the keyboard.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_selectable (ClutterText *self,
+                              gboolean     selectable)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->selectable != selectable)
+     {
+       priv->selectable = selectable;
+       if (CLUTTER_ACTOR_IS_VISIBLE (self))
+         clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "selectable");
+     }
+ }
+ /**
+  * clutter_text_get_selectable:
+  * @self: a #ClutterText
+  *
+  * Retrieves whether a #ClutterText is selectable or not.
+  *
+  * Return value: %TRUE if the actor is selectable
+  *
+  * Since: 1.0
+  */
+ gboolean
+ clutter_text_get_selectable (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
+   return self->priv->selectable;
+ }
+ /**
+  * clutter_text_set_activatable:
+  * @self: a #ClutterText
+  * @activatable: whether the #ClutterText actor should be activatable
+  *
+  * Sets whether a #ClutterText actor should be activatable.
+  *
+  * An activatable #ClutterText actor will emit the #ClutterText::activate
+  * signal whenever the 'Enter' (or 'Return') key is pressed; if it is not
+  * activatable, a new line will be appended to the current content.
+  *
+  * An activatable #ClutterText must also be set as editable using
+  * clutter_text_set_editable().
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_activatable (ClutterText *self,
+                               gboolean     activatable)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->activatable != activatable)
+     {
+       priv->activatable = activatable;
+       if (CLUTTER_ACTOR_IS_VISIBLE (self))
+         clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "activatable");
+     }
+ }
+ /**
+  * clutter_text_get_activatable:
+  * @self: a #ClutterText
+  *
+  * Retrieves whether a #ClutterText is activatable or not.
+  *
+  * Return value: %TRUE if the actor is activatable
+  *
+  * Since: 1.0
+  */
+ gboolean
+ clutter_text_get_activatable (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
+   return self->priv->activatable;
+ }
+ /**
+  * clutter_text_activate:
+  * @self: a #ClutterText
+  *
+  * Emits the #ClutterText::activate signal, if @self has been set
+  * as activatable using clutter_text_set_activatable().
+  *
+  * This function can be used to emit the ::activate signal inside
+  * a #ClutterActor::captured-event or #ClutterActor::key-press-event
+  * signal handlers before the default signal handler for the
+  * #ClutterText is invoked.
+  *
+  * Return value: %TRUE if the ::activate signal has been emitted,
+  *   and %FALSE otherwise
+  *
+  * Since: 1.0
+  */
+ gboolean
+ clutter_text_activate (ClutterText *self)
+ {
+   ClutterTextPrivate *priv;
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
+   priv = self->priv;
+   if (priv->activatable)
+     {
+       g_signal_emit (self, text_signals[ACTIVATE], 0);
+       return TRUE;
+     }
+   return FALSE;
+ }
+ /**
+  * clutter_text_set_cursor_visible:
+  * @self: a #ClutterText
+  * @cursor_visible: whether the cursor should be visible
+  *
+  * Sets whether the cursor of a #ClutterText actor should be
+  * visible or not.
+  *
+  * The color of the cursor will be the same as the text color
+  * unless clutter_text_set_cursor_color() has been called.
+  *
+  * The size of the cursor can be set using clutter_text_set_cursor_size().
+  *
+  * The position of the cursor can be changed programmatically using
+  * clutter_text_set_cursor_position().
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_cursor_visible (ClutterText *self,
+                                  gboolean     cursor_visible)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->cursor_visible != cursor_visible)
+     {
+       priv->cursor_visible = cursor_visible;
+       if (CLUTTER_ACTOR_IS_VISIBLE (self))
+         clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "cursor-visible");
+     }
+ }
+ /**
+  * clutter_text_get_cursor_visible:
+  * @self: a #ClutterText
+  *
+  * Retrieves whether the cursor of a #ClutterText actor is visible.
+  *
+  * Return value: %TRUE if the cursor is visible
+  *
+  * Since: 1.0
+  */
+ gboolean
+ clutter_text_get_cursor_visible (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE);
+   return self->priv->cursor_visible;
+ }
+ /**
+  * clutter_text_set_cursor_color:
+  * @self: a #ClutterText
+  * @color: the color of the cursor, or %NULL to unset it
+  *
+  * Sets the color of the cursor of a #ClutterText actor.
+  *
+  * If @color is %NULL, the cursor color will be the same as the
+  * text color.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_cursor_color (ClutterText        *self,
+                                const ClutterColor *color)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (color)
+     {
+       priv->cursor_color = *color;
+       priv->cursor_color_set = TRUE;
+     }
+   else
+     priv->cursor_color_set = FALSE;
+   if (CLUTTER_ACTOR_IS_VISIBLE (self))
+     clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+   g_object_notify (G_OBJECT (self), "cursor-color");
+   g_object_notify (G_OBJECT (self), "cursor-color-set");
+ }
+ /**
+  * clutter_text_get_cursor_color:
+  * @self: a #ClutterText
+  * @color: return location for a #ClutterColor
+  *
+  * Retrieves the color of the cursor of a #ClutterText actor.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_get_cursor_color (ClutterText  *self,
+                                ClutterColor *color)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   g_return_if_fail (color != NULL);
+   priv = self->priv;
+   *color = priv->cursor_color;
+ }
+ /**
+  * clutter_text_set_selection:
+  * @self: a #ClutterText
+  * @start_pos: start of the selection, in characters
+  * @end_pos: end of the selection, in characters
+  *
+  * Selects the region of text between @start_pos and @end_pos.
+  *
+  * This function changes the position of the cursor to match
+  * @start_pos and the selection bound to match @end_pos.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_selection (ClutterText *self,
+                             gssize       start_pos,
+                             gssize       end_pos)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (end_pos < 0)
+     end_pos = priv->n_chars;
+   start_pos = MIN (priv->n_chars, start_pos);
+   end_pos = MIN (priv->n_chars, end_pos);
+   g_object_freeze_notify (G_OBJECT (self));
+   clutter_text_set_cursor_position (self, start_pos);
+   clutter_text_set_selection_bound (self, end_pos);
+   g_object_thaw_notify (G_OBJECT (self));
+ }
+ /**
+  * clutter_text_get_selection:
+  * @self: a #ClutterText
+  *
+  * Retrieves the currently selected text.
+  *
+  * Return value: a newly allocated string containing the currently
+  *   selected text, or %NULL. Use g_free() to free the returned
+  *   string.
+  *
+  * Since: 1.0
+  */
+ gchar *
+ clutter_text_get_selection (ClutterText *self)
+ {
+   ClutterTextPrivate *priv;
+   gchar *str;
+   gint len;
+   gint start_index, end_index;
+   gint start_offset, end_offset;
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
+   priv = self->priv;
+   start_index = priv->position;
+   end_index = priv->selection_bound;
+   if (end_index == start_index)
+     return g_strdup ("");
+   if (end_index < start_index)
+     {
+       gint temp = start_index;
+       start_index = end_index;
+       end_index = temp;
+     }
+   start_offset = offset_to_bytes (priv->text, start_index);
+   end_offset = offset_to_bytes (priv->text, end_index);
+   len = end_offset - start_offset;
+   str = g_malloc (len + 1);
+   g_utf8_strncpy (str, priv->text + start_offset, end_index - start_index);
+   return str;
+ }
+ /**
+  * clutter_text_set_selection_bound:
+  * @self: a #ClutterText
+  * @selection_bound: the position of the end of the selection, in characters
+  *
+  * Sets the other end of the selection, starting from the current
+  * cursor position.
+  *
+  * If @selection_bound is -1, the selection unset.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_selection_bound (ClutterText *self,
+                                   gint         selection_bound)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->selection_bound != selection_bound)
+     {
+       priv->selection_bound = selection_bound;
+       if (CLUTTER_ACTOR_IS_VISIBLE (self))
+         clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "selection-bound");
+     }
+ }
+ /**
+  * clutter_text_get_selection_bound:
+  * @self: a #ClutterText
+  *
+  * Retrieves the other end of the selection of a #ClutterText actor,
+  * in characters from the current cursor position.
+  *
+  * Return value: the position of the other end of the selection
+  *
+  * Since: 1.0
+  */
+ gint
+ clutter_text_get_selection_bound (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1);
+   return self->priv->selection_bound;
+ }
+ /**
+  * clutter_text_get_font_name:
+  * @self: a #ClutterText
+  *
+  * Retrieves the font name as set by clutter_text_set_font_name().
+  *
+  * Return value: a string containing the font name. The returned
+  *   string is owned by the #ClutterText actor and should not be
+  *   modified or freed
+  *
+  * Since: 1.0
+  */
+ G_CONST_RETURN gchar *
+ clutter_text_get_font_name (ClutterText *text)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL);
+   return text->priv->font_name;
+ }
+ /**
+  * clutter_text_set_font_name:
+  * @self: a #ClutterText
+  * @font_name: a font name, or %NULL to set the default font name
+  *
+  * Sets the font used by a #ClutterText. The @font_name string
+  * must either be %NULL, which means that the font name from the
+  * default #ClutterBackend will be used; or be something that can
+  * be parsed by the pango_font_description_from_string() function,
+  * like:
+  *
+  * |[
+  *   clutter_text_set_font_name (text, "Sans 10pt");
+  *   clutter_text_set_font_name (text, "Serif 16px");
+  *   clutter_text_set_font_name (text, "Helvetica 10");
+  * ]|
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_font_name (ClutterText *self,
+                             const gchar *font_name)
+ {
+   ClutterTextPrivate *priv;
+   PangoFontDescription *desc;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   /* get the default font name from the backend */
+   if (!font_name || font_name[0] == '\0')
+     font_name = clutter_backend_get_font_name (clutter_get_default_backend ());
+   priv = self->priv;
+   if (priv->font_name && strcmp (priv->font_name, font_name) == 0)
+     return;
+   desc = pango_font_description_from_string (font_name);
+   if (!desc)
+     {
+       g_warning ("Attempting to create a PangoFontDescription for "
+                "font name `%s', but failed.",
+                font_name);
+       return;
+     }
+   g_free (priv->font_name);
+   priv->font_name = g_strdup (font_name);
+   if (priv->font_desc)
+     pango_font_description_free (priv->font_desc);
+   priv->font_desc = desc;
+   clutter_text_dirty_cache (self);
+   if (priv->text && priv->text[0] != '\0')
+     clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+   g_object_notify (G_OBJECT (self), "font-name");
+ }
+ /**
+  * clutter_text_get_text:
+  * @self: a #ClutterText
+  *
+  * Retrieves a pointer to the current contents of a #ClutterText
+  * actor.
+  *
+  * If you need a copy of the contents for manipulating, either
+  * use g_strdup() on the returned string, or use:
+  *
+  * |[
+  *    copy = clutter_text_get_chars (text, 0, -1);
+  * ]|
+  *
+  * Which will return a newly allocated string.
+  *
+  * Return value: the contents of the actor. The returned string
+  *   is owned by the #ClutterText actor and should never be
+  *   modified or freed
+  *
+  * Since: 1.0
+  */
+ G_CONST_RETURN gchar *
+ clutter_text_get_text (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
+   return self->priv->text;
+ }
+ /**
+  * clutter_text_set_text:
+  * @self: a #ClutterText
+  * @text: the text to set
+  *
+  * Sets the contents of a #ClutterText actor. The @text string
+  * must not be %NULL; to unset the current contents of the
+  * #ClutterText actor simply pass "" (an empty string).
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_text (ClutterText *self,
+                        const gchar *text)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   g_return_if_fail (text != NULL);
+   priv = self->priv;
+   if (priv->max_length > 0)
+     {
+       gint len = g_utf8_strlen (text, -1);
+       if (len < priv->max_length)
+         {
+            g_free (priv->text);
+            priv->text = g_strdup (text);
+            priv->n_bytes = strlen (text);
+            priv->n_chars = len;
+         }
+       else
+         {
+           gchar *n = g_malloc0 (priv->max_length + 1);
+           g_free (priv->text);
+           g_utf8_strncpy (n, text, priv->max_length);
+           priv->text = n;
+           priv->n_bytes = strlen (n);
+           priv->n_chars = priv->max_length;
+         }
+     }
+   else
+     {
+       g_free (priv->text);
+       priv->text = g_strdup (text);
+       priv->n_bytes = strlen (text);
+       priv->n_chars = g_utf8_strlen (text, -1);
+     }
+   clutter_text_dirty_cache (self);
+   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+   g_signal_emit (self, text_signals[TEXT_CHANGED], 0);
+   g_object_notify (G_OBJECT (self), "text");
+ }
+ /**
+  * clutter_text_get_layout:
+  * @self: a #ClutterText
+  *
+  * Retrieves the current #PangoLayout used by a #ClutterText actor.
+  *
+  * Return value: a #PangoLayout. The returned object is owned by
+  *   the #ClutterText actor and should not be modified or freed
+  *
+  * Since: 1.0
+  */
+ PangoLayout *
+ clutter_text_get_layout (ClutterText *self)
+ {
+   ClutterUnit width;
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
+   width = clutter_actor_get_widthu (CLUTTER_ACTOR (self));
+   return clutter_text_create_layout (self, width);
+ }
+ /**
+  * clutter_text_set_color:
+  * @self: a #ClutterText
+  * @color: a #ClutterColor
+  *
+  * Sets the color of the contents of a #ClutterText actor.
+  *
+  * The overall opacity of the #ClutterText actor will be the
+  * result of the alpha value of @color and the composited
+  * opacity of the actor itself on the scenegraph, as returned
+  * by clutter_actor_get_paint_opacity().
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_color (ClutterText        *self,
+                         const ClutterColor *color)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   g_return_if_fail (color != NULL);
+   priv = self->priv;
+   priv->text_color = *color;
+   if (CLUTTER_ACTOR_IS_VISIBLE (self))
+     clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+   g_object_notify (G_OBJECT (self), "color");
+ }
+ /**
+  * clutter_text_get_color:
+  * @self: a #ClutterText
+  * @color: return location for a #ClutterColor
+  *
+  * Retrieves the text color as set by clutter_text_get_color().
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_get_color (ClutterText  *self,
+                         ClutterColor *color)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   g_return_if_fail (color != NULL);
+   priv = self->priv;
+   *color = priv->text_color;
+ }
+ /**
+  * clutter_text_set_ellipsize:
+  * @self: a #ClutterText
+  * @mode: a #PangoEllipsizeMode
+  *
+  * Sets the mode used to ellipsize (add an ellipsis: "...") to the
+  * text if there is not enough space to render the entire contents
+  * of a #ClutterText actor
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_ellipsize (ClutterText        *self,
+                           PangoEllipsizeMode  mode)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE &&
+                   mode <= PANGO_ELLIPSIZE_END);
+   priv = self->priv;
+   if ((PangoEllipsizeMode) priv->ellipsize != mode)
+     {
+       priv->ellipsize = mode;
+       clutter_text_dirty_cache (self);
+       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "ellipsize");
+     }
+ }
+ /**
+  * clutter_text_get_ellipsize:
+  * @self: a #ClutterText
+  *
+  * Returns the ellipsizing position of a #ClutterText actor, as
+  * set by clutter_text_set_ellipsize().
+  *
+  * Return value: #PangoEllipsizeMode
+  *
+  * Since: 1.0
+  */
+ PangoEllipsizeMode
+ clutter_text_get_ellipsize (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ELLIPSIZE_NONE);
+   return self->priv->ellipsize;
+ }
+ /**
+  * clutter_text_get_line_wrap:
+  * @self: a #ClutterText
+  *
+  * Retrieves the value set using clutter_text_set_line_wrap().
+  *
+  * Return value: %TRUE if the #ClutterText actor should wrap
+  *   its contents
+  *
+  * Since: 1.0
+  */
+ gboolean
+ clutter_text_get_line_wrap (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
+   return self->priv->wrap;
+ }
+ /**
+  * clutter_text_set_line_wrap:
+  * @self: a #ClutterText
+  * @line_wrap: whether the contents should wrap
+  *
+  * Sets whether the contents of a #ClutterText actor should wrap,
+  * if they don't fit the size assigned to the actor.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_line_wrap (ClutterText *self,
+                             gboolean     line_wrap)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->wrap != line_wrap)
+     {
+       priv->wrap = line_wrap;
+       clutter_text_dirty_cache (self);
+       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "line-wrap");
+     }
+ }
+ /**
+  * clutter_text_set_line_wrap_mode:
+  * @self: a #ClutterText
+  * @wrap_mode: the line wrapping mode
+  *
+  * If line wrapping is enabled (see clutter_text_set_line_wrap()) this
+  * function controls how the line wrapping is performed. The default is
+  * %PANGO_WRAP_WORD which means wrap on word boundaries.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_line_wrap_mode (ClutterText   *self,
+                                PangoWrapMode  wrap_mode)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->wrap_mode != wrap_mode)
+     {
+       priv->wrap_mode = wrap_mode;
+       clutter_text_dirty_cache (self);
+       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "line-wrap-mode");
+     }
+ }
+ /**
+  * clutter_text_get_line_wrap_mode:
+  * @self: a #ClutterText
+  *
+  * Retrieves the line wrap mode used by the #ClutterText actor.
+  *
+  * See clutter_text_set_line_wrap_mode ().
+  *
+  * Return value: the wrap mode used by the #ClutterText
+  *
+  * Since: 1.0
+  */
+ PangoWrapMode
+ clutter_text_get_line_wrap_mode (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_WRAP_WORD);
+   return self->priv->wrap_mode;
+ }
+ /**
+  * clutter_text_set_attributes:
+  * @self: a #ClutterText
+  * @attrs: a #PangoAttrList or %NULL to unset the attributes
+  *
+  * Sets the attributes list that are going to be applied to the
+  * #ClutterText contents. The attributes set with this function
+  * will be ignored if the #ClutterText:use_markup property is
+  * set to %TRUE.
+  *
+  * The #ClutterText actor will take a reference on the #PangoAttrList
+  * passed to this function.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_attributes (ClutterText   *self,
+                            PangoAttrList *attrs)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (attrs)
+     pango_attr_list_ref (attrs);
+   if (priv->attrs)
+     pango_attr_list_unref (priv->attrs);
+   if (!priv->use_markup)
+     {
+       if (attrs)
+       pango_attr_list_ref (attrs);
+       if (priv->effective_attrs)
+       pango_attr_list_unref (priv->effective_attrs);
+       priv->effective_attrs = attrs;
+     }
+   priv->attrs = attrs;
+   clutter_text_dirty_cache (self);
+   g_object_notify (G_OBJECT (self), "attributes");
+   clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+ }
+ /**
+  * clutter_text_get_attributes:
+  * @self: a #ClutterText
+  *
+  * Gets the attribute list that was set on the #ClutterText actor
+  * clutter_text_set_attributes(), if any.
+  *
+  * Return value: the attribute list, or %NULL if none was set. The
+  *  returned value is owned by the #ClutterText and should not be
+  *  unreferenced.
+  *
+  * Since: 1.0
+  */
+ PangoAttrList *
+ clutter_text_get_attributes (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
+   return self->priv->attrs;
+ }
+ /**
+  * clutter_text_set_alignment:
+  * @self: a #ClutterText
+  * @alignment: A #PangoAlignment
+  *
+  * Sets text alignment of the #ClutterText actor.
+  *
+  * The alignment will only be used when the contents of the
+  * #ClutterText actor are enough to wrap, and the #ClutterText:line-wrap
+  * property is set to %TRUE.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_alignment (ClutterText    *self,
+                             PangoAlignment  alignment)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->alignment != alignment)
+     {
+       priv->alignment = alignment;
+       clutter_text_dirty_cache (self);
+       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "alignment");
+     }
+ }
+ /**
+  * clutter_text_get_alignment:
+  * @self: a #ClutterText
+  *
+  * Retrieves the alignment of @self.
+  *
+  * Return value: a #PangoAlignment
+  *
+  * Since 1.0
+  */
+ PangoAlignment
+ clutter_text_get_alignment (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ALIGN_LEFT);
+   return self->priv->alignment;
+ }
+ /**
+  * clutter_text_set_use_markup:
+  * @self: a #ClutterText
+  * @setting: %TRUE if the text should be parsed for markup.
+  *
+  * Sets whether the contents of the #ClutterText actor contains markup
+  * in <link linkend="PangoMarkupFormat">Pango's text markup language</link>.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_use_markup (ClutterText *self,
+                            gboolean     setting)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->use_markup != setting)
+     {
+       priv->use_markup = setting;
+       clutter_text_dirty_cache (self);
+       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "use-markup");
+     }
+ }
+ /**
+  * clutter_text_get_use_markup:
+  * @self: a #ClutterText
+  *
+  * Retrieves whether the contents of the #ClutterText actor should be
+  * parsed for the Pango text markup.
+  *
+  * Return value: %TRUE if the contents will be parsed for markup
+  *
+  * Since: 1.0
+  */
+ gboolean
+ clutter_text_get_use_markup (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
+   return self->priv->use_markup;
+ }
+ /**
+  * clutter_text_set_justify:
+  * @self: a #ClutterText
+  * @justify: whether the text should be justified
+  *
+  * Sets whether the text of the #ClutterText actor should be justified
+  * on both margins. This setting is ignored if Clutter is compiled
+  * against Pango &lt; 1.18.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_justify (ClutterText *self,
+                           gboolean     justify)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->justify != justify)
+     {
+       priv->justify = justify;
+       clutter_text_dirty_cache (self);
+       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "justify");
+     }
+ }
+ /**
+  * clutter_text_get_justify:
+  * @self: a #ClutterText
+  *
+  * Retrieves whether the #ClutterText actor should justify its contents
+  * on both margins.
+  *
+  * Return value: %TRUE if the text should be justified
+  *
+  * Since: 0.6
+  */
+ gboolean
+ clutter_text_get_justify (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
+   return self->priv->justify;
+ }
+ /**
+  * clutter_text_get_cursor_position:
+  * @self: a #ClutterText
+  *
+  * Retrieves the cursor position.
+  *
+  * Return value: the cursor position, in characters
+  *
+  * Since: 1.0
+  */
+ gint
+ clutter_text_get_cursor_position (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1);
+   return self->priv->position;
+ }
+ /**
+  * clutter_text_set_cursor_position:
+  * @self: a #ClutterText
+  * @position: the new cursor position, in characters
+  *
+  * Sets the cursor of a #ClutterText actor at @position.
+  *
+  * The position is expressed in characters, not in bytes.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_cursor_position (ClutterText *self,
+                                   gint         position)
+ {
+   ClutterTextPrivate *priv;
+   gint len;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   len = priv->n_chars;
+   if (position < 0 || position >= len)
+     priv->position = -1;
+   else
+     priv->position = position;
+   if (CLUTTER_ACTOR_IS_VISIBLE (self))
+     clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ }
+ /**
+  * clutter_text_set_cursor_size:
+  * @self: a #ClutterText
+  * @size: the size of the cursor, in pixels, or -1 to use the
+  *   default value
+  *
+  * Sets the size of the cursor of a #ClutterText. The cursor
+  * will only be visible if the #ClutterText:cursor-visible property
+  * is set to %TRUE.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_cursor_size (ClutterText *self,
+                               gint         size)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->cursor_size != size)
+     {
+       if (size < 0)
+         size = DEFAULT_CURSOR_SIZE;
+       priv->cursor_size = size;
+       if (CLUTTER_ACTOR_IS_VISIBLE (self))
+         clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "cursor-size");
+     }
+ }
+ /**
+  * clutter_text_get_cursor_size:
+  * @self: a #ClutterText
+  *
+  * Retrieves the size of the cursor of a #ClutterText actor.
+  *
+  * Return value: the size of the cursor, in pixels
+  *
+  * Since: 1.0
+  */
+ guint
+ clutter_text_get_cursor_size (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), DEFAULT_CURSOR_SIZE);
+   return self->priv->cursor_size;
+ }
+ /**
+  * clutter_text_set_password_char:
+  * @self: a #ClutterText
+  * @wc: a Unicode character, or 0 to unset the password character
+  *
+  * Sets the character to use in place of the actual text in a
+  * password text actor.
+  *
+  * If @wc is 0 the text will be displayed as it is entered in the
+  * #ClutterText actor.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_password_char (ClutterText *self,
+                                 gunichar     wc)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->password_char != wc)
+     {
+       priv->password_char = wc;
+       clutter_text_dirty_cache (self);
+       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "password-char");
+     }
+ }
+ /**
+  * clutter_text_get_password_char:
+  * @self: a #ClutterText
+  *
+  * Retrieves the character to use in place of the actual text
+  * as set by clutter_text_set_password_char().
+  *
+  * Return value: a Unicode character or 0 if the password
+  *   character is not set
+  *
+  * Since: 1.0
+  */
+ gunichar
+ clutter_text_get_password_char (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
+   return self->priv->password_char;
+ }
+ /**
+  * clutter_text_set_max_length:
+  * @self: a #ClutterText
+  * @max: the maximum number of characters allowed in the text actor; 0
+  *   to disable or -1 to set the length of the current string
+  *
+  * Sets the maximum allowed length of the contents of the actor. If the
+  * current contents are longer than the given length, then they will be
+  * truncated to fit.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_max_length (ClutterText *self,
+                              gint         max)
+ {
+   ClutterTextPrivate *priv;
+   gchar *new = NULL;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->max_length != max)
+     {
+       if (max < 0)
+         max = priv->n_chars;
+       priv->max_length = max;
+       new = g_strdup (priv->text);
+       clutter_text_set_text (self, new);
+       g_free (new);
+       g_object_notify (G_OBJECT (self), "max-length");
+     }
+ }
+ /**
+  * clutter_text_get_max_length:
+  * @self: a #ClutterText
+  *
+  * Gets the maximum length of text that can be set into a text actor.
+  *
+  * See clutter_text_set_max_length().
+  *
+  * Return value: the maximum number of characters.
+  *
+  * Since: 1.0
+  */
+ gint
+ clutter_text_get_max_length (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
+   return self->priv->max_length;
+ }
+ /**
+  * clutter_text_insert_unichar:
+  * @self: a #ClutterText
+  * @wc: a Unicode character
+  *
+  * Inserts @wc at the current cursor position of a
+  * #ClutterText actor.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_insert_unichar (ClutterText *self,
+                              gunichar     wc)
+ {
+   ClutterTextPrivate *priv;
+   GString *new = NULL;
+   glong pos;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   g_return_if_fail (g_unichar_validate (wc));
+   if (wc == 0)
+     return;
+   priv = self->priv;
+   new = g_string_new (priv->text);
+   if (priv->text)
+     pos = offset_to_bytes (priv->text, priv->position);
+   else
+     pos = 0;
+   new = g_string_insert_unichar (new, pos, wc);
+   clutter_text_set_text (self, new->str);
+   if (priv->position >= 0)
+     {
+       clutter_text_set_cursor_position (self, priv->position + 1);
+       clutter_text_set_selection_bound (self, priv->position);
+     }
+   g_string_free (new, TRUE);
+ }
+ /**
+  * clutter_text_insert_text:
+  * @self: a #ClutterText
+  * @text: the text to be inserted
+  * @position: the position of the insertion, or -1
+  *
+  * Inserts @text into a #ClutterActor at the given position.
+  *
+  * If @position is a negative number, the text will be appended
+  * at the end of the current contents of the #ClutterText.
+  *
+  * The position is expressed in characters, not in bytes.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_insert_text (ClutterText *self,
+                           const gchar *text,
+                           gssize       position)
+ {
+   ClutterTextPrivate *priv;
+   GString *new = NULL;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   g_return_if_fail (text != NULL);
+   priv = self->priv;
+   new = g_string_new (priv->text);
+   new = g_string_insert (new, position, text);
+   clutter_text_set_text (self, new->str);
+   g_string_free (new, TRUE);
+ }
+ /**
+  * clutter_text_delete_text:
+  * @self: a #ClutterText
+  * @start_pos: starting position
+  * @end_pos: ending position
+  *
+  * Deletes the text inside a #ClutterText actor between @start_pos
+  * and @end_pos.
+  *
+  * The starting and ending positions are expressed in characters,
+  * not in bytes.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_delete_text (ClutterText *self,
+                           gssize       start_pos,
+                           gssize       end_pos)
+ {
+   ClutterTextPrivate *priv;
+   GString *new = NULL;
+   gint start_bytes;
+   gint end_bytes;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (!priv->text)
+     return;
+   if (start_pos == 0)
+     start_bytes = 0;
+   else
+     start_bytes = offset_to_bytes (priv->text, start_pos);
+   if (end_pos == -1)
+     end_bytes = offset_to_bytes (priv->text, priv->n_chars);
+   else
+     end_bytes = offset_to_bytes (priv->text, end_pos);
+   new = g_string_new (priv->text);
+   new = g_string_erase (new, start_bytes, end_bytes - start_bytes);
+   clutter_text_set_text (self, new->str);
+   g_string_free (new, TRUE);
+ }
+ /**
+  * clutter_text_delete_chars:
+  * @self: a #ClutterText
+  * @n_chars: the number of characters to delete
+  *
+  * Deletes @n_chars inside a #ClutterText actor, starting from the
+  * current cursor position.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_delete_chars (ClutterText *self,
+                            guint        n_chars)
+ {
+   ClutterTextPrivate *priv;
+   GString *new = NULL;
+   gint len;
+   gint pos;
+   gint num_pos;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (!priv->text)
+     return;
+   len = priv->n_chars;
+   new = g_string_new (priv->text);
+   if (priv->position == -1)
+     {
+       num_pos = offset_to_bytes (priv->text, priv->n_chars - n_chars);
+       new = g_string_erase (new, num_pos, -1);
+     }
+   else
+     {
+       pos = offset_to_bytes (priv->text, priv->position - n_chars);
+       num_pos = offset_to_bytes (priv->text, priv->position);
+       new = g_string_erase (new, pos, num_pos - pos);
+     }
+   clutter_text_set_text (self, new->str);
+   if (priv->position > 0)
+     clutter_text_set_cursor_position (self, priv->position - n_chars);
+   g_string_free (new, TRUE);
+   g_object_notify (G_OBJECT (self), "text");
+ }
+ /**
+  * clutter_text_get_chars:
+  * @self: a #ClutterText
+  * @start_pos: start of text, in characters
+  * @end_pos: end of text, in characters
+  *
+  * Retrieves the contents of the #ClutterText actor between
+  * @start_pos and @end_pos.
+  *
+  * The positions are specified in characters, not in bytes.
+  *
+  * Return value: a newly allocated string with the contents of
+  *   the text actor between the specified positions. Use g_free()
+  *   to free the resources when done
+  *
+  * Since: 1.0
+  */
+ gchar *
+ clutter_text_get_chars (ClutterText *self,
+                         gssize       start_pos,
+                         gssize       end_pos)
+ {
+   ClutterTextPrivate *priv;
+   gint start_index, end_index;
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
+   priv = self->priv;
+   if (end_pos < 0)
+     end_pos = priv->n_chars;
+   start_pos = MIN (priv->n_chars, start_pos);
+   end_pos = MIN (priv->n_chars, end_pos);
+   start_index = g_utf8_offset_to_pointer (priv->text, start_pos)
+               - priv->text;
+   end_index   = g_utf8_offset_to_pointer (priv->text, end_pos)
+               - priv->text;
+   return g_strndup (priv->text + start_index, end_index - start_index);
+ }
+ /**
+  * clutter_text_set_single_line_mode:
+  * @self: a #ClutterText
+  * @single_line: whether to enable single line mode
+  *
+  * Sets whether a #ClutterText actor should be in single line mode
+  * or not.
+  *
+  * A text actor in single line mode will not wrap text and will clip
+  * the the visible area to the predefined size. The contents of the
+  * text actor will scroll to display the end of the text if its length
+  * is bigger than the allocated width.
+  *
+  * When setting the single line mode the #ClutterText:activatable
+  * property is also set as a side effect. Instead of entering a new
+  * line character, the text actor will emit the #ClutterText::activate
+  * signal.
+  *
+  * Since: 1.0
+  */
+ void
+ clutter_text_set_single_line_mode (ClutterText *self,
+                                    gboolean     single_line)
+ {
+   ClutterTextPrivate *priv;
+   g_return_if_fail (CLUTTER_IS_TEXT (self));
+   priv = self->priv;
+   if (priv->single_line_mode != single_line)
+     {
+       g_object_freeze_notify (G_OBJECT (self));
+       priv->single_line_mode = single_line;
+       if (priv->single_line_mode)
+         {
+           priv->activatable = TRUE;
+           g_object_notify (G_OBJECT (self), "activatable");
+         }
+       clutter_text_dirty_cache (self);
+       clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+       g_object_notify (G_OBJECT (self), "single-line-mode");
+       g_object_thaw_notify (G_OBJECT (self));
+     }
+ }
+ /**
+  * clutter_text_get_single_line_mode:
+  * @self: a #ClutterText
+  *
+  * Retrieves whether the #ClutterText actor is in single line mode.
+  *
+  * Return value: %TRUE if the #ClutterText actor is in single line mode
+  *
+  * Since: 1.0
+  */
+ gboolean
+ clutter_text_get_single_line_mode (ClutterText *self)
+ {
+   g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
+   return self->priv->single_line_mode;
+ }
Simple merge
@@@ -57,9 -57,7 +57,8 @@@
        <xi:include href="xml/clutter-rectangle.xml"/>
        <xi:include href="xml/clutter-texture.xml"/>
        <xi:include href="xml/clutter-clone-texture.xml"/>
-       <xi:include href="xml/clutter-label.xml"/>
-       <xi:include href="xml/clutter-entry.xml"/>
 +      <xi:include href="xml/clutter-cairo-texture.xml"/>
+       <xi:include href="xml/clutter-text.xml"/>
      </chapter>
  
      <chapter>
@@@ -707,7 -671,8 +671,9 @@@ clutter_path_add_rel_curve_t
  clutter_path_add_close
  clutter_path_add_string
  clutter_path_add_node
 +clutter_path_add_cairo_path
+ <SUBSECTION>
  clutter_path_get_n_nodes
  clutter_path_get_node
  clutter_path_get_nodes
@@@ -1538,21 -1460,22 +1463,36 @@@ clutter_shader_set_unifor
  clutter_shader_get_cogl_program
  clutter_shader_get_cogl_fragment_shader
  clutter_shader_get_cogl_vertex_shader
+ clutter_shader_set_uniform_1f
+ <SUBSECTION>
+ ClutterShaderFloat
+ CLUTTER_VALUE_HOLDS_SHADER_FLOAT
+ clutter_value_set_shader_float
+ clutter_value_get_shader_float
+ ClutterShaderInt
+ CLUTTER_VALUE_HOLDS_SHADER_INT
+ clutter_value_set_shader_int
+ clutter_value_get_shader_int
+ ClutterShaderMatrix
+ CLUTTER_VALUE_HOLDS_SHADER_MATRIX
+ clutter_value_set_shader_matrix
+ clutter_value_get_shader_matrix
  
 +<SUBSECTION>
 +CLUTTER_VALUE_HOLDS_SHADER_FLOAT
 +ClutterShaderFloat
 +clutter_value_set_shader_float
 +clutter_value_get_shader_float
 +CLUTTER_VALUE_HOLDS_SHADER_INT
 +ClutterShaderInt
 +clutter_value_set_shader_int
 +clutter_value_get_shader_int
 +CLUTTER_VALUE_HOLDS_SHADER_MATRIX
 +ClutterShaderMatrix
 +clutter_value_set_shader_matrix
 +clutter_value_get_shader_matrix
 +
  <SUBSECTION Standard>
  CLUTTER_IS_SHADER
  CLUTTER_IS_SHADER_CLASS
@@@ -1568,10 -1491,10 +1508,13 @@@ CLUTTER_TYPE_SHADER_MATRI
  <SUBSECTION Private>
  ClutterShaderPrivate
  clutter_shader_get_type
 +clutter_shader_float_get_type
 +clutter_shader_int_get_type
 +clutter_shader_matrix_get_type
  clutter_shader_error_quark
+ clutter_shader_float_get_type
+ clutter_shader_int_get_type
+ clutter_shader_matrix_get_type
  </SECTION>
  
  <SECTION>
@@@ -1679,30 -1603,80 +1623,108 @@@ clutter_binding_pool_activat
  </SECTION>
  
  <SECTION>
 +<TITLE>ClutterCairoTexture</TITLE>
 +<FILE>clutter-cairo-texture</FILE>
 +ClutterCairoTexture
 +ClutterCairoTextureClass
 +clutter_cairo_texture_new
 +clutter_cairo_texture_set_surface_size
 +clutter_cairo_texture_get_surface_size
 +
 +<SUBSECTION>
 +clutter_cairo_texture_create
 +clutter_cairo_texture_create_region
 +
 +<SUBSECTION>
 +clutter_cairo_set_source_color
 +
 +<SUBSECTION Standard>
 +CLUTTER_TYPE_CAIRO_TEXTURE
 +CLUTTER_CAIRO_TEXTURE
 +CLUTTER_IS_CAIRO_TEXTURE
 +CLUTTER_CAIRO_TEXTURE_CLASS
 +CLUTTER_IS_CAIRO_TEXTURE_CLASS
 +CLUTTER_CAIRO_TEXTURE_GET_CLASS
 +
 +<SUBSECTION Private>
 +ClutterCairoTexturePrivate
 +clutter_cairo_texture_get_type
++
++<SECTION>
+ <TITLE>ClutterText</TITLE>
+ <FILE>clutter-text</FILE>
+ ClutterText
+ ClutterTextClass
+ clutter_text_new
+ clutter_text_new_full
+ clutter_text_new_with_text
+ <SUBSECTION>
+ clutter_text_set_text
+ clutter_text_get_text
+ clutter_text_set_activatable
+ clutter_text_get_activatable
+ clutter_text_set_alignment
+ clutter_text_get_alignment
+ clutter_text_set_attributes
+ clutter_text_get_attributes
+ clutter_text_set_color
+ clutter_text_get_color
+ clutter_text_set_ellipsize
+ clutter_text_get_ellipsize
+ clutter_text_set_font_name
+ clutter_text_get_font_name
+ clutter_text_set_password_char
+ clutter_text_get_password_char
+ clutter_text_set_justify
+ clutter_text_get_justify
+ clutter_text_get_layout
+ clutter_text_set_line_wrap
+ clutter_text_get_line_wrap
+ clutter_text_set_line_wrap_mode
+ clutter_text_get_line_wrap_mode
+ clutter_text_set_max_length
+ clutter_text_get_max_length
+ clutter_text_set_selectable
+ clutter_text_get_selectable
+ clutter_text_set_selection
+ clutter_text_get_selection
+ clutter_text_set_selection_bound
+ clutter_text_get_selection_bound
+ clutter_text_set_single_line_mode
+ clutter_text_get_single_line_mode
+ clutter_text_set_use_markup
+ clutter_text_get_use_markup
+ <SUBSECTION>
+ clutter_text_set_editable
+ clutter_text_get_editable
+ clutter_text_insert_text
+ clutter_text_insert_unichar
+ clutter_text_delete_chars
+ clutter_text_delete_text
+ clutter_text_get_chars
+ clutter_text_set_cursor_color
+ clutter_text_get_cursor_color
+ clutter_text_set_cursor_position
+ clutter_text_get_cursor_position
+ clutter_text_set_cursor_visible
+ clutter_text_get_cursor_visible
+ clutter_text_set_cursor_size
+ clutter_text_get_cursor_size
+ <SUBSECTION>
+ clutter_text_activate
+ <SUBSECTION Standard>
+ CLUTTER_IS_TEXT
+ CLUTTER_IS_TEXT_CLASS
+ CLUTTER_TEXT
+ CLUTTER_TEXT_CLASS
+ CLUTTER_TEXT_GET_CLASS
+ CLUTTER_TYPE_TEXT
+ <SUBSECTION Private>
+ ClutterTextPrivate
+ clutter_text_get_type
  </SECTION>
@@@ -28,4 -26,4 +26,5 @@@ clutter_list_model_get_typ
  clutter_score_get_type
  clutter_shader_get_type
  clutter_child_meta_get_type
 +clutter_cairo_texture_get_type
+ clutter_text_get_type
@@@ -1,57 -1,36 +1,57 @@@
 +NULL =
 +
  noinst_PROGRAMS = test-conformance
  
 -test_conformance_SOURCES = \
 -      test-conform-main.c \
 -      test-conform-common.c \
 -      test-conform-common.h \
 +test_conformance_SOURCES =            \
 +      test-conform-main.c             \
 +      test-conform-common.c           \
 +      test-conform-common.h           \
        \
 -      test-timeline-dup-frames.c \
 -      test-timeline-interpolate.c \
 -      test-timeline-rewind.c \
 -      test-timeline-smoothness.c \
 -      test-timeline.c \
 -      test-mesh-contiguous.c \
 -      test-mesh-interleved.c \
 -      test-mesh-mutability.c \
 -      test-path.c \
 -      test-pick.c \
 -      test-clutter-rectangle.c \
 -      test-clutter-fixed.c \
 -        test-actor-invariants.c \
 -        test-paint-opacity.c \
 -      test-backface-culling.c \
 -        test-binding-pool.c \
 -        test-clutter-text.c \
 -        test-text-cache.c
 +      test-timeline-dup-frames.c      \
 +      test-timeline-interpolate.c     \
 +      test-timeline-rewind.c          \
 +      test-timeline-smoothness.c      \
 +      test-timeline.c                 \
 +      test-mesh-contiguous.c          \
 +      test-mesh-interleved.c          \
 +      test-mesh-mutability.c          \
 +      test-path.c                     \
 +      test-pick.c                     \
-       test-label-cache.c              \
-       test-clutter-entry.c            \
 +      test-clutter-rectangle.c        \
 +      test-clutter-fixed.c            \
 +      test-actor-invariants.c         \
 +      test-paint-opacity.c            \
 +      test-backface-culling.c         \
 +      test-binding-pool.c             \
-       $(NULL)
++        test-clutter-text.c             \
++        test-text-cache.c               \
++        $(NULL)
  
  # For convenience, this provides a way to easily run individual unit tests:
 -.PHONY: wrappers
 +.PHONY: wrappers clean-wrappers
 +
 +UNIT_TESTS = `./test-conformance -l -m thorough | $(GREP) '^/'`
 +
  wrappers: test-conformance$(EXEEXT)
 -      for i in `./test-conformance -l -m thorough|grep '^/'`; \
 +      @for i in $(UNIT_TESTS); \
 +      do \
 +              unit=`basename $$i | sed -e s/_/-/g`; \
 +              echo "GEN $$unit"; \
 +              ( echo "#!/bin/sh" ; echo "$(top_srcdir)/tests/conform/test-launcher.sh '$$i'" ) > $$unit$(EXEEXT) ; \
 +              chmod +x $$unit$(EXEEXT); \
 +      done
 +
 +clean-wrappers:
 +      @for i in $(UNIT_TESTS); \
        do \
 -              ln -sf $(top_srcdir)/tests/conform/wrapper.sh `basename $$i`; \
 +              unit=`basename $$i | sed -e s/_/-/g`; \
 +              echo "RM $$unit"; \
 +              rm -f $$unit$(EXEEXT) ; \
        done
 +
 +# we override the clean-generic target to clean up the wrappers
 +clean-generic: clean-wrappers
 +
  # NB: BUILT_SOURCES here a misnomer. We aren't building source, just inserting
  # a phony rule that will generate symlink scripts for running individual tests
  BUILT_SOURCES = wrappers
Simple merge
@@@ -787,7 -787,7 +787,7 @@@ test_layout_main (int argc, char *argv[
  
    clutter_container_add_actor (CLUTTER_CONTAINER (stage), box);
  
-   instructions = clutter_label_new_with_text ("Sans 14", 
 -  instructions = clutter_text_new_with_text ("Sans 14", 
++  instructions = clutter_text_new_with_text ("Sans 14",
                                                "<b>Instructions:</b>\n"
                                                "a - add a new item\n"
                                                "d - remove last item\n"
index 0000000,30231b3..8b9bd1c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,178 +1,179 @@@
 -static void cursor_event (ClutterText        *text,
 -                          ClutterGeometry *geometry)
+ /* Try this text editor, it has issues but does work,
+  * try ctrl+A, ctrl+C, ctrl+V, ctrl+X as well as selecting text with
+  * mouse and keyboard, /Øyvind K
+  */
+ #include <gmodule.h>
+ #include <clutter/clutter.h>
+ #define FONT "Mono Bold 22px"
+ static gchar *clipboard = NULL;
+ static gchar *runes =
+ "ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ\n"
+ "ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ\n"
+ "ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬\n";
+ static gboolean
+ select_all (ClutterText     *ttext,
+             const gchar  *commandline,
+             ClutterEvent *event)
+ {
+   gint len;
+   len = g_utf8_strlen (clutter_text_get_text (ttext), -1);
+   clutter_text_set_cursor_position (ttext, 0);
+   clutter_text_set_selection_bound (ttext, len);
+   return TRUE;
+ }
+ static gboolean
+ copy (ClutterText     *ttext,
+       const gchar  *commandline,
+       ClutterEvent *event)
+ {
+   if (clipboard)
+     g_free (clipboard);
+   clipboard = clutter_text_get_selection (ttext);
+   return TRUE;
+ }
+ static gboolean
+ paste (ClutterText     *ttext,
+        const gchar  *commandline,
+        ClutterEvent *event)
+ {
+   const gchar *p;
+   if (!clipboard)
+     return TRUE;
+   for (p=clipboard; *p!='\0'; p=g_utf8_next_char (p))
+     {
+       clutter_text_insert_unichar (ttext, g_utf8_get_char_validated (p, 3));
+     }
+   return TRUE;
+ }
+ static gboolean
+ cut (ClutterText     *ttext,
+      const gchar  *commandline,
+      ClutterEvent *event)
+ {
+   clutter_text_action (ttext, "copy", NULL);
+   clutter_text_action (ttext, "truncate-selection", NULL);
+   return TRUE;
+ }
+ static gboolean
+ pageup (ClutterText     *ttext,
+         const gchar  *commandline,
+         ClutterEvent *event)
+ {
+   gint i;
+   for (i=0;i<10;i++)
+     clutter_text_action (ttext, "move-up", event);
+   return TRUE;
+ }
+ static gboolean
+ pagedown (ClutterText     *ttext,
+           const gchar  *commandline,
+           ClutterEvent *event)
+ {
+   gint i;
+   for (i=0;i<10;i++)
+     clutter_text_action (ttext, "move-down", event);
+   return TRUE;
+ }
 - 
++static void
++cursor_event (ClutterText        *text,
++              ClutterGeometry *geometry)
+ {
+   gint y;
++
+   y = clutter_actor_get_y (CLUTTER_ACTOR (text));
+   if (y + geometry->y < 50)
+     {
+       clutter_actor_set_y (CLUTTER_ACTOR (text), y + 100);
+     }
+   else if (y + geometry->y > 720)
+     {
+       clutter_actor_set_y (CLUTTER_ACTOR (text), y - 100);
+     }
+ }
+ G_MODULE_EXPORT gint
+ test_text_main (gint    argc,
+                 gchar **argv)
+ {
+   ClutterActor         *stage;
+   ClutterActor         *text;
+   ClutterColor          text_color       = {0x33, 0xff, 0x33, 0xff};
+   ClutterColor          cursor_color     = {0xff, 0x33, 0x33, 0xff};
+   ClutterColor          background_color = {0x00, 0x00, 0x00, 0xff};
+   clutter_init (&argc, &argv);
+   stage = clutter_stage_get_default ();
+   clutter_stage_set_color (CLUTTER_STAGE (stage), &background_color);
+   text = clutter_text_new_full (FONT, "·", &text_color);
+   clutter_container_add (CLUTTER_CONTAINER (stage), text, NULL);
+   clutter_actor_set_position (text, 40, 30);
+   clutter_actor_set_width (text, 1024);
+   clutter_text_set_line_wrap (CLUTTER_TEXT (text), TRUE);
+   clutter_actor_set_reactive (text, TRUE);
+   clutter_stage_set_key_focus (CLUTTER_STAGE (stage), text);
+   clutter_text_set_editable (CLUTTER_TEXT (text), TRUE);
+   clutter_text_set_selectable (CLUTTER_TEXT (text), TRUE);
+   clutter_text_set_cursor_color (CLUTTER_TEXT (text), &cursor_color);
+ #if 0
+   clutter_text_add_action (CLUTTER_TEXT (text), "select-all", select_all);
+   clutter_text_add_action (CLUTTER_TEXT (text), "copy",       copy);
+   clutter_text_add_action (CLUTTER_TEXT (text), "paste",      paste);
+   clutter_text_add_action (CLUTTER_TEXT (text), "cut",        cut);
+   clutter_text_add_action (CLUTTER_TEXT (text), "pageup",     pageup);
+   clutter_text_add_action (CLUTTER_TEXT (text), "pagedown",   pagedown);
+   clutter_text_add_mapping (CLUTTER_TEXT (text),
+         CLUTTER_a, CLUTTER_CONTROL_MASK, "select-all");
+   clutter_text_add_mapping (CLUTTER_TEXT (text),
+         CLUTTER_c, CLUTTER_CONTROL_MASK, "copy");
+   clutter_text_add_mapping (CLUTTER_TEXT (text),
+         CLUTTER_v, CLUTTER_CONTROL_MASK, "paste");
+   clutter_text_add_mapping (CLUTTER_TEXT (text),
+         CLUTTER_x, CLUTTER_CONTROL_MASK, "cut");
+   clutter_text_add_mapping (CLUTTER_TEXT (text),
+         CLUTTER_Page_Up, 0, "pageup");
+   clutter_text_add_mapping (CLUTTER_TEXT (text),
+         CLUTTER_Page_Down, 0, "pagedown");
+ #endif
+   if (argv[1])
+     {
+       gchar                *utf8;
+       g_file_get_contents (argv[1], &utf8, NULL, NULL);
+       clutter_text_set_text (CLUTTER_TEXT (text), utf8);
+     }
+   else
+     {
+       clutter_text_set_text (CLUTTER_TEXT (text), runes);
+     }
+   g_signal_connect (text, "cursor-event", G_CALLBACK (cursor_event), NULL);
+   clutter_actor_set_size (stage, 1024, 768);
+   clutter_actor_show (stage);
+   clutter_main ();
+   return 0;
+ }
Simple merge