summaryrefslogtreecommitdiff
path: root/clutter/clutter/clutter-text.c
diff options
context:
space:
mode:
Diffstat (limited to 'clutter/clutter/clutter-text.c')
-rw-r--r--clutter/clutter/clutter-text.c6857
1 files changed, 0 insertions, 6857 deletions
diff --git a/clutter/clutter/clutter-text.c b/clutter/clutter/clutter-text.c
deleted file mode 100644
index 45c7eac56..000000000
--- a/clutter/clutter/clutter-text.c
+++ /dev/null
@@ -1,6857 +0,0 @@
-/*
- * 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
- */
-
-#include "clutter-build-config.h"
-
-#include <string.h>
-#include <math.h>
-
-#include "clutter-text.h"
-
-#include "clutter-actor-private.h"
-#include "clutter-animatable.h"
-#include "clutter-backend-private.h"
-#include "clutter-binding-pool.h"
-#include "clutter-color.h"
-#include "clutter-debug.h"
-#include "clutter-enum-types.h"
-#include "clutter-keysyms.h"
-#include "clutter-main.h"
-#include "clutter-marshal.h"
-#include "clutter-private.h" /* includes <cogl-pango/cogl-pango.h> */
-#include "clutter-property-transition.h"
-#include "clutter-text-buffer.h"
-#include "clutter-units.h"
-#include "clutter-paint-volume-private.h"
-#include "clutter-scriptable.h"
-#include "clutter-input-focus.h"
-
-/* cursor width in pixels */
-#define DEFAULT_CURSOR_SIZE 2
-
-/* vertical padding for the cursor */
-#define CURSOR_Y_PADDING 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
- *
- * since we might get multiple queries from layout managers doing a
- * double-pass allocations, like tabular ones, we should use 6 slots
- */
-#define N_CACHED_LAYOUTS 6
-
-typedef struct _LayoutCache LayoutCache;
-
-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;
-
- /* 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 _ClutterTextInputFocus
-{
- ClutterInputFocus parent_instance;
- ClutterText *text;
-};
-
-struct _ClutterTextPrivate
-{
- PangoFontDescription *font_desc;
-
- /* the displayed text */
- ClutterTextBuffer *buffer;
-
- gchar *font_name;
-
- gchar *preedit_str;
-
- ClutterColor text_color;
-
- LayoutCache cached_layouts[N_CACHED_LAYOUTS];
- guint cache_age;
-
- /* These are the attributes set by the attributes property */
- PangoAttrList *attrs;
- /* These are the attributes derived from the text when the
- use-markup property is set */
- PangoAttrList *markup_attrs;
- /* This is the combination of the above two lists. It is set to NULL
- whenever either of them changes and then regenerated by merging
- the two lists whenever a layout is needed */
- PangoAttrList *effective_attrs;
- /* These are the attributes for the preedit string. These are merged
- with the effective attributes into a temporary list before
- creating a layout */
- PangoAttrList *preedit_attrs;
-
- /* 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 (in both physical and logical pixels)
- * when in single line mode, to scroll the contents of the
- * text actor
- */
- gint text_x;
- gint text_logical_x;
-
- /* the y position of the PangoLayout (in both physical and logical pixels),
- * fixed to 0 by default for now */
- gint text_y;
- gint text_logical_y;
-
- /* Where to draw the cursor */
- graphene_rect_t cursor_rect;
- ClutterColor cursor_color;
- guint cursor_size;
-
- /* Box representing the paint volume. The box is lazily calculated
- and cached */
- ClutterPaintVolume paint_volume;
-
- guint preedit_cursor_pos;
- gint preedit_n_chars;
-
- ClutterColor selection_color;
-
- ClutterColor selected_text_color;
-
- gunichar password_char;
-
- guint password_hint_id;
- guint password_hint_timeout;
-
- /* Signal handler for when the backend changes its font settings */
- gulong settings_changed_id;
-
- /* Signal handler for when the :text-direction changes */
- gulong direction_changed_id;
-
- ClutterInputFocus *input_focus;
- ClutterInputContentHintFlags input_hints;
- ClutterInputContentPurpose input_purpose;
-
- /* bitfields */
- 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 selection_color_set : 1;
- guint in_select_drag : 1;
- guint in_select_touch : 1;
- guint cursor_color_set : 1;
- guint preedit_set : 1;
- guint is_default_font : 1;
- guint has_focus : 1;
- guint selected_text_color_set : 1;
- guint paint_volume_valid : 1;
- guint show_password_hint : 1;
- guint password_hint_visible : 1;
- guint resolved_direction : 4;
-};
-
-enum
-{
- PROP_0,
-
- PROP_BUFFER,
- PROP_FONT_NAME,
- PROP_FONT_DESCRIPTION,
- PROP_TEXT,
- PROP_COLOR,
- PROP_USE_MARKUP,
- PROP_ATTRIBUTES,
- PROP_LINE_ALIGNMENT,
- PROP_LINE_WRAP,
- PROP_LINE_WRAP_MODE,
- PROP_JUSTIFY,
- PROP_ELLIPSIZE,
- PROP_POSITION, /* XXX:2.0 - remove */
- PROP_SELECTION_BOUND,
- PROP_SELECTION_COLOR,
- PROP_SELECTION_COLOR_SET,
- PROP_CURSOR_VISIBLE,
- PROP_CURSOR_COLOR,
- PROP_CURSOR_COLOR_SET,
- PROP_CURSOR_SIZE,
- PROP_CURSOR_POSITION,
- PROP_EDITABLE,
- PROP_SELECTABLE,
- PROP_ACTIVATABLE,
- PROP_PASSWORD_CHAR,
- PROP_MAX_LENGTH,
- PROP_SINGLE_LINE_MODE,
- PROP_SELECTED_TEXT_COLOR,
- PROP_SELECTED_TEXT_COLOR_SET,
- PROP_INPUT_HINTS,
- PROP_INPUT_PURPOSE,
-
- PROP_LAST
-};
-
-static GParamSpec *obj_props[PROP_LAST];
-
-enum
-{
- TEXT_CHANGED,
- CURSOR_EVENT, /* XXX:2.0 - remove */
- ACTIVATE,
- INSERT_TEXT,
- DELETE_TEXT,
- CURSOR_CHANGED,
-
- LAST_SIGNAL
-};
-
-static guint text_signals[LAST_SIGNAL] = { 0, };
-
-static void clutter_text_settings_changed_cb (ClutterText *text);
-static void buffer_connect_signals (ClutterText *self);
-static void buffer_disconnect_signals (ClutterText *self);
-static ClutterTextBuffer *get_buffer (ClutterText *self);
-
-static const ClutterColor default_cursor_color = { 0, 0, 0, 255 };
-static const ClutterColor default_selection_color = { 0, 0, 0, 255 };
-static const ClutterColor default_text_color = { 0, 0, 0, 255 };
-static const ClutterColor default_selected_text_color = { 0, 0, 0, 255 };
-
-static CoglPipeline *default_color_pipeline = NULL;
-
-static ClutterAnimatableInterface *parent_animatable_iface = NULL;
-static ClutterScriptableIface *parent_scriptable_iface = NULL;
-
-/* ClutterTextInputFocus */
-#define CLUTTER_TYPE_TEXT_INPUT_FOCUS (clutter_text_input_focus_get_type ())
-
-G_DECLARE_FINAL_TYPE (ClutterTextInputFocus, clutter_text_input_focus,
- CLUTTER, TEXT_INPUT_FOCUS, ClutterInputFocus)
-G_DEFINE_TYPE (ClutterTextInputFocus, clutter_text_input_focus,
- CLUTTER_TYPE_INPUT_FOCUS)
-
-/* Utilities pango to (logical) pixels functions */
-static float
-pixels_to_pango (float px)
-{
- return ceilf (px * (float) PANGO_SCALE);
-}
-
-static float
-logical_pixels_to_pango (float px,
- float scale)
-{
- return pixels_to_pango (px * scale);
-}
-
-static float
-pango_to_pixels (float size)
-{
- return ceilf (size / (float) PANGO_SCALE);
-}
-
-static float
-pango_to_logical_pixels (float size,
- float scale)
-{
- return pango_to_pixels (size / scale);
-}
-
-static void
-clutter_text_input_focus_request_surrounding (ClutterInputFocus *focus)
-{
- ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text;
- ClutterTextBuffer *buffer;
- const gchar *text;
- gint anchor_pos, cursor_pos;
-
- buffer = clutter_text_get_buffer (clutter_text);
- text = clutter_text_buffer_get_text (buffer);
-
- cursor_pos = clutter_text_get_cursor_position (clutter_text);
- if (cursor_pos < 0)
- cursor_pos = clutter_text_buffer_get_length (buffer);
-
- anchor_pos = clutter_text_get_selection_bound (clutter_text);
- if (anchor_pos < 0)
- anchor_pos = cursor_pos;
-
- clutter_input_focus_set_surrounding (focus, text,
- g_utf8_offset_to_pointer (text, cursor_pos) - text,
- g_utf8_offset_to_pointer (text, anchor_pos) - text);
-}
-
-static void
-clutter_text_input_focus_delete_surrounding (ClutterInputFocus *focus,
- int offset,
- guint len)
-{
- ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text;
- int cursor;
- int start;
-
- cursor = clutter_text_get_cursor_position (clutter_text);
- start = cursor + offset;
- if (start < 0)
- {
- g_warning ("The offset '%d' of deleting surrounding is larger than the cursor pos '%d'",
- offset, cursor);
- return;
- }
- if (clutter_text_get_editable (clutter_text))
- clutter_text_delete_text (clutter_text, start, len);
-}
-
-static void
-clutter_text_input_focus_commit_text (ClutterInputFocus *focus,
- const gchar *text)
-{
- ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text;
-
- if (clutter_text_get_editable (clutter_text))
- {
- clutter_text_delete_selection (clutter_text);
- clutter_text_insert_text (clutter_text, text,
- clutter_text_get_cursor_position (clutter_text));
- clutter_text_set_preedit_string (clutter_text, NULL, NULL, 0);
- }
-}
-
-static void
-clutter_text_input_focus_set_preedit_text (ClutterInputFocus *focus,
- const gchar *preedit_text,
- guint cursor_pos)
-{
- ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text;
-
- if (clutter_text_get_editable (clutter_text))
- {
- PangoAttrList *list;
-
- list = pango_attr_list_new ();
- pango_attr_list_insert (list, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE));
- clutter_text_set_preedit_string (clutter_text,
- preedit_text, list,
- cursor_pos);
- pango_attr_list_unref (list);
- }
-}
-
-static void
-clutter_text_input_focus_class_init (ClutterTextInputFocusClass *klass)
-{
- ClutterInputFocusClass *focus_class = CLUTTER_INPUT_FOCUS_CLASS (klass);
-
- focus_class->request_surrounding = clutter_text_input_focus_request_surrounding;
- focus_class->delete_surrounding = clutter_text_input_focus_delete_surrounding;
- focus_class->commit_text = clutter_text_input_focus_commit_text;
- focus_class->set_preedit_text = clutter_text_input_focus_set_preedit_text;
-}
-
-static void
-clutter_text_input_focus_init (ClutterTextInputFocus *focus)
-{
-}
-
-static ClutterInputFocus *
-clutter_text_input_focus_new (ClutterText *text)
-{
- ClutterTextInputFocus *focus;
-
- focus = g_object_new (CLUTTER_TYPE_TEXT_INPUT_FOCUS, NULL);
- focus->text = text;
-
- return CLUTTER_INPUT_FOCUS (focus);
-}
-
-/* ClutterText */
-static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
-static void clutter_animatable_iface_init (ClutterAnimatableInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (ClutterText,
- clutter_text,
- CLUTTER_TYPE_ACTOR,
- G_ADD_PRIVATE (ClutterText)
- G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
- clutter_scriptable_iface_init)
- G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
- clutter_animatable_iface_init));
-
-static inline void
-clutter_text_dirty_paint_volume (ClutterText *text)
-{
- ClutterTextPrivate *priv = text->priv;
-
- if (priv->paint_volume_valid)
- {
- clutter_paint_volume_free (&priv->paint_volume);
- priv->paint_volume_valid = FALSE;
- }
-}
-
-static inline void
-clutter_text_queue_redraw (ClutterActor *self)
-{
- /* This is a wrapper for clutter_actor_queue_redraw that also
- dirties the cached paint volume. It would be nice if we could
- just override the default implementation of the queue redraw
- signal to do this instead but that doesn't work because the
- signal isn't immediately emitted when queue_redraw is called.
- Clutter will however immediately call get_paint_volume when
- queue_redraw is called so we do need to dirty it immediately. */
-
- clutter_text_dirty_paint_volume (CLUTTER_TEXT (self));
-
- clutter_actor_queue_redraw (self);
-}
-
-static gboolean
-clutter_text_should_draw_cursor (ClutterText *self)
-{
- ClutterTextPrivate *priv = self->priv;
-
- return (priv->editable || priv->selectable) &&
- priv->cursor_visible &&
- priv->has_focus;
-}
-
-#define clutter_actor_queue_redraw \
- Please_use_clutter_text_queue_redraw_instead
-
-#define offset_real(t,p) ((p) == -1 ? g_utf8_strlen ((t), -1) : (p))
-
-static gint
-offset_to_bytes (const gchar *text,
- gint pos)
-{
- const gchar *ptr;
-
- if (pos < 0)
- return strlen (text);
-
- /* Loop over each character in the string until we either reach the
- end or the requested position */
- for (ptr = text; *ptr && pos-- > 0; ptr = g_utf8_next_char (ptr));
-
- return ptr - text;
-}
-
-#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;
-
- if (priv->selection_bound != priv->position)
- {
- priv->selection_bound = priv->position;
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
- clutter_text_queue_redraw (CLUTTER_ACTOR (self));
- }
-}
-
-static gboolean
-clutter_text_is_empty (ClutterText *self)
-{
- if (self->priv->buffer == NULL)
- return TRUE;
-
- if (clutter_text_buffer_get_length (self->priv->buffer) == 0)
- return TRUE;
-
- return FALSE;
-}
-
-static gchar *
-clutter_text_get_display_text (ClutterText *self)
-{
- ClutterTextPrivate *priv = self->priv;
- ClutterTextBuffer *buffer;
- const gchar *text;
-
- /* short-circuit the case where the buffer is unset or it's empty,
- * to avoid creating a pointless TextBuffer and emitting
- * notifications with it
- */
- if (clutter_text_is_empty (self))
- return g_strdup ("");
-
- buffer = get_buffer (self);
- text = clutter_text_buffer_get_text (buffer);
-
- /* simple short-circuit to avoid going through GString
- * with an empty text and a password char set
- */
- if (text[0] == '\0')
- return g_strdup ("");
-
- if (G_LIKELY (priv->password_char == 0))
- return g_strdup (text);
- else
- {
- GString *str;
- gunichar invisible_char;
- gchar buf[7];
- gint char_len, i;
- guint n_chars;
-
- n_chars = clutter_text_buffer_get_length (buffer);
- str = g_string_sized_new (clutter_text_buffer_get_bytes (buffer));
- 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);
-
- if (priv->show_password_hint && priv->password_hint_visible)
- {
- char *last_char;
-
- for (i = 0; i < n_chars - 1; i++)
- g_string_append_len (str, buf, char_len);
-
- last_char = g_utf8_offset_to_pointer (text, n_chars - 1);
- g_string_append (str, last_char);
- }
- else
- {
- for (i = 0; i < n_chars; i++)
- g_string_append_len (str, buf, char_len);
- }
-
- return g_string_free (str, FALSE);
- }
-}
-
-static void
-ensure_effective_pango_scale_attribute (ClutterText *self)
-{
- float resource_scale;
- ClutterTextPrivate *priv = self->priv;
-
- resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (self));
-
- if (priv->effective_attrs != NULL)
- {
- PangoAttrIterator *iter;
- PangoAttribute *scale_attrib;
- PangoAttrList *old_attributes;
-
- old_attributes = priv->effective_attrs;
- priv->effective_attrs = pango_attr_list_copy (priv->effective_attrs);
- pango_attr_list_unref (old_attributes);
-
- iter = pango_attr_list_get_iterator (priv->effective_attrs);
- scale_attrib = pango_attr_iterator_get (iter, PANGO_ATTR_SCALE);
-
- if (scale_attrib != NULL)
- resource_scale *= ((PangoAttrFloat *) scale_attrib)->value;
-
- pango_attr_iterator_destroy (iter);
- }
- else
- priv->effective_attrs = pango_attr_list_new ();
-
- pango_attr_list_change (priv->effective_attrs,
- pango_attr_scale_new (resource_scale));
-}
-
-static void
-set_effective_pango_attributes (ClutterText *self,
- PangoAttrList *attributes)
-{
- ClutterTextPrivate *priv = self->priv;
-
- if (attributes != NULL)
- {
- PangoAttrList *old_attributes = priv->effective_attrs;
- priv->effective_attrs = pango_attr_list_ref (attributes);
-
- if (old_attributes != NULL)
- pango_attr_list_unref (old_attributes);
- }
- else
- {
- g_clear_pointer (&priv->effective_attrs, pango_attr_list_unref);
- }
-
- ensure_effective_pango_scale_attribute (self);
-}
-
-static inline void
-clutter_text_ensure_effective_attributes (ClutterText *self)
-{
- ClutterTextPrivate *priv = self->priv;
-
- /* If we already have the effective attributes then we don't need to
- do anything */
- if (priv->effective_attrs != NULL)
- return;
-
- /* Same as if we don't have any attribute at all.
- * We also ignore markup attributes for editable. */
- if (priv->attrs == NULL && (priv->editable || priv->markup_attrs == NULL))
- {
- set_effective_pango_attributes (self, NULL);
- return;
- }
-
- if (priv->attrs != NULL)
- {
- /* If there are no markup attributes, or if this is editable (in which
- * case we ignore markup), then we can just use these attrs directly */
- if (priv->editable || priv->markup_attrs == NULL)
- set_effective_pango_attributes (self, priv->attrs);
- else
- {
- /* Otherwise we need to merge the two lists */
- PangoAttrList *effective_attrs;
- PangoAttrIterator *iter;
- GSList *attributes, *l;
-
- effective_attrs = pango_attr_list_copy (priv->markup_attrs);
-
- iter = pango_attr_list_get_iterator (priv->attrs);
- do
- {
- attributes = pango_attr_iterator_get_attrs (iter);
-
- for (l = attributes; l != NULL; l = l->next)
- {
- PangoAttribute *attr = l->data;
-
- pango_attr_list_insert (effective_attrs, attr);
- }
-
- g_slist_free (attributes);
- }
- while (pango_attr_iterator_next (iter));
-
- pango_attr_iterator_destroy (iter);
-
- set_effective_pango_attributes (self, effective_attrs);
- pango_attr_list_unref (effective_attrs);
- }
- }
- else if (priv->markup_attrs != NULL)
- {
- set_effective_pango_attributes (self, priv->markup_attrs);
- }
-}
-
-static PangoLayout *
-clutter_text_create_layout_no_cache (ClutterText *text,
- gint width,
- gint height,
- PangoEllipsizeMode ellipsize)
-{
- ClutterTextPrivate *priv = text->priv;
- PangoLayout *layout;
- gchar *contents;
- gsize contents_len;
-
- layout = clutter_actor_create_pango_layout (CLUTTER_ACTOR (text), NULL);
- pango_layout_set_font_description (layout, priv->font_desc);
-
- contents = clutter_text_get_display_text (text);
- contents_len = strlen (contents);
-
- if (priv->editable && priv->preedit_set)
- {
- GString *tmp = g_string_new (contents);
- PangoAttrList *tmp_attrs = pango_attr_list_new ();
- gint cursor_index;
-
- if (priv->position == 0)
- cursor_index = 0;
- else
- cursor_index = offset_to_bytes (contents, priv->position);
-
- g_string_insert (tmp, cursor_index, priv->preedit_str);
-
- pango_layout_set_text (layout, tmp->str, tmp->len);
-
- if (priv->preedit_attrs != NULL)
- {
- pango_attr_list_splice (tmp_attrs, priv->preedit_attrs,
- cursor_index,
- strlen (priv->preedit_str));
-
- pango_layout_set_attributes (layout, tmp_attrs);
- }
-
- g_string_free (tmp, TRUE);
- pango_attr_list_unref (tmp_attrs);
- }
- else
- {
- PangoDirection pango_dir;
-
- if (priv->password_char != 0)
- pango_dir = PANGO_DIRECTION_NEUTRAL;
- else
- pango_dir = _clutter_pango_find_base_dir (contents, contents_len);
-
- if (pango_dir == PANGO_DIRECTION_NEUTRAL)
- {
- ClutterBackend *backend = clutter_get_default_backend ();
- ClutterTextDirection text_dir;
-
- if (clutter_actor_has_key_focus (CLUTTER_ACTOR (text)))
- {
- ClutterSeat *seat;
- ClutterKeymap *keymap;
-
- seat = clutter_backend_get_default_seat (backend);
- keymap = clutter_seat_get_keymap (seat);
- pango_dir = clutter_keymap_get_direction (keymap);
- }
- else
- {
- text_dir = clutter_actor_get_text_direction (CLUTTER_ACTOR (text));
-
- if (text_dir == CLUTTER_TEXT_DIRECTION_RTL)
- pango_dir = PANGO_DIRECTION_RTL;
- else
- pango_dir = PANGO_DIRECTION_LTR;
- }
- }
-
- pango_context_set_base_dir (clutter_actor_get_pango_context (CLUTTER_ACTOR (text)), pango_dir);
-
- priv->resolved_direction = pango_dir;
-
- pango_layout_set_text (layout, contents, contents_len);
- }
-
- /* This will merge the markup attributes and the attributes
- * property if needed */
- clutter_text_ensure_effective_attributes (text);
-
- if (priv->effective_attrs != NULL)
- 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);
- pango_layout_set_wrap (layout, priv->wrap_mode);
-
- pango_layout_set_ellipsize (layout, ellipsize);
- pango_layout_set_width (layout, width);
- pango_layout_set_height (layout, height);
-
- g_free (contents);
-
- 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_dirty_paint_volume (text);
-}
-
-/*
- * clutter_text_set_font_description_internal:
- * @self: a #ClutterText
- * @desc: a #PangoFontDescription
- *
- * Sets @desc as the font description to be used by the #ClutterText
- * actor. The #PangoFontDescription is copied.
- *
- * This function will also set the :font-name field as a side-effect
- *
- * This function will evict the layout cache, and queue a relayout if
- * the #ClutterText actor has contents.
- */
-static inline void
-clutter_text_set_font_description_internal (ClutterText *self,
- PangoFontDescription *desc,
- gboolean is_default_font)
-{
- ClutterTextPrivate *priv = self->priv;
-
- priv->is_default_font = is_default_font;
-
- if (priv->font_desc == desc ||
- pango_font_description_equal (priv->font_desc, desc))
- return;
-
- if (priv->font_desc != NULL)
- pango_font_description_free (priv->font_desc);
-
- priv->font_desc = pango_font_description_copy (desc);
-
- /* update the font name string we use */
- g_free (priv->font_name);
- priv->font_name = pango_font_description_to_string (priv->font_desc);
-
- clutter_text_dirty_cache (self);
-
- if (clutter_text_buffer_get_length (get_buffer (self)) != 0)
- clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_DESCRIPTION]);
-}
-
-static void
-clutter_text_settings_changed_cb (ClutterText *text)
-{
- ClutterTextPrivate *priv = text->priv;
- guint password_hint_time = 0;
- ClutterSettings *settings;
-
- settings = clutter_settings_get_default ();
-
- g_object_get (settings, "password-hint-time", &password_hint_time, NULL);
-
- priv->show_password_hint = password_hint_time > 0;
- priv->password_hint_timeout = password_hint_time;
-
- if (priv->is_default_font)
- {
- PangoFontDescription *font_desc;
- gchar *font_name = NULL;
-
- g_object_get (settings, "font-name", &font_name, NULL);
-
- CLUTTER_NOTE (ACTOR, "Text[%p]: default font changed to '%s'",
- text,
- font_name);
-
- font_desc = pango_font_description_from_string (font_name);
- clutter_text_set_font_description_internal (text, font_desc, TRUE);
-
- pango_font_description_free (font_desc);
- g_free (font_name);
- }
-
- clutter_text_dirty_cache (text);
- clutter_actor_queue_relayout (CLUTTER_ACTOR (text));
-}
-
-static void
-clutter_text_direction_changed_cb (GObject *gobject,
- GParamSpec *pspec)
-{
- clutter_text_dirty_cache (CLUTTER_TEXT (gobject));
-
- /* no need to queue a relayout: set_text_direction() will do that for us */
-}
-
-/*
- * clutter_text_create_layout:
- * @text: a #ClutterText
- * @allocation_width: the allocation width
- * @allocation_height: the allocation height
- *
- * 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,
- gfloat allocation_width,
- gfloat allocation_height)
-{
- ClutterTextPrivate *priv = text->priv;
- LayoutCache *oldest_cache = priv->cached_layouts;
- gboolean found_free_cache = FALSE;
- gint width = -1;
- gint height = -1;
- PangoEllipsizeMode ellipsize = PANGO_ELLIPSIZE_NONE;
- int i;
-
- /* First determine the width, height, and ellipsize mode that
- * we need for the layout. The ellipsize mode depends on
- * allocation_width/allocation_size as follows:
- *
- * Cases, assuming ellipsize != NONE on actor:
- *
- * Width request: ellipsization can be set or not on layout,
- * doesn't matter.
- *
- * Height request: ellipsization must never be set on layout
- * if wrap=true, because we need to measure the wrapped
- * height. It must always be set if wrap=false.
- *
- * Allocate: ellipsization must always be set.
- *
- * See http://bugzilla.gnome.org/show_bug.cgi?id=560931
- */
-
- if (priv->ellipsize != PANGO_ELLIPSIZE_NONE)
- {
- if (allocation_height < 0 && priv->wrap)
- ; /* must not set ellipsization on wrap=true height request */
- else
- {
- if (!priv->editable)
- ellipsize = priv->ellipsize;
- }
- }
-
- /* When painting, we always need to set the width, since
- * we might need to align to the right. When getting the
- * height, however, there are some cases where we know that
- * the width won't affect the width.
- *
- * - editable, single-line text actors, since those can
- * scroll the layout.
- * - non-wrapping, non-ellipsizing actors.
- */
- if (allocation_width >= 0 &&
- (allocation_height >= 0 ||
- !((priv->editable && priv->single_line_mode) ||
- (priv->ellipsize == PANGO_ELLIPSIZE_NONE && !priv->wrap))))
- {
- width = pixels_to_pango (allocation_width);
- }
-
- /* Pango only uses height if ellipsization is enabled, so don't set
- * height if ellipsize isn't set. Pango implicitly enables wrapping
- * if height is set, so don't set height if wrapping is disabled.
- * In other words, only set height if we want to both wrap then
- * ellipsize and we're not in single line mode.
- *
- * See http://bugzilla.gnome.org/show_bug.cgi?id=560931 if this
- * seems odd.
- */
- if (allocation_height >= 0 &&
- priv->wrap &&
- priv->ellipsize != PANGO_ELLIPSIZE_NONE &&
- !priv->single_line_mode)
- {
- height = pixels_to_pango (allocation_height);
- }
-
- /* 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;
- }
- else
- {
- PangoLayout *cached = priv->cached_layouts[i].layout;
- gint cached_width = pango_layout_get_width (cached);
- gint cached_height = pango_layout_get_height (cached);
- gint cached_ellipsize = pango_layout_get_ellipsize (cached);
-
- if (cached_width == width &&
- cached_height == height &&
- cached_ellipsize == ellipsize)
- {
- /* If this cached layout is using the same size then we can
- * just return that directly
- */
- CLUTTER_NOTE (ACTOR,
- "ClutterText: %p: cache hit for size %.2fx%.2f",
- text,
- allocation_width,
- allocation_height);
-
- return priv->cached_layouts[i].layout;
- }
-
- /* When getting the preferred height for a specific width,
- * we might be able to reuse the layout from getting the
- * preferred width. If the width that the layout gives
- * unconstrained is less than the width that we are using
- * than the height will be unaffected by that width.
- */
- if (allocation_height < 0 &&
- cached_width == -1 &&
- cached_ellipsize == ellipsize)
- {
- PangoRectangle logical_rect;
-
- pango_layout_get_extents (priv->cached_layouts[i].layout,
- NULL,
- &logical_rect);
-
- if (logical_rect.width <= width)
- {
- /* We've been asked for our height for the width we gave as a result
- * of a _get_preferred_width call
- */
- CLUTTER_NOTE (ACTOR,
- "ClutterText: %p: cache hit for size %.2fx%.2f "
- "(unwrapped width narrower than given width)",
- text,
- allocation_width,
- allocation_height);
-
- return priv->cached_layouts[i].layout;
- }
- }
-
- 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 size %.2fx%.2f",
- text,
- allocation_width,
- allocation_height);
-
- /* 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, width, height, ellipsize);
-
- 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++;
- return oldest_cache->layout;
-}
-
-static PangoLayout *
-create_text_layout_with_scale (ClutterText *text,
- gfloat allocation_width,
- gfloat allocation_height,
- gfloat scale)
-{
- if (allocation_width > 0)
- allocation_width = roundf (allocation_width * scale);
-
- if (allocation_height > 0)
- allocation_height = roundf (allocation_height * scale);
-
- return clutter_text_create_layout (text, allocation_width, allocation_height);
-}
-
-static PangoLayout *
-maybe_create_text_layout_with_resource_scale (ClutterText *text,
- gfloat allocation_width,
- gfloat allocation_height)
-{
- float resource_scale;
-
- resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (text));
-
- return create_text_layout_with_scale (text,
- allocation_width,
- allocation_height,
- resource_scale);
-}
-
-/**
- * clutter_text_coords_to_position:
- * @self: a #ClutterText
- * @x: the X coordinate, relative to the actor
- * @y: the Y coordinate, relative to the actor
- *
- * Retrieves the position of the character at the given coordinates.
- *
- * Return: the position of the character
- *
- * Since: 1.10
- */
-gint
-clutter_text_coords_to_position (ClutterText *self,
- gfloat x,
- gfloat y)
-{
- gint index_;
- gint px, py;
- gint trailing;
- gfloat resource_scale;
-
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
-
- resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (self));
-
- /* Take any offset due to scrolling into account, and normalize
- * the coordinates to PangoScale units
- */
- px = logical_pixels_to_pango (x - self->priv->text_logical_x, resource_scale);
- py = logical_pixels_to_pango (y - self->priv->text_logical_y, resource_scale);
-
- pango_layout_xy_to_index (clutter_text_get_layout (self),
- px, py,
- &index_, &trailing);
-
- return index_ + trailing;
-}
-
-static gboolean
-clutter_text_position_to_coords_internal (ClutterText *self,
- gint position,
- gfloat *x,
- gfloat *y,
- gfloat *line_height)
-{
- ClutterTextPrivate *priv;
- PangoRectangle rect;
- gint n_chars;
- gint password_char_bytes = 1;
- gint index_;
- gsize n_bytes;
-
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
-
- priv = self->priv;
-
- n_chars = clutter_text_buffer_get_length (get_buffer (self));
- if (priv->preedit_set)
- n_chars += priv->preedit_n_chars;
-
- if (position < -1 || position > n_chars)
- return FALSE;
-
- if (priv->password_char != 0)
- password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL);
-
- if (position == -1)
- {
- if (priv->password_char == 0)
- {
- n_bytes = clutter_text_buffer_get_bytes (get_buffer (self));
- if (priv->editable && priv->preedit_set)
- index_ = n_bytes + strlen (priv->preedit_str);
- else
- index_ = n_bytes;
- }
- else
- index_ = n_chars * password_char_bytes;
- }
- else if (position == 0)
- {
- index_ = 0;
- }
- else
- {
- gchar *text = clutter_text_get_display_text (self);
- GString *tmp = g_string_new (text);
- gint cursor_index;
-
- cursor_index = offset_to_bytes (text, priv->position);
-
- if (priv->preedit_str != NULL)
- g_string_insert (tmp, cursor_index, priv->preedit_str);
-
- if (priv->password_char == 0)
- index_ = offset_to_bytes (tmp->str, position);
- else
- index_ = position * password_char_bytes;
-
- g_free (text);
- g_string_free (tmp, TRUE);
- }
-
- pango_layout_get_cursor_pos (clutter_text_get_layout (self),
- index_,
- &rect, NULL);
-
- if (x)
- {
- *x = pango_to_pixels (rect.x);
-
- /* Take any offset due to scrolling into account */
- if (priv->single_line_mode)
- *x += priv->text_x;
- }
-
- if (y)
- *y = pango_to_pixels (rect.y);
-
- if (line_height)
- *line_height = pango_to_pixels (rect.height);
-
- return TRUE;
-}
-
-/**
- * clutter_text_position_to_coords:
- * @self: a #ClutterText
- * @position: position in characters
- * @x: (out): return location for the X coordinate, or %NULL
- * @y: (out): return location for the Y coordinate, or %NULL
- * @line_height: (out): return location for the line height, or %NULL
- *
- * Retrieves the coordinates of the given @position.
- *
- * Return value: %TRUE if the conversion was successful
- *
- * Since: 1.0
- */
-gboolean
-clutter_text_position_to_coords (ClutterText *self,
- gint position,
- gfloat *x,
- gfloat *y,
- gfloat *line_height)
-{
- gfloat resource_scale;
- gboolean ret;
-
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
-
- resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (self));
-
- ret = clutter_text_position_to_coords_internal (self, position,
- x, y, line_height);
-
- if (x)
- *x /= resource_scale;
-
- if (y)
- *y /= resource_scale;
-
- if (line_height)
- *line_height /= resource_scale;
-
- return ret;
-}
-
-static inline void
-update_cursor_location (ClutterText *self)
-{
- ClutterTextPrivate *priv = self->priv;
- graphene_rect_t rect;
- float x, y;
-
- if (!priv->editable)
- return;
-
- clutter_text_get_cursor_rect (self, &rect);
- clutter_actor_get_transformed_position (CLUTTER_ACTOR (self), &x, &y);
- graphene_rect_offset (&rect, x, y);
- clutter_input_focus_set_cursor_location (priv->input_focus, &rect);
-}
-
-static inline void
-clutter_text_ensure_cursor_position (ClutterText *self,
- float scale)
-{
- ClutterTextPrivate *priv = self->priv;
- gfloat x, y, cursor_height;
- graphene_rect_t cursor_rect = GRAPHENE_RECT_INIT_ZERO;
- gint position;
-
- position = priv->position;
-
- if (priv->editable && priv->preedit_set)
- {
- if (position == -1)
- position = clutter_text_buffer_get_length (get_buffer (self));
-
- position += priv->preedit_cursor_pos;
- }
-
- CLUTTER_NOTE (MISC, "Cursor at %d (preedit %s at pos: %d)",
- position,
- priv->preedit_set ? "set" : "unset",
- priv->preedit_set ? priv->preedit_cursor_pos : 0);
-
- x = y = cursor_height = 0;
- clutter_text_position_to_coords_internal (self, position,
- &x, &y,
- &cursor_height);
-
- graphene_rect_init (&cursor_rect,
- x,
- y + CURSOR_Y_PADDING * scale,
- priv->cursor_size * scale,
- cursor_height - 2 * CURSOR_Y_PADDING * scale);
-
- if (!graphene_rect_equal (&priv->cursor_rect, &cursor_rect))
- {
- priv->cursor_rect = cursor_rect;
-
- g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &cursor_rect);
- g_signal_emit (self, text_signals[CURSOR_CHANGED], 0);
-
- update_cursor_location (self);
- }
-}
-
-/**
- * clutter_text_delete_selection:
- * @self: a #ClutterText
- *
- * Deletes the currently selected text
- *
- * This function is only useful in subclasses of #ClutterText
- *
- * Return value: %TRUE if text was deleted or if the text actor
- * is empty, and %FALSE otherwise
- *
- * Since: 1.0
- */
-gboolean
-clutter_text_delete_selection (ClutterText *self)
-{
- ClutterTextPrivate *priv;
- gint start_index;
- gint end_index;
- gint old_position, old_selection;
- guint n_chars;
-
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
-
- priv = self->priv;
-
- n_chars = clutter_text_buffer_get_length (get_buffer (self));
- if (n_chars == 0)
- return TRUE;
-
- start_index = priv->position == -1 ? n_chars : priv->position;
- end_index = priv->selection_bound == -1 ? n_chars : 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;
- }
-
- old_position = priv->position;
- old_selection = priv->selection_bound;
-
- clutter_text_delete_text (self, start_index, end_index);
-
- priv->position = start_index;
- priv->selection_bound = start_index;
-
- /* Not required to be guarded by g_object_freeze/thaw_notify */
- if (priv->position != old_position)
- {
- /* XXX:2.0 - remove */
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]);
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_POSITION]);
- g_signal_emit (self, text_signals[CURSOR_CHANGED], 0);
- }
-
- if (priv->selection_bound != old_selection)
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]);
-
- return TRUE;
-}
-
-/*
- * Utility function to update both cursor position and selection bound
- * at once
- */
-static inline void
-clutter_text_set_positions (ClutterText *self,
- gint new_pos,
- gint new_bound)
-{
- g_object_freeze_notify (G_OBJECT (self));
- clutter_text_set_cursor_position (self, new_pos);
- clutter_text_set_selection_bound (self, new_bound);
- g_object_thaw_notify (G_OBJECT (self));
-}
-
-static inline void
-clutter_text_set_markup_internal (ClutterText *self,
- const gchar *str)
-{
- ClutterTextPrivate *priv = self->priv;
- GError *error;
- gchar *text = NULL;
- PangoAttrList *attrs = NULL;
- gboolean res;
-
- g_assert (str != NULL);
-
- error = NULL;
- res = pango_parse_markup (str, -1, 0,
- &attrs,
- &text,
- NULL,
- &error);
-
- if (!res)
- {
- if (G_LIKELY (error != NULL))
- {
- g_warning ("Failed to set the markup of the actor '%s': %s",
- _clutter_actor_get_debug_name (CLUTTER_ACTOR (self)),
- error->message);
- g_error_free (error);
- }
- else
- g_warning ("Failed to set the markup of the actor '%s'",
- _clutter_actor_get_debug_name (CLUTTER_ACTOR (self)));
-
- return;
- }
-
- if (text)
- {
- clutter_text_buffer_set_text (get_buffer (self), text, -1);
- g_free (text);
- }
-
- /* Store the new markup attributes */
- if (priv->markup_attrs != NULL)
- pango_attr_list_unref (priv->markup_attrs);
-
- priv->markup_attrs = attrs;
-
- /* Clear the effective attributes so they will be regenerated when a
- layout is created */
- if (priv->effective_attrs != NULL)
- {
- pango_attr_list_unref (priv->effective_attrs);
- priv->effective_attrs = NULL;
- }
-}
-
-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_BUFFER:
- clutter_text_set_buffer (self, g_value_get_object (value));
- break;
-
- case PROP_TEXT:
- {
- const char *str = g_value_get_string (value);
- if (self->priv->use_markup)
- clutter_text_set_markup_internal (self, str ? str : "");
- else
- clutter_text_buffer_set_text (get_buffer (self), str ? str : "", -1);
- }
- 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_FONT_DESCRIPTION:
- clutter_text_set_font_description (self, g_value_get_boxed (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_LINE_ALIGNMENT:
- clutter_text_set_line_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: /* XXX:2.0: remove */
- case PROP_CURSOR_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_SELECTION_COLOR:
- clutter_text_set_selection_color (self, g_value_get_boxed (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;
-
- case PROP_SELECTED_TEXT_COLOR:
- clutter_text_set_selected_text_color (self, clutter_value_get_color (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)
-{
- ClutterText *self = CLUTTER_TEXT (gobject);
- ClutterTextPrivate *priv = self->priv;
-
- switch (prop_id)
- {
- case PROP_BUFFER:
- g_value_set_object (value, clutter_text_get_buffer (self));
- break;
-
- case PROP_TEXT:
- g_value_set_string (value, clutter_text_buffer_get_text (get_buffer (self)));
- break;
-
- case PROP_FONT_NAME:
- g_value_set_string (value, priv->font_name);
- break;
-
- case PROP_FONT_DESCRIPTION:
- g_value_set_boxed (value, priv->font_desc);
- break;
-
- case PROP_USE_MARKUP:
- g_value_set_boolean (value, priv->use_markup);
- 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: /* XXX:2.0 - remove */
- case PROP_CURSOR_POSITION:
- g_value_set_int (value, priv->position);
- break;
-
- case PROP_SELECTION_BOUND:
- g_value_set_int (value, 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_SELECTION_COLOR:
- clutter_value_set_color (value, &priv->selection_color);
- break;
-
- case PROP_SELECTION_COLOR_SET:
- g_value_set_boolean (value, priv->selection_color_set);
- 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, clutter_text_buffer_get_max_length (get_buffer (self)));
- break;
-
- case PROP_SINGLE_LINE_MODE:
- g_value_set_boolean (value, priv->single_line_mode);
- break;
-
- case PROP_ELLIPSIZE:
- g_value_set_enum (value, priv->ellipsize);
- break;
-
- case PROP_LINE_WRAP:
- g_value_set_boolean (value, priv->wrap);
- break;
-
- case PROP_LINE_WRAP_MODE:
- g_value_set_enum (value, priv->wrap_mode);
- break;
-
- case PROP_LINE_ALIGNMENT:
- g_value_set_enum (value, priv->alignment);
- break;
-
- case PROP_JUSTIFY:
- g_value_set_boolean (value, priv->justify);
- break;
-
- case PROP_ATTRIBUTES:
- g_value_set_boxed (value, priv->attrs);
- break;
-
- case PROP_SELECTED_TEXT_COLOR:
- clutter_value_set_color (value, &priv->selected_text_color);
- break;
-
- case PROP_SELECTED_TEXT_COLOR_SET:
- g_value_set_boolean (value, priv->selected_text_color_set);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
- }
-}
-
-static void
-clutter_text_dispose (GObject *gobject)
-{
- ClutterText *self = CLUTTER_TEXT (gobject);
- ClutterTextPrivate *priv = self->priv;
-
- /* get rid of the entire cache */
- clutter_text_dirty_cache (self);
-
- g_clear_signal_handler (&priv->direction_changed_id, self);
- g_clear_signal_handler (&priv->settings_changed_id,
- clutter_get_default_backend ());
-
- g_clear_handle_id (&priv->password_hint_id, g_source_remove);
-
- clutter_text_set_buffer (self, NULL);
-
- 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);
-
- if (priv->attrs)
- pango_attr_list_unref (priv->attrs);
- if (priv->markup_attrs)
- pango_attr_list_unref (priv->markup_attrs);
- if (priv->effective_attrs)
- pango_attr_list_unref (priv->effective_attrs);
- if (priv->preedit_attrs)
- pango_attr_list_unref (priv->preedit_attrs);
-
- clutter_text_dirty_paint_volume (self);
-
- clutter_text_set_buffer (self, NULL);
- g_free (priv->font_name);
-
- g_clear_object (&priv->input_focus);
-
- G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject);
-}
-
-typedef void (* ClutterTextSelectionFunc) (ClutterText *text,
- const ClutterActorBox *box,
- gpointer user_data);
-
-static void
-clutter_text_foreach_selection_rectangle (ClutterText *self,
- float scale,
- ClutterTextSelectionFunc func,
- gpointer user_data)
-{
- ClutterTextPrivate *priv = self->priv;
- PangoLayout *layout = clutter_text_get_layout (self);
- gchar *utf8 = clutter_text_get_display_text (self);
- 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;
- ClutterActorBox box;
- gfloat 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_internal (self,
- bytes_to_offset (utf8, index_),
- NULL, &y, &height);
-
- box.y1 = y;
- box.y2 = y + height;
-
- for (i = 0; i < n_ranges; i++)
- {
- gfloat range_x;
- gfloat range_width;
-
- range_x = pango_to_pixels (ranges[i * 2]);
-
- /* Account for any scrolling in single line mode */
- if (priv->single_line_mode)
- range_x += priv->text_x;
-
-
- range_width = pango_to_pixels (ranges[i * 2 + 1] - ranges[i * 2]);
- box.x1 = range_x;
- box.x2 = ceilf (range_x + range_width);
-
- clutter_actor_box_scale (&box, scale);
-
- func (self, &box, user_data);
- }
-
- g_free (ranges);
- }
-
- g_free (utf8);
-}
-
-static void
-clutter_text_foreach_selection_rectangle_prescaled (ClutterText *self,
- ClutterTextSelectionFunc func,
- gpointer user_data)
-{
- clutter_text_foreach_selection_rectangle (self, 1.0f, func, user_data);
-}
-
-static void
-paint_selection_rectangle (ClutterText *self,
- const ClutterActorBox *box,
- gpointer user_data)
-{
- CoglFramebuffer *fb = user_data;
- ClutterTextPrivate *priv = self->priv;
- ClutterActor *actor = CLUTTER_ACTOR (self);
- guint8 paint_opacity = clutter_actor_get_paint_opacity (actor);
- CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline);
- PangoLayout *layout = clutter_text_get_layout (self);
- CoglColor cogl_color = { 0, };
- const ClutterColor *color;
-
- /* Paint selection background */
- if (priv->selection_color_set)
- color = &priv->selection_color;
- else if (priv->cursor_color_set)
- color = &priv->cursor_color;
- else
- color = &priv->text_color;
-
- cogl_color_init_from_4ub (&cogl_color,
- color->red,
- color->green,
- color->blue,
- paint_opacity * color->alpha / 255);
- cogl_color_premultiply (&cogl_color);
- cogl_pipeline_set_color (color_pipeline, &cogl_color);
-
- cogl_framebuffer_push_rectangle_clip (fb,
- box->x1, box->y1,
- box->x2, box->y2);
- cogl_framebuffer_draw_rectangle (fb, color_pipeline,
- box->x1, box->y1,
- box->x2, box->y2);
-
- if (priv->selected_text_color_set)
- color = &priv->selected_text_color;
- else
- color = &priv->text_color;
-
- cogl_color_init_from_4ub (&cogl_color,
- color->red,
- color->green,
- color->blue,
- paint_opacity * color->alpha / 255);
-
- cogl_pango_show_layout (fb, layout, priv->text_x, 0, &cogl_color);
-
- cogl_framebuffer_pop_clip (fb);
- cogl_object_unref (color_pipeline);
-}
-
-/* Draws the selected text, its background, and the cursor */
-static void
-selection_paint (ClutterText *self,
- CoglFramebuffer *fb)
-{
- ClutterTextPrivate *priv = self->priv;
- ClutterActor *actor = CLUTTER_ACTOR (self);
- guint8 paint_opacity = clutter_actor_get_paint_opacity (actor);
- const ClutterColor *color;
-
- if (!clutter_text_should_draw_cursor (self))
- return;
-
- if (priv->position == priv->selection_bound)
- {
- CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline);
- CoglColor cogl_color;
-
- /* No selection, just draw the cursor */
- if (priv->cursor_color_set)
- color = &priv->cursor_color;
- else
- color = &priv->text_color;
-
-
- cogl_color_init_from_4ub (&cogl_color,
- color->red,
- color->green,
- color->blue,
- paint_opacity * color->alpha / 255);
- cogl_color_premultiply (&cogl_color);
- cogl_pipeline_set_color (color_pipeline, &cogl_color);
-
- cogl_framebuffer_draw_rectangle (fb,
- color_pipeline,
- priv->cursor_rect.origin.x,
- priv->cursor_rect.origin.y,
- priv->cursor_rect.origin.x + priv->cursor_rect.size.width,
- priv->cursor_rect.origin.y + priv->cursor_rect.size.height);
- }
- else
- {
- clutter_text_foreach_selection_rectangle_prescaled (self,
- paint_selection_rectangle,
- fb);
- }
-}
-
-static gint
-clutter_text_move_word_backward (ClutterText *self,
- gint start)
-{
- gint retval = start;
-
- if (clutter_text_buffer_get_length (get_buffer (self)) > 0 && start > 0)
- {
- PangoLayout *layout = clutter_text_get_layout (self);
- PangoLogAttr *log_attrs = NULL;
- gint n_attrs = 0;
-
- pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
-
- retval = start - 1;
- while (retval > 0 && !log_attrs[retval].is_word_start)
- retval -= 1;
-
- g_free (log_attrs);
- }
-
- return retval;
-}
-
-static gint
-clutter_text_move_word_forward (ClutterText *self,
- gint start)
-{
- gint retval = start;
- guint n_chars;
-
- n_chars = clutter_text_buffer_get_length (get_buffer (self));
- if (n_chars > 0 && start < n_chars)
- {
- PangoLayout *layout = clutter_text_get_layout (self);
- PangoLogAttr *log_attrs = NULL;
- gint n_attrs = 0;
-
- pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs);
-
- retval = start + 1;
- while (retval < n_chars && !log_attrs[retval].is_word_end)
- retval += 1;
-
- g_free (log_attrs);
- }
-
- return retval;
-}
-
-static gint
-clutter_text_move_line_start (ClutterText *self,
- gint start)
-{
- PangoLayoutLine *layout_line;
- PangoLayout *layout;
- gint line_no;
- gint index_;
- gint position;
- const gchar *text;
-
- layout = clutter_text_get_layout (self);
- text = clutter_text_buffer_get_text (get_buffer (self));
-
- if (start == 0)
- index_ = 0;
- else
- index_ = offset_to_bytes (text, start);
-
- 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 (text, index_);
-
- return position;
-}
-
-static gint
-clutter_text_move_line_end (ClutterText *self,
- gint start)
-{
- ClutterTextPrivate *priv = self->priv;
- PangoLayoutLine *layout_line;
- PangoLayout *layout;
- gint line_no;
- gint index_;
- gint trailing;
- gint position;
- const gchar *text;
-
- layout = clutter_text_get_layout (self);
- text = clutter_text_buffer_get_text (get_buffer (self));
-
- if (start == 0)
- index_ = 0;
- else
- index_ = offset_to_bytes (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 (text, index_);
-
- return position;
-}
-
-static void
-clutter_text_select_word (ClutterText *self)
-{
- gint cursor_pos = self->priv->position;
- gint start_pos, end_pos;
-
- start_pos = clutter_text_move_word_backward (self, cursor_pos);
- end_pos = clutter_text_move_word_forward (self, cursor_pos);
-
- clutter_text_set_selection (self, start_pos, end_pos);
-}
-
-static void
-clutter_text_select_line (ClutterText *self)
-{
- ClutterTextPrivate *priv = self->priv;
- gint cursor_pos = priv->position;
- gint start_pos, end_pos;
-
- if (priv->single_line_mode)
- {
- start_pos = 0;
- end_pos = -1;
- }
- else
- {
- start_pos = clutter_text_move_line_start (self, cursor_pos);
- end_pos = clutter_text_move_line_end (self, cursor_pos);
- }
-
- clutter_text_set_selection (self, start_pos, end_pos);
-}
-
-static gboolean
-clutter_text_press (ClutterActor *actor,
- ClutterEvent *event)
-{
- ClutterText *self = CLUTTER_TEXT (actor);
- ClutterTextPrivate *priv = self->priv;
- ClutterEventType type = clutter_event_type (event);
- gboolean res = FALSE;
- gfloat x, y;
- gint index_;
-
- /* if a ClutterText is just used for display purposes, then we
- * should ignore the events we receive
- */
- if (!(priv->editable || priv->selectable))
- return CLUTTER_EVENT_PROPAGATE;
-
- clutter_actor_grab_key_focus (actor);
- clutter_input_focus_set_input_panel_state (priv->input_focus,
- CLUTTER_INPUT_PANEL_STATE_TOGGLE);
-
- /* if the actor is empty we just reset everything and not
- * set up the dragging of the selection since there's nothing
- * to select
- */
- if (clutter_text_buffer_get_length (get_buffer (self)) == 0)
- {
- clutter_text_set_positions (self, -1, -1);
-
- return CLUTTER_EVENT_STOP;
- }
-
- clutter_event_get_coords (event, &x, &y);
-
- res = clutter_actor_transform_stage_point (actor, x, y, &x, &y);
- if (res)
- {
- const char *text;
- int offset;
-
- index_ = clutter_text_coords_to_position (self, x, y);
- text = clutter_text_buffer_get_text (get_buffer (self));
- offset = bytes_to_offset (text, index_);
-
- /* what we select depends on the number of button clicks we
- * receive, and whether we are selectable:
- *
- * 1: just position the cursor and the selection
- * 2: select the current word
- * 3: select the contents of the whole actor
- */
- if (type == CLUTTER_BUTTON_PRESS)
- {
- gint click_count = clutter_event_get_click_count (event);
-
- if (click_count == 1)
- {
- clutter_text_set_positions (self, offset, offset);
- }
- else if (priv->selectable && click_count == 2)
- {
- clutter_text_select_word (self);
- }
- else if (priv->selectable && click_count == 3)
- {
- clutter_text_select_line (self);
- }
- }
- else
- {
- /* touch events do not have click count */
- clutter_text_set_positions (self, offset, offset);
- }
- }
-
- /* we don't need to go any further if we're not selectable */
- if (!priv->selectable)
- return CLUTTER_EVENT_STOP;
-
- /* grab the pointer */
- priv->in_select_drag = TRUE;
-
- if (type == CLUTTER_BUTTON_PRESS)
- {
- clutter_input_device_grab (clutter_event_get_device (event),
- actor);
- }
- else
- {
- clutter_input_device_sequence_grab (clutter_event_get_device (event),
- clutter_event_get_event_sequence (event),
- actor);
- priv->in_select_touch = TRUE;
- }
-
- return CLUTTER_EVENT_STOP;
-}
-
-static gboolean
-clutter_text_move (ClutterActor *actor,
- ClutterEvent *event)
-{
- ClutterText *self = CLUTTER_TEXT (actor);
- ClutterTextPrivate *priv = self->priv;
- gfloat x, y;
- gint index_, offset;
- gboolean res;
- const gchar *text;
-
- if (!priv->in_select_drag)
- return CLUTTER_EVENT_PROPAGATE;
-
- clutter_event_get_coords (event, &x, &y);
-
- res = clutter_actor_transform_stage_point (actor, x, y, &x, &y);
- if (!res)
- return CLUTTER_EVENT_PROPAGATE;
-
- index_ = clutter_text_coords_to_position (self, x, y);
- text = clutter_text_buffer_get_text (get_buffer (self));
- offset = bytes_to_offset (text, index_);
-
- if (priv->selectable)
- clutter_text_set_cursor_position (self, offset);
- else
- clutter_text_set_positions (self, offset, offset);
-
- return CLUTTER_EVENT_STOP;
-}
-
-static gboolean
-clutter_text_release (ClutterActor *actor,
- ClutterEvent *event)
-{
- ClutterText *self = CLUTTER_TEXT (actor);
- ClutterTextPrivate *priv = self->priv;
- ClutterEventType type = clutter_event_type (event);
-
- if (priv->in_select_drag)
- {
- if (type == CLUTTER_BUTTON_RELEASE)
- {
- if (!priv->in_select_touch)
- {
- clutter_input_device_ungrab (clutter_event_get_device (event));
- priv->in_select_drag = FALSE;
-
- return CLUTTER_EVENT_STOP;
- }
- }
- else
- {
- if (priv->in_select_touch)
- {
- ClutterInputDevice *device = clutter_event_get_device (event);
- ClutterEventSequence *sequence =
- clutter_event_get_event_sequence (event);
-
- clutter_input_device_sequence_ungrab (device, sequence);
- priv->in_select_touch = FALSE;
- priv->in_select_drag = FALSE;
-
- return CLUTTER_EVENT_STOP;
- }
- }
- }
-
- return CLUTTER_EVENT_PROPAGATE;
-}
-
-static gboolean
-clutter_text_button_press (ClutterActor *actor,
- ClutterButtonEvent *event)
-{
- return clutter_text_press (actor, (ClutterEvent *) event);
-}
-
-static gboolean
-clutter_text_motion (ClutterActor *actor,
- ClutterMotionEvent *event)
-{
- return clutter_text_move (actor, (ClutterEvent *) event);
-}
-
-static gboolean
-clutter_text_button_release (ClutterActor *actor,
- ClutterButtonEvent *event)
-{
- return clutter_text_release (actor, (ClutterEvent *) event);
-}
-
-static gboolean
-clutter_text_touch_event (ClutterActor *actor,
- ClutterTouchEvent *event)
-{
- switch (event->type)
- {
- case CLUTTER_TOUCH_BEGIN:
- return clutter_text_press (actor, (ClutterEvent *) event);
-
- case CLUTTER_TOUCH_END:
- case CLUTTER_TOUCH_CANCEL:
- /* TODO: the cancel case probably need a special handler */
- return clutter_text_release (actor, (ClutterEvent *) event);
-
- case CLUTTER_TOUCH_UPDATE:
- return clutter_text_move (actor, (ClutterEvent *) event);
-
- default:
- break;
- }
-
- return CLUTTER_EVENT_PROPAGATE;
-}
-
-static gboolean
-clutter_text_remove_password_hint (gpointer data)
-{
- ClutterText *self = data;
-
- self->priv->password_hint_visible = FALSE;
- self->priv->password_hint_id = 0;
-
- clutter_text_dirty_cache (data);
- clutter_text_queue_redraw (data);
-
- return G_SOURCE_REMOVE;
-}
-
-static gboolean
-clutter_text_key_press (ClutterActor *actor,
- ClutterKeyEvent *event)
-{
- ClutterText *self = CLUTTER_TEXT (actor);
- ClutterTextPrivate *priv = self->priv;
- ClutterBindingPool *pool;
- gboolean res;
-
- if (!priv->editable)
- return CLUTTER_EVENT_PROPAGATE;
-
- /* we need to use the ClutterText type name to find our own
- * key bindings; subclasses will override or chain up this
- * event handler, so they can do whatever they want there
- */
- pool = clutter_binding_pool_find (g_type_name (CLUTTER_TYPE_TEXT));
- g_assert (pool != NULL);
-
- if (!(event->flags & CLUTTER_EVENT_FLAG_INPUT_METHOD) &&
- clutter_input_focus_is_focused (priv->input_focus) &&
- clutter_input_focus_filter_event (priv->input_focus,
- (ClutterEvent *) event))
- return CLUTTER_EVENT_STOP;
-
- /* we allow passing synthetic events that only contain
- * the Unicode value and not the key symbol, unless they
- * contain the input method flag.
- */
- if (event->keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC) &&
- !(event->flags & CLUTTER_EVENT_FLAG_INPUT_METHOD))
- res = FALSE;
- else
- res = clutter_binding_pool_activate (pool, event->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 CLUTTER_EVENT_STOP;
- else if ((event->modifier_state & CLUTTER_CONTROL_MASK) == 0)
- {
- gunichar key_unichar;
-
- /* Skip keys when control is pressed */
- key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) event);
-
- /* return is reported as CR, but we want LF */
- if (key_unichar == '\r')
- key_unichar = '\n';
-
- if ((key_unichar == '\n' && !priv->single_line_mode) ||
- (g_unichar_validate (key_unichar) &&
- !g_unichar_iscntrl (key_unichar)))
- {
- /* truncate the eventual selection so that the
- * Unicode character can replace it
- */
- clutter_text_delete_selection (self);
- clutter_text_insert_unichar (self, key_unichar);
-
- if (priv->show_password_hint)
- {
- g_clear_handle_id (&priv->password_hint_id, g_source_remove);
-
- priv->password_hint_visible = TRUE;
- priv->password_hint_id =
- clutter_threads_add_timeout (priv->password_hint_timeout,
- clutter_text_remove_password_hint,
- self);
- }
-
- return CLUTTER_EVENT_STOP;
- }
- }
-
- return CLUTTER_EVENT_PROPAGATE;
-}
-
-static gboolean
-clutter_text_key_release (ClutterActor *actor,
- ClutterKeyEvent *event)
-{
- ClutterText *self = CLUTTER_TEXT (actor);
- ClutterTextPrivate *priv = self->priv;
-
- if (clutter_input_focus_is_focused (priv->input_focus) &&
- clutter_input_focus_filter_event (priv->input_focus,
- (ClutterEvent *) event))
- return CLUTTER_EVENT_STOP;
-
- return CLUTTER_EVENT_PROPAGATE;
-}
-
-static void
-clutter_text_compute_layout_offsets (ClutterText *self,
- PangoLayout *layout,
- const ClutterActorBox *alloc,
- int *text_x,
- int *text_y)
-{
- ClutterActor *actor = CLUTTER_ACTOR (self);
- ClutterActorAlign x_align, y_align;
- PangoRectangle logical_rect;
- float alloc_width, alloc_height;
- float x, y;
-
- clutter_actor_box_get_size (alloc, &alloc_width, &alloc_height);
- pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
-
- if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_HORIZONTAL))
- x_align = _clutter_actor_get_effective_x_align (actor);
- else
- x_align = CLUTTER_ACTOR_ALIGN_FILL;
-
- if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_VERTICAL))
- y_align = clutter_actor_get_y_align (actor);
- else
- y_align = CLUTTER_ACTOR_ALIGN_FILL;
-
- x = 0.f;
- switch (x_align)
- {
- case CLUTTER_ACTOR_ALIGN_FILL:
- case CLUTTER_ACTOR_ALIGN_START:
- break;
-
- case CLUTTER_ACTOR_ALIGN_END:
- if (alloc_width > logical_rect.width)
- x = alloc_width - logical_rect.width;
- break;
-
- case CLUTTER_ACTOR_ALIGN_CENTER:
- if (alloc_width > logical_rect.width)
- x = (alloc_width - logical_rect.width) / 2.f;
- break;
- }
-
- y = 0.f;
- switch (y_align)
- {
- case CLUTTER_ACTOR_ALIGN_FILL:
- case CLUTTER_ACTOR_ALIGN_START:
- break;
-
- case CLUTTER_ACTOR_ALIGN_END:
- if (alloc_height > logical_rect.height)
- y = alloc_height - logical_rect.height;
- break;
-
- case CLUTTER_ACTOR_ALIGN_CENTER:
- if (alloc_height > logical_rect.height)
- y = (alloc_height - logical_rect.height) / 2.f;
- break;
- }
-
- if (text_x != NULL)
- *text_x = floorf (x);
-
- if (text_y != NULL)
- *text_y = floorf (y);
-}
-
-#define TEXT_PADDING 2
-
-static void
-clutter_text_paint (ClutterActor *self,
- ClutterPaintContext *paint_context)
-{
- ClutterText *text = CLUTTER_TEXT (self);
- ClutterTextPrivate *priv = text->priv;
- CoglFramebuffer *fb;
- PangoLayout *layout;
- ClutterActorBox alloc = { 0, };
- CoglColor color = { 0, };
- guint8 real_opacity;
- gint text_x = priv->text_x;
- gint text_y = priv->text_y;
- gboolean clip_set = FALSE;
- gboolean bg_color_set = FALSE;
- guint n_chars;
- float alloc_width;
- float alloc_height;
- float resource_scale;
-
- fb = clutter_paint_context_get_framebuffer (paint_context);
-
- /* Note that if anything in this paint method changes it needs to be
- reflected in the get_paint_volume implementation which is tightly
- tied to the workings of this function */
- n_chars = clutter_text_buffer_get_length (get_buffer (text));
-
- clutter_actor_get_allocation_box (self, &alloc);
-
- if (G_UNLIKELY (default_color_pipeline == NULL))
- {
- CoglContext *ctx =
- clutter_backend_get_cogl_context (clutter_get_default_backend ());
- default_color_pipeline = cogl_pipeline_new (ctx);
- }
-
- g_assert (default_color_pipeline != NULL);
-
- g_object_get (self, "background-color-set", &bg_color_set, NULL);
- if (bg_color_set)
- {
- CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline);
- ClutterColor bg_color;
-
- clutter_actor_get_background_color (self, &bg_color);
- bg_color.alpha = clutter_actor_get_paint_opacity (self)
- * bg_color.alpha
- / 255;
-
- cogl_color_init_from_4ub (&color,
- bg_color.red,
- bg_color.green,
- bg_color.blue,
- bg_color.alpha);
- cogl_color_premultiply (&color);
- cogl_pipeline_set_color (color_pipeline, &color);
-
- cogl_framebuffer_draw_rectangle (fb,
- color_pipeline,
- 0, 0,
- clutter_actor_box_get_width (&alloc),
- clutter_actor_box_get_height (&alloc));
-
- cogl_object_unref (color_pipeline);
- }
-
- /* don't bother painting an empty text actor, unless it's
- * editable, in which case we want to paint at least the
- * cursor
- */
- if (n_chars == 0 &&
- !clutter_text_should_draw_cursor (text))
- return;
-
- resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (self));
-
- clutter_actor_box_scale (&alloc, resource_scale);
- clutter_actor_box_get_size (&alloc, &alloc_width, &alloc_height);
-
- if (priv->editable && priv->single_line_mode)
- layout = clutter_text_create_layout (text, -1, -1);
- else
- {
- /* the only time when we create the PangoLayout using the full
- * width and height of the allocation is when we can both wrap
- * and ellipsize
- */
- if (priv->wrap && priv->ellipsize)
- {
- layout = clutter_text_create_layout (text, alloc_width, alloc_height);
- }
- else
- {
- /* if we're not wrapping we cannot set the height of the
- * layout, otherwise Pango will happily wrap the text to
- * fit in the rectangle - thus making the :wrap property
- * useless
- *
- * see bug:
- *
- * http://bugzilla.clutter-project.org/show_bug.cgi?id=2339
- *
- * in order to fix this, we create a layout that would fit
- * in the assigned width, then we clip the actor if the
- * logical rectangle overflows the allocation.
- */
- layout = clutter_text_create_layout (text, alloc_width, -1);
- }
- }
-
- if (resource_scale != 1.0f)
- {
- float paint_scale = 1.0f / resource_scale;
- cogl_framebuffer_push_matrix (fb);
- cogl_framebuffer_scale (fb, paint_scale, paint_scale, 1.0f);
- }
-
- if (clutter_text_should_draw_cursor (text))
- clutter_text_ensure_cursor_position (text, resource_scale);
-
- if (priv->editable && priv->single_line_mode)
- {
- PangoRectangle logical_rect = { 0, };
- gint actor_width, text_width;
- gboolean rtl;
-
- pango_layout_get_extents (layout, NULL, &logical_rect);
-
- cogl_framebuffer_push_rectangle_clip (fb, 0, 0, alloc_width, alloc_height);
- clip_set = TRUE;
-
- actor_width = alloc_width - 2 * TEXT_PADDING;
- text_width = pango_to_pixels (logical_rect.width);
-
- rtl = priv->resolved_direction == PANGO_DIRECTION_RTL;
-
- if (actor_width < text_width)
- {
- gint cursor_x = graphene_rect_get_x (&priv->cursor_rect);
-
- if (priv->position == -1)
- {
- text_x = rtl ? TEXT_PADDING : actor_width - text_width;
- }
- else if (priv->position == 0)
- {
- text_x = rtl ? actor_width - text_width : TEXT_PADDING;
- }
- else
- {
- if (cursor_x < 0)
- {
- text_x = text_x - cursor_x - TEXT_PADDING;
- }
- else if (cursor_x > actor_width)
- {
- text_x = text_x + (actor_width - cursor_x) - TEXT_PADDING;
- }
- }
- }
- else
- {
- text_x = rtl ? actor_width - text_width : TEXT_PADDING;
- }
- }
- else if (!priv->editable && !(priv->wrap && priv->ellipsize))
- {
- PangoRectangle logical_rect = { 0, };
-
- pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
-
- /* don't clip if the layout managed to fit inside our allocation */
- if (logical_rect.width > alloc_width ||
- logical_rect.height > alloc_height)
- {
- cogl_framebuffer_push_rectangle_clip (fb, 0, 0, alloc_width, alloc_height);
- clip_set = TRUE;
- }
-
- clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y);
- }
- else
- clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y);
-
- if (priv->text_x != text_x ||
- priv->text_y != text_y)
- {
- priv->text_x = text_x;
- priv->text_y = text_y;
- priv->text_logical_x = roundf ((float) text_x / resource_scale);
- priv->text_logical_y = roundf ((float) text_y / resource_scale);
-
- clutter_text_ensure_cursor_position (text, resource_scale);
- }
-
- real_opacity = clutter_actor_get_paint_opacity (self)
- * priv->text_color.alpha
- / 255;
-
- CLUTTER_NOTE (PAINT, "painting text (text: '%s')",
- clutter_text_buffer_get_text (get_buffer (text)));
-
- cogl_color_init_from_4ub (&color,
- priv->text_color.red,
- priv->text_color.green,
- priv->text_color.blue,
- real_opacity);
- cogl_pango_show_layout (fb, layout, priv->text_x, priv->text_y, &color);
-
- selection_paint (text, fb);
-
- if (resource_scale != 1.0f)
- cogl_framebuffer_pop_matrix (fb);
-
- if (clip_set)
- cogl_framebuffer_pop_clip (fb);
-}
-
-static void
-add_selection_to_paint_volume (ClutterText *text,
- const ClutterActorBox *box,
- gpointer user_data)
-{
- ClutterPaintVolume *total_volume = user_data;
- ClutterPaintVolume rect_volume;
- graphene_point3d_t vertex;
-
- _clutter_paint_volume_init_static (&rect_volume, CLUTTER_ACTOR (text));
-
- vertex.x = box->x1;
- vertex.y = box->y1;
- vertex.z = 0.0f;
- clutter_paint_volume_set_origin (&rect_volume, &vertex);
- clutter_paint_volume_set_width (&rect_volume, box->x2 - box->x1);
- clutter_paint_volume_set_height (&rect_volume, box->y2 - box->y1);
-
- clutter_paint_volume_union (total_volume, &rect_volume);
-
- clutter_paint_volume_free (&rect_volume);
-}
-
-static void
-clutter_text_get_paint_volume_for_cursor (ClutterText *text,
- float resource_scale,
- ClutterPaintVolume *volume)
-{
- ClutterTextPrivate *priv = text->priv;
- graphene_point3d_t origin;
-
- clutter_text_ensure_cursor_position (text, resource_scale);
-
- if (priv->position == priv->selection_bound)
- {
- float width, height;
-
- width = priv->cursor_rect.size.width / resource_scale;
- height = priv->cursor_rect.size.height / resource_scale;
- origin.x = priv->cursor_rect.origin.x / resource_scale;
- origin.y = priv->cursor_rect.origin.y / resource_scale;
- origin.z = 0;
-
- clutter_paint_volume_set_origin (volume, &origin);
- clutter_paint_volume_set_width (volume, width);
- clutter_paint_volume_set_height (volume, height);
- }
- else
- {
- clutter_text_foreach_selection_rectangle (text,
- 1.0f / resource_scale,
- add_selection_to_paint_volume,
- volume);
- }
-}
-
-static gboolean
-clutter_text_get_paint_volume (ClutterActor *self,
- ClutterPaintVolume *volume)
-{
- ClutterText *text = CLUTTER_TEXT (self);
- ClutterTextPrivate *priv = text->priv;
-
- /* ClutterText uses the logical layout as the natural size of the
- actor. This means that it can sometimes paint outside of its
- allocation for example with italic fonts with serifs. Therefore
- we should use the ink rectangle of the layout instead */
-
- if (!priv->paint_volume_valid)
- {
- PangoLayout *layout;
- PangoRectangle ink_rect;
- graphene_point3d_t origin;
- float resource_scale;
-
- /* If the text is single line editable then it gets clipped to
- the allocation anyway so we can just use that */
- if (priv->editable && priv->single_line_mode)
- return _clutter_actor_set_default_paint_volume (self,
- CLUTTER_TYPE_TEXT,
- volume);
-
- if (G_OBJECT_TYPE (self) != CLUTTER_TYPE_TEXT)
- return FALSE;
-
- if (!clutter_actor_has_allocation (self))
- return FALSE;
-
- resource_scale = clutter_actor_get_resource_scale (self);
-
- _clutter_paint_volume_init_static (&priv->paint_volume, self);
-
- layout = clutter_text_get_layout (text);
- pango_layout_get_extents (layout, &ink_rect, NULL);
-
- origin.x = pango_to_logical_pixels (ink_rect.x, resource_scale);
- origin.y = pango_to_logical_pixels (ink_rect.y, resource_scale);
- origin.z = 0;
- clutter_paint_volume_set_origin (&priv->paint_volume, &origin);
- clutter_paint_volume_set_width (&priv->paint_volume,
- pango_to_logical_pixels (ink_rect.width,
- resource_scale));
- clutter_paint_volume_set_height (&priv->paint_volume,
- pango_to_logical_pixels (ink_rect.height,
- resource_scale));
-
- /* If the cursor is visible then that will likely be drawn
- outside of the ink rectangle so we should merge that in */
- if (clutter_text_should_draw_cursor (text))
- {
- ClutterPaintVolume cursor_paint_volume;
-
- _clutter_paint_volume_init_static (&cursor_paint_volume, self);
-
- clutter_text_get_paint_volume_for_cursor (text, resource_scale,
- &cursor_paint_volume);
-
- clutter_paint_volume_union (&priv->paint_volume,
- &cursor_paint_volume);
-
- clutter_paint_volume_free (&cursor_paint_volume);
- }
-
- priv->paint_volume_valid = TRUE;
- }
-
- _clutter_paint_volume_copy_static (&priv->paint_volume, volume);
-
- return TRUE;
-}
-
-static void
-clutter_text_get_preferred_width (ClutterActor *self,
- gfloat for_height,
- gfloat *min_width_p,
- gfloat *natural_width_p)
-{
- ClutterText *text = CLUTTER_TEXT (self);
- ClutterTextPrivate *priv = text->priv;
- PangoRectangle logical_rect = { 0, };
- PangoLayout *layout;
- gint logical_width;
- gfloat layout_width;
- gfloat resource_scale;
-
- resource_scale = clutter_actor_get_resource_scale (self);
-
- layout = clutter_text_create_layout (text, -1, -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
- ? pango_to_logical_pixels (logical_width, resource_scale)
- : 1;
-
- if (min_width_p)
- {
- if (priv->wrap || priv->ellipsize || priv->editable)
- *min_width_p = 1;
- else
- *min_width_p = layout_width;
- }
-
- if (natural_width_p)
- {
- if (priv->editable && priv->single_line_mode)
- *natural_width_p = layout_width + TEXT_PADDING * 2;
- else
- *natural_width_p = layout_width;
- }
-}
-
-static void
-clutter_text_get_preferred_height (ClutterActor *self,
- gfloat for_width,
- gfloat *min_height_p,
- gfloat *natural_height_p)
-{
- ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
-
- 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;
- gfloat layout_height;
- gfloat resource_scale;
-
- resource_scale = clutter_actor_get_resource_scale (self);
-
- if (priv->single_line_mode)
- for_width = -1;
-
- layout = create_text_layout_with_scale (CLUTTER_TEXT (self),
- for_width, -1, resource_scale);
-
- 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 = pango_to_logical_pixels (logical_height, resource_scale);
-
- if (min_height_p)
- {
- /* if we wrap and ellipsize then the minimum height is
- * going to be at least the size of the first line
- */
- if ((priv->ellipsize && priv->wrap) && !priv->single_line_mode)
- {
- PangoLayoutLine *line;
- gfloat line_height;
-
- line = pango_layout_get_line_readonly (layout, 0);
- pango_layout_line_get_extents (line, NULL, &logical_rect);
-
- logical_height = logical_rect.y + logical_rect.height;
- line_height = pango_to_logical_pixels (logical_height,
- resource_scale);
-
- *min_height_p = line_height;
- }
- else
- *min_height_p = layout_height;
- }
-
- if (natural_height_p)
- *natural_height_p = layout_height;
- }
-}
-
-static void
-clutter_text_allocate (ClutterActor *self,
- const ClutterActorBox *box)
-{
- 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
- *
- * if the Text is editable and in single line mode we don't want
- * to have any limit on the layout size, since the paint will clip
- * it to the allocation of the actor
- */
- if (text->priv->editable && text->priv->single_line_mode)
- clutter_text_create_layout (text, -1, -1);
- else
- maybe_create_text_layout_with_resource_scale (text,
- box->x2 - box->x1,
- box->y2 - box->y1);
-
- parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class);
- parent_class->allocate (self, box);
-}
-
-static gboolean
-clutter_text_has_overlaps (ClutterActor *self)
-{
- return clutter_text_should_draw_cursor ((ClutterText *) self);
-}
-
-static float
-clutter_text_calculate_resource_scale (ClutterActor *actor,
- int phase)
-{
- ClutterActorClass *parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class);
- float new_resource_scale;
-
- new_resource_scale = parent_class->calculate_resource_scale (actor, phase);
-
- if (phase == 1)
- return MAX (new_resource_scale, clutter_actor_get_real_resource_scale (actor));
-
- return new_resource_scale;
-}
-
-static void
-clutter_text_resource_scale_changed (ClutterActor *actor)
-{
- ClutterText *text = CLUTTER_TEXT (actor);
- ClutterTextPrivate *priv = text->priv;
-
- g_clear_pointer (&priv->effective_attrs, pango_attr_list_unref);
- clutter_text_dirty_cache (text);
-
- clutter_actor_queue_immediate_relayout (actor);
-}
-
-static gboolean
-clutter_text_event (ClutterActor *self,
- ClutterEvent *event)
-{
- ClutterText *text = CLUTTER_TEXT (self);
- ClutterTextPrivate *priv = text->priv;
-
- if (clutter_input_focus_is_focused (priv->input_focus) &&
- (event->type == CLUTTER_IM_COMMIT ||
- event->type == CLUTTER_IM_DELETE ||
- event->type == CLUTTER_IM_PREEDIT))
- {
- return clutter_input_focus_filter_event (priv->input_focus, event);
- }
-
- return CLUTTER_EVENT_PROPAGATE;
-}
-
-static void
-clutter_text_im_focus (ClutterText *text)
-{
- ClutterTextPrivate *priv = text->priv;
- ClutterBackend *backend = clutter_get_default_backend ();
- ClutterInputMethod *method = clutter_backend_get_input_method (backend);
-
- if (!method)
- return;
-
- clutter_input_method_focus_in (method, priv->input_focus);
- clutter_input_focus_set_content_purpose (priv->input_focus,
- priv->input_purpose);
- clutter_input_focus_set_content_hints (priv->input_focus,
- priv->input_hints);
- clutter_input_focus_set_can_show_preedit (priv->input_focus, TRUE);
- update_cursor_location (text);
-}
-
-static void
-clutter_text_key_focus_in (ClutterActor *actor)
-{
- ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
-
- if (priv->editable)
- clutter_text_im_focus (CLUTTER_TEXT (actor));
-
- priv->has_focus = TRUE;
-
- clutter_text_queue_redraw (actor);
-}
-
-static void
-clutter_text_key_focus_out (ClutterActor *actor)
-{
- ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv;
- ClutterBackend *backend = clutter_get_default_backend ();
- ClutterInputMethod *method = clutter_backend_get_input_method (backend);
-
- priv->has_focus = FALSE;
-
- if (priv->editable && clutter_input_focus_is_focused (priv->input_focus))
- {
- clutter_text_set_preedit_string (CLUTTER_TEXT (actor), NULL, NULL, 0);
- clutter_input_method_focus_out (method);
- }
-
- clutter_text_queue_redraw (actor);
-}
-
-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 new_pos = 0;
- gint len;
-
- len = clutter_text_buffer_get_length (get_buffer (self));
-
- g_object_freeze_notify (G_OBJECT (self));
-
- if (pos != 0 && len != 0)
- {
- if (modifiers & CLUTTER_CONTROL_MASK)
- {
- if (pos == -1)
- new_pos = clutter_text_move_word_backward (self, len);
- else
- new_pos = clutter_text_move_word_backward (self, pos);
- }
- else
- {
- if (pos == -1)
- new_pos = len - 1;
- else
- new_pos = pos - 1;
- }
-
- clutter_text_set_cursor_position (self, new_pos);
- }
-
- if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
- clutter_text_clear_selection (self);
-
- g_object_thaw_notify (G_OBJECT (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 = clutter_text_buffer_get_length (get_buffer (self));
- gint new_pos = 0;
-
- g_object_freeze_notify (G_OBJECT (self));
-
- if (pos != -1 && len !=0)
- {
- if (modifiers & CLUTTER_CONTROL_MASK)
- {
- if (pos != len)
- new_pos = clutter_text_move_word_forward (self, pos);
- }
- else
- {
- if (pos != len)
- new_pos = pos + 1;
- }
-
- clutter_text_set_cursor_position (self, new_pos);
- }
-
- if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
- clutter_text_clear_selection (self);
-
- g_object_thaw_notify (G_OBJECT (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_, trailing;
- gint pos;
- gint x;
- const gchar *text;
-
- layout = clutter_text_get_layout (self);
- text = clutter_text_buffer_get_text (get_buffer (self));
-
- if (priv->position == 0)
- index_ = 0;
- else
- index_ = offset_to_bytes (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;
-
- 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_, &trailing);
-
- g_object_freeze_notify (G_OBJECT (self));
-
- pos = bytes_to_offset (text, index_);
- clutter_text_set_cursor_position (self, pos + trailing);
-
- /* Store the target x position to avoid drifting left and right when
- moving the cursor up and down */
- priv->x_pos = x;
-
- if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
- clutter_text_clear_selection (self);
-
- g_object_thaw_notify (G_OBJECT (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_, trailing;
- gint x;
- gint pos;
- const gchar *text;
-
- layout = clutter_text_get_layout (self);
- text = clutter_text_buffer_get_text (get_buffer (self));
-
- if (priv->position == 0)
- index_ = 0;
- else
- index_ = offset_to_bytes (text, priv->position);
-
- pango_layout_index_to_line_x (layout, index_,
- 0,
- &line_no, &x);
-
- if (priv->x_pos != -1)
- x = priv->x_pos;
-
- 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_, &trailing);
-
- g_object_freeze_notify (G_OBJECT (self));
-
- pos = bytes_to_offset (text, index_);
- clutter_text_set_cursor_position (self, pos + trailing);
-
- /* Store the target x position to avoid drifting left and right when
- moving the cursor up and down */
- priv->x_pos = x;
-
- if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
- clutter_text_clear_selection (self);
-
- g_object_thaw_notify (G_OBJECT (self));
-
- return TRUE;
-}
-
-static gboolean
-clutter_text_real_line_start (ClutterText *self,
- const gchar *action,
- guint keyval,
- ClutterModifierType modifiers)
-{
- ClutterTextPrivate *priv = self->priv;
- gint position;
-
- g_object_freeze_notify (G_OBJECT (self));
-
- position = clutter_text_move_line_start (self, priv->position);
- clutter_text_set_cursor_position (self, position);
-
- if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
- clutter_text_clear_selection (self);
-
- g_object_thaw_notify (G_OBJECT (self));
-
- return TRUE;
-}
-
-static gboolean
-clutter_text_real_line_end (ClutterText *self,
- const gchar *action,
- guint keyval,
- ClutterModifierType modifiers)
-{
- ClutterTextPrivate *priv = self->priv;
- gint position;
-
- g_object_freeze_notify (G_OBJECT (self));
-
- position = clutter_text_move_line_end (self, priv->position);
- clutter_text_set_cursor_position (self, position);
-
- if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK)))
- clutter_text_clear_selection (self);
-
- g_object_thaw_notify (G_OBJECT (self));
-
- return TRUE;
-}
-
-static gboolean
-clutter_text_real_select_all (ClutterText *self,
- const gchar *action,
- guint keyval,
- ClutterModifierType modifiers)
-{
- guint n_chars = clutter_text_buffer_get_length (get_buffer (self));
- clutter_text_set_positions (self, 0, 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_delete_selection (self))
- return TRUE;
-
- pos = priv->position;
- len = clutter_text_buffer_get_length (get_buffer (self));
-
- if (len && pos != -1 && pos < len)
- clutter_text_delete_text (self, pos, pos + 1);
-
- return TRUE;
-}
-
-static gboolean
-clutter_text_real_del_word_next (ClutterText *self,
- const gchar *action,
- guint keyval,
- ClutterModifierType modifiers)
-{
- ClutterTextPrivate *priv = self->priv;
- gint pos;
- gint len;
-
- pos = priv->position;
- len = clutter_text_buffer_get_length (get_buffer (self));
-
- if (len && pos != -1 && pos < len)
- {
- gint end;
-
- end = clutter_text_move_word_forward (self, pos);
- clutter_text_delete_text (self, pos, end);
-
- if (priv->selection_bound >= end)
- {
- gint new_bound;
-
- new_bound = priv->selection_bound - (end - pos);
- clutter_text_set_selection_bound (self, new_bound);
- }
- else if (priv->selection_bound > pos)
- {
- clutter_text_set_selection_bound (self, pos);
- }
- }
-
- 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_delete_selection (self))
- return TRUE;
-
- pos = priv->position;
- len = clutter_text_buffer_get_length (get_buffer (self));
-
- if (pos != 0 && len != 0)
- {
- if (pos == -1)
- {
- clutter_text_delete_text (self, len - 1, len);
-
- clutter_text_set_positions (self, -1, -1);
- }
- else
- {
- clutter_text_delete_text (self, pos - 1, pos);
-
- clutter_text_set_positions (self, pos - 1, pos - 1);
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-clutter_text_real_del_word_prev (ClutterText *self,
- const gchar *action,
- guint keyval,
- ClutterModifierType modifiers)
-{
- ClutterTextPrivate *priv = self->priv;
- gint pos;
- gint len;
-
- pos = priv->position;
- len = clutter_text_buffer_get_length (get_buffer (self));
-
- if (pos != 0 && len != 0)
- {
- gint new_pos;
-
- if (pos == -1)
- {
- new_pos = clutter_text_move_word_backward (self, len);
- clutter_text_delete_text (self, new_pos, len);
-
- clutter_text_set_positions (self, -1, -1);
- }
- else
- {
- new_pos = clutter_text_move_word_backward (self, pos);
- clutter_text_delete_text (self, new_pos, pos);
-
- clutter_text_set_cursor_position (self, new_pos);
- if (priv->selection_bound >= pos)
- {
- gint new_bound;
-
- new_bound = priv->selection_bound - (pos - new_pos);
- clutter_text_set_selection_bound (self, new_bound);
- }
- else if (priv->selection_bound >= new_pos)
- {
- clutter_text_set_selection_bound (self, new_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,
- ClutterModifierType additional_modifiers,
- 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);
-
- if (additional_modifiers != 0)
- {
- clutter_binding_pool_install_action (pool, action,
- key_val,
- additional_modifiers,
- callback,
- NULL, NULL);
- clutter_binding_pool_install_action (pool, action,
- key_val,
- CLUTTER_SHIFT_MASK |
- additional_modifiers,
- callback,
- NULL, NULL);
- }
-}
-
-static gboolean
-clutter_text_parse_custom_node (ClutterScriptable *scriptable,
- ClutterScript *script,
- GValue *value,
- const gchar *name,
- JsonNode *node)
-{
- if (strncmp (name, "font-description", 16) == 0)
- {
- g_value_init (value, G_TYPE_STRING);
- g_value_set_string (value, json_node_get_string (node));
-
- return TRUE;
- }
-
- return parent_scriptable_iface->parse_custom_node (scriptable, script,
- value,
- name,
- node);
-}
-
-static void
-clutter_text_set_color_internal (ClutterText *self,
- GParamSpec *pspec,
- const ClutterColor *color)
-{
- ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv;
- GParamSpec *other = NULL;
-
- switch (pspec->param_id)
- {
- case PROP_COLOR:
- priv->text_color = *color;
- break;
-
- case PROP_CURSOR_COLOR:
- if (color)
- {
- priv->cursor_color = *color;
- priv->cursor_color_set = TRUE;
- }
- else
- priv->cursor_color_set = FALSE;
-
- other = obj_props[PROP_CURSOR_COLOR_SET];
- break;
-
- case PROP_SELECTION_COLOR:
- if (color)
- {
- priv->selection_color = *color;
- priv->selection_color_set = TRUE;
- }
- else
- priv->selection_color_set = FALSE;
-
- other = obj_props[PROP_SELECTION_COLOR_SET];
- break;
-
- case PROP_SELECTED_TEXT_COLOR:
- if (color)
- {
- priv->selected_text_color = *color;
- priv->selected_text_color_set = TRUE;
- }
- else
- priv->selected_text_color_set = FALSE;
-
- other = obj_props[PROP_SELECTED_TEXT_COLOR_SET];
- break;
-
- default:
- g_assert_not_reached ();
- break;
- }
-
- clutter_text_queue_redraw (CLUTTER_ACTOR (self));
- g_object_notify_by_pspec (G_OBJECT (self), pspec);
- if (other)
- g_object_notify_by_pspec (G_OBJECT (self), other);
-}
-
-static void
-clutter_text_set_color_animated (ClutterText *self,
- GParamSpec *pspec,
- const ClutterColor *color)
-{
- ClutterActor *actor = CLUTTER_ACTOR (self);
- ClutterTextPrivate *priv = self->priv;
- const ClutterAnimationInfo *info;
- ClutterTransition *transition;
-
- info = _clutter_actor_get_animation_info (actor);
- transition = clutter_actor_get_transition (actor, pspec->name);
-
- /* jump to the end if there is no easing state, or if the easing
- * state has a duration of 0 msecs
- */
- if (info->cur_state == NULL ||
- info->cur_state->easing_duration == 0)
- {
- /* ensure that we remove any currently running transition */
- if (transition != NULL)
- {
- clutter_actor_remove_transition (actor, pspec->name);
- transition = NULL;
- }
-
- clutter_text_set_color_internal (self, pspec, color);
-
- return;
- }
-
- if (transition == NULL)
- {
- transition = clutter_property_transition_new (pspec->name);
- clutter_transition_set_animatable (transition,
- CLUTTER_ANIMATABLE (self));
- clutter_transition_set_remove_on_complete (transition, TRUE);
-
- /* delay only makes sense if the transition has just been created */
- clutter_timeline_set_delay (CLUTTER_TIMELINE (transition),
- info->cur_state->easing_delay);
-
- clutter_actor_add_transition (actor, pspec->name, transition);
-
- /* the actor now owns the transition */
- g_object_unref (transition);
- }
-
- /* if a transition already exist, update its bounds */
- switch (pspec->param_id)
- {
- case PROP_COLOR:
- clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
- &priv->text_color);
- break;
-
- case PROP_CURSOR_COLOR:
- clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
- &priv->cursor_color);
- break;
-
- case PROP_SELECTION_COLOR:
- clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
- &priv->selection_color);
- break;
-
- case PROP_SELECTED_TEXT_COLOR:
- clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR,
- &priv->selected_text_color);
- break;
-
- default:
- g_assert_not_reached ();
- }
-
- clutter_transition_set_to (transition, CLUTTER_TYPE_COLOR, color);
-
- /* always use the current easing state */
- clutter_timeline_set_duration (CLUTTER_TIMELINE (transition),
- info->cur_state->easing_duration);
- clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition),
- info->cur_state->easing_mode);
-
- /* ensure that we start from the beginning */
- clutter_timeline_rewind (CLUTTER_TIMELINE (transition));
- clutter_timeline_start (CLUTTER_TIMELINE (transition));
-}
-
-static void
-clutter_text_set_custom_property (ClutterScriptable *scriptable,
- ClutterScript *script,
- const gchar *name,
- const GValue *value)
-{
- if (strncmp (name, "font-description", 16) == 0)
- {
- g_assert (G_VALUE_HOLDS (value, G_TYPE_STRING));
- if (g_value_get_string (value) != NULL)
- clutter_text_set_font_name (CLUTTER_TEXT (scriptable),
- g_value_get_string (value));
- }
- else
- parent_scriptable_iface->set_custom_property (scriptable, script,
- name,
- value);
-}
-
-static void
-clutter_scriptable_iface_init (ClutterScriptableIface *iface)
-{
- parent_scriptable_iface = g_type_interface_peek_parent (iface);
-
- iface->parse_custom_node = clutter_text_parse_custom_node;
- iface->set_custom_property = clutter_text_set_custom_property;
-}
-
-static void
-clutter_text_set_final_state (ClutterAnimatable *animatable,
- const char *property_name,
- const GValue *value)
-{
- if (strcmp (property_name, "color") == 0)
- {
- const ClutterColor *color = clutter_value_get_color (value);
- clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
- obj_props[PROP_COLOR], color);
- }
- else if (strcmp (property_name, "cursor-color") == 0)
- {
- const ClutterColor *color = clutter_value_get_color (value);
- clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
- obj_props[PROP_CURSOR_COLOR],
- color);
- }
- else if (strcmp (property_name, "selected-text-color") == 0)
- {
- const ClutterColor *color = clutter_value_get_color (value);
- clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
- obj_props[PROP_SELECTED_TEXT_COLOR],
- color);
- }
- else if (strcmp (property_name, "selection-color") == 0)
- {
- const ClutterColor *color = clutter_value_get_color (value);
- clutter_text_set_color_internal (CLUTTER_TEXT (animatable),
- obj_props[PROP_SELECTION_COLOR],
- color);
- }
- else
- parent_animatable_iface->set_final_state (animatable, property_name, value);
-}
-
-static void
-clutter_animatable_iface_init (ClutterAnimatableInterface *iface)
-{
- parent_animatable_iface = g_type_interface_peek_parent (iface);
-
- iface->set_final_state = clutter_text_set_final_state;
-}
-
-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;
-
- 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_paint_volume = clutter_text_get_paint_volume;
- 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->key_release_event = clutter_text_key_release;
- 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;
- actor_class->touch_event = clutter_text_touch_event;
- actor_class->key_focus_in = clutter_text_key_focus_in;
- actor_class->key_focus_out = clutter_text_key_focus_out;
- actor_class->has_overlaps = clutter_text_has_overlaps;
- actor_class->calculate_resource_scale = clutter_text_calculate_resource_scale;
- actor_class->resource_scale_changed = clutter_text_resource_scale_changed;
- actor_class->event = clutter_text_event;
-
- /**
- * ClutterText:buffer:
- *
- * The buffer which stores the text for this #ClutterText.
- *
- * If set to %NULL, a default buffer will be created.
- *
- * Since: 1.8
- */
- pspec = g_param_spec_object ("buffer",
- P_("Buffer"),
- P_("The buffer for the text"),
- CLUTTER_TYPE_TEXT_BUFFER,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_BUFFER] = pspec;
- g_object_class_install_property (gobject_class, PROP_BUFFER, pspec);
-
- /**
- * ClutterText:font-name:
- *
- * The font to be used by the #ClutterText, as a string
- * that can be parsed by pango_font_description_from_string().
- *
- * If set to %NULL, the default system font will be used instead.
- *
- * Since: 1.0
- */
- pspec = g_param_spec_string ("font-name",
- P_("Font Name"),
- P_("The font to be used by the text"),
- NULL,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_FONT_NAME] = pspec;
- g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec);
-
- /**
- * ClutterText:font-description:
- *
- * The #PangoFontDescription that should be used by the #ClutterText
- *
- * If you have a string describing the font then you should look at
- * #ClutterText:font-name instead
- *
- * Since: 1.2
- */
- pspec = g_param_spec_boxed ("font-description",
- P_("Font Description"),
- P_("The font description to be used"),
- PANGO_TYPE_FONT_DESCRIPTION,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_FONT_DESCRIPTION] = pspec;
- g_object_class_install_property (gobject_class,
- PROP_FONT_DESCRIPTION,
- pspec);
-
- /**
- * ClutterText:text:
- *
- * The text to render inside the actor.
- *
- * Since: 1.0
- */
- pspec = g_param_spec_string ("text",
- P_("Text"),
- P_("The text to render"),
- "",
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_TEXT] = pspec;
- 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",
- P_("Font Color"),
- P_("Color of the font used by the text"),
- &default_text_color,
- CLUTTER_PARAM_READWRITE |
- CLUTTER_PARAM_ANIMATABLE);
- obj_props[PROP_COLOR] = pspec;
- 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",
- P_("Editable"),
- P_("Whether the text is editable"),
- FALSE,
- G_PARAM_READWRITE);
- obj_props[PROP_EDITABLE] = pspec;
- 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.
- *
- * This property depends on the #ClutterActor:reactive property being
- * set to %TRUE.
- *
- * Since: 1.0
- */
- pspec = g_param_spec_boolean ("selectable",
- P_("Selectable"),
- P_("Whether the text is selectable"),
- TRUE,
- G_PARAM_READWRITE);
- obj_props[PROP_SELECTABLE] = pspec;
- 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",
- P_("Activatable"),
- P_("Whether pressing return causes the activate signal to be emitted"),
- TRUE,
- G_PARAM_READWRITE);
- obj_props[PROP_ACTIVATABLE] = pspec;
- g_object_class_install_property (gobject_class, PROP_ACTIVATABLE, pspec);
-
- /**
- * ClutterText:cursor-visible:
- *
- * Whether the input cursor is visible or not.
- *
- * The cursor will only be visible if this property and either
- * the #ClutterText:editable or the #ClutterText:selectable properties
- * are set to %TRUE.
- *
- * Since: 1.0
- */
- pspec = g_param_spec_boolean ("cursor-visible",
- P_("Cursor Visible"),
- P_("Whether the input cursor is visible"),
- TRUE,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_CURSOR_VISIBLE] = pspec;
- 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",
- P_("Cursor Color"),
- P_("Cursor Color"),
- &default_cursor_color,
- CLUTTER_PARAM_READWRITE |
- CLUTTER_PARAM_ANIMATABLE);
- obj_props[PROP_CURSOR_COLOR] = pspec;
- 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",
- P_("Cursor Color Set"),
- P_("Whether the cursor color has been set"),
- FALSE,
- CLUTTER_PARAM_READABLE);
- obj_props[PROP_CURSOR_COLOR_SET] = pspec;
- 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",
- P_("Cursor Size"),
- P_("The width of the cursor, in pixels"),
- -1, G_MAXINT, DEFAULT_CURSOR_SIZE,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_CURSOR_SIZE] = pspec;
- 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
- *
- * Deprecated: 1.12: Use ClutterText:cursor-position instead.
- */
- pspec = g_param_spec_int ("position",
- P_("Cursor Position"),
- P_("The cursor position"),
- -1, G_MAXINT,
- -1,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS |
- G_PARAM_DEPRECATED);
- obj_props[PROP_POSITION] = pspec;
- g_object_class_install_property (gobject_class, PROP_POSITION, pspec);
-
- /**
- * ClutterText:cursor-position:
- *
- * The current input cursor position. -1 is taken to be the end of the text
- *
- * Since: 1.12
- */
- pspec = g_param_spec_int ("cursor-position",
- P_("Cursor Position"),
- P_("The cursor position"),
- -1, G_MAXINT,
- -1,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_CURSOR_POSITION] = pspec;
- g_object_class_install_property (gobject_class, PROP_CURSOR_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",
- P_("Selection-bound"),
- P_("The cursor position of the other end of the selection"),
- -1, G_MAXINT,
- -1,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_SELECTION_BOUND] = pspec;
- g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec);
-
- /**
- * ClutterText:selection-color:
- *
- * The color of the selection.
- *
- * Since: 1.0
- */
- pspec = clutter_param_spec_color ("selection-color",
- P_("Selection Color"),
- P_("Selection Color"),
- &default_selection_color,
- CLUTTER_PARAM_READWRITE |
- CLUTTER_PARAM_ANIMATABLE);
- obj_props[PROP_SELECTION_COLOR] = pspec;
- g_object_class_install_property (gobject_class, PROP_SELECTION_COLOR, pspec);
-
- /**
- * ClutterText:selection-color-set:
- *
- * Will be set to %TRUE if #ClutterText:selection-color has been set.
- *
- * Since: 1.0
- */
- pspec = g_param_spec_boolean ("selection-color-set",
- P_("Selection Color Set"),
- P_("Whether the selection color has been set"),
- FALSE,
- CLUTTER_PARAM_READABLE);
- obj_props[PROP_SELECTION_COLOR_SET] = pspec;
- g_object_class_install_property (gobject_class, PROP_SELECTION_COLOR_SET, 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",
- P_("Attributes"),
- P_("A list of style attributes to apply to the contents of the actor"),
- PANGO_TYPE_ATTR_LIST,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_ATTRIBUTES] = pspec;
- g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec);
-
- /**
- * ClutterText:use-markup:
- *
- * Whether the text includes Pango markup.
- *
- * For more information about the Pango markup format, see
- * pango_layout_set_markup() in the Pango documentation.
- *
- * It is not possible to round-trip this property between
- * %TRUE and %FALSE. Once a string with markup has been set on
- * a #ClutterText actor with :use-markup set to %TRUE, the markup
- * is stripped from the string.
- *
- * Since: 1.0
- */
- pspec = g_param_spec_boolean ("use-markup",
- P_("Use markup"),
- P_("Whether or not the text includes Pango markup"),
- FALSE,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_USE_MARKUP] = pspec;
- 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",
- P_("Line wrap"),
- P_("If set, wrap the lines if the text becomes too wide"),
- FALSE,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_LINE_WRAP] = pspec;
- 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",
- P_("Line wrap mode"),
- P_("Control how line-wrapping is done"),
- PANGO_TYPE_WRAP_MODE,
- PANGO_WRAP_WORD,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_LINE_WRAP_MODE] = pspec;
- 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",
- P_("Ellipsize"),
- P_("The preferred place to ellipsize the string"),
- PANGO_TYPE_ELLIPSIZE_MODE,
- PANGO_ELLIPSIZE_NONE,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_ELLIPSIZE] = pspec;
- g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec);
-
- /**
- * ClutterText:line-alignment:
- *
- * The preferred alignment for the text. This property controls
- * the alignment of multi-line paragraphs.
- *
- * Since: 1.0
- */
- pspec = g_param_spec_enum ("line-alignment",
- P_("Line Alignment"),
- P_("The preferred alignment for the string, for multi-line text"),
- PANGO_TYPE_ALIGNMENT,
- PANGO_ALIGN_LEFT,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_LINE_ALIGNMENT] = pspec;
- g_object_class_install_property (gobject_class, PROP_LINE_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",
- P_("Justify"),
- P_("Whether the text should be justified"),
- FALSE,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_JUSTIFY] = pspec;
- 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",
- P_("Password Character"),
- P_("If non-zero, use this character to display the actor's contents"),
- 0,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_PASSWORD_CHAR] = pspec;
- 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",
- P_("Max Length"),
- P_("Maximum length of the text inside the actor"),
- -1, G_MAXINT, 0,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_MAX_LENGTH] = pspec;
- 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.
- *
- * The #ClutterText:single-line-mode property is used only if the
- * #ClutterText:editable property is set to %TRUE.
- *
- * Since: 1.0
- */
- pspec = g_param_spec_boolean ("single-line-mode",
- P_("Single Line Mode"),
- P_("Whether the text should be a single line"),
- FALSE,
- CLUTTER_PARAM_READWRITE);
- obj_props[PROP_SINGLE_LINE_MODE] = pspec;
- g_object_class_install_property (gobject_class, PROP_SINGLE_LINE_MODE, pspec);
-
- /**
- * ClutterText:selected-text-color:
- *
- * The color of selected text.
- *
- * Since: 1.8
- */
- pspec = clutter_param_spec_color ("selected-text-color",
- P_("Selected Text Color"),
- P_("Selected Text Color"),
- &default_selected_text_color,
- CLUTTER_PARAM_READWRITE |
- CLUTTER_PARAM_ANIMATABLE);
- obj_props[PROP_SELECTED_TEXT_COLOR] = pspec;
- g_object_class_install_property (gobject_class, PROP_SELECTED_TEXT_COLOR, pspec);
-
- /**
- * ClutterText:selected-text-color-set:
- *
- * Will be set to %TRUE if #ClutterText:selected-text-color has been set.
- *
- * Since: 1.8
- */
- pspec = g_param_spec_boolean ("selected-text-color-set",
- P_("Selected Text Color Set"),
- P_("Whether the selected text color has been set"),
- FALSE,
- CLUTTER_PARAM_READABLE);
- obj_props[PROP_SELECTED_TEXT_COLOR_SET] = pspec;
- g_object_class_install_property (gobject_class, PROP_SELECTED_TEXT_COLOR_SET, pspec);
-
- pspec = g_param_spec_flags ("input-hints",
- P_("Input hints"),
- P_("Input hints"),
- CLUTTER_TYPE_INPUT_CONTENT_HINT_FLAGS,
- 0, CLUTTER_PARAM_READWRITE);
- obj_props[PROP_INPUT_HINTS] = pspec;
- g_object_class_install_property (gobject_class, PROP_INPUT_HINTS, pspec);
-
- pspec = g_param_spec_enum ("input-purpose",
- P_("Input purpose"),
- P_("Input purpose"),
- CLUTTER_TYPE_INPUT_CONTENT_PURPOSE,
- 0, CLUTTER_PARAM_READWRITE);
- obj_props[PROP_INPUT_PURPOSE] = pspec;
- g_object_class_install_property (gobject_class, PROP_INPUT_PURPOSE, 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 (I_("text-changed"),
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ClutterTextClass, text_changed),
- NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-
- /**
- * ClutterText::insert-text:
- * @self: the #ClutterText that emitted the signal
- * @new_text: the new text to insert
- * @new_text_length: the length of the new text, in bytes, or -1 if
- * new_text is nul-terminated
- * @position: the position, in characters, at which to insert the
- * new text. this is an in-out parameter. After the signal
- * emission is finished, it should point after the newly
- * inserted text.
- *
- * This signal is emitted when text is inserted into the actor by
- * the user. It is emitted before @self text changes.
- *
- * Since: 1.2
- */
- text_signals[INSERT_TEXT] =
- g_signal_new (I_("insert-text"),
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- 0,
- NULL, NULL,
- _clutter_marshal_VOID__STRING_INT_POINTER,
- G_TYPE_NONE, 3,
- G_TYPE_STRING,
- G_TYPE_INT,
- G_TYPE_POINTER);
-
- /**
- * ClutterText::delete-text:
- * @self: the #ClutterText that emitted the signal
- * @start_pos: the starting position
- * @end_pos: the end position
- *
- * This signal is emitted when text is deleted from the actor by
- * the user. It is emitted before @self text changes.
- *
- * Since: 1.2
- */
- text_signals[DELETE_TEXT] =
- g_signal_new (I_("delete-text"),
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- 0,
- NULL, NULL,
- _clutter_marshal_VOID__INT_INT,
- G_TYPE_NONE, 2,
- G_TYPE_INT,
- G_TYPE_INT);
-
- /**
- * ClutterText::cursor-event:
- * @self: the #ClutterText that emitted the signal
- * @rect: the coordinates of the cursor
- *
- * The ::cursor-event signal is emitted whenever the cursor position
- * changes inside a #ClutterText actor. Inside @rect it is stored
- * the current position and size of the cursor, relative to the actor
- * itself.
- *
- * Since: 1.0
- *
- * Deprecated: 1.16: Use the #ClutterText::cursor-changed signal instead
- */
- text_signals[CURSOR_EVENT] =
- g_signal_new (I_("cursor-event"),
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED,
- G_STRUCT_OFFSET (ClutterTextClass, cursor_event),
- NULL, NULL, NULL,
- G_TYPE_NONE, 1,
- GRAPHENE_TYPE_RECT | G_SIGNAL_TYPE_STATIC_SCOPE);
-
- /**
- * ClutterText::cursor-changed:
- * @self: the #ClutterText that emitted the signal
- *
- * The ::cursor-changed signal is emitted whenever the cursor
- * position or size changes.
- *
- * Since: 1.16
- */
- text_signals[CURSOR_CHANGED] =
- g_signal_new (I_("cursor-changed"),
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ClutterTextClass, cursor_changed),
- NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-
- /**
- * 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 (I_("activate"),
- G_TYPE_FROM_CLASS (gobject_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ClutterTextClass, activate),
- NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-
- binding_pool = clutter_binding_pool_get_for_class (klass);
-
- clutter_text_add_move_binding (binding_pool, "move-left",
- CLUTTER_KEY_Left, CLUTTER_CONTROL_MASK,
- G_CALLBACK (clutter_text_real_move_left));
- clutter_text_add_move_binding (binding_pool, "move-left",
- CLUTTER_KEY_KP_Left, CLUTTER_CONTROL_MASK,
- G_CALLBACK (clutter_text_real_move_left));
- clutter_text_add_move_binding (binding_pool, "move-right",
- CLUTTER_KEY_Right, CLUTTER_CONTROL_MASK,
- G_CALLBACK (clutter_text_real_move_right));
- clutter_text_add_move_binding (binding_pool, "move-right",
- CLUTTER_KEY_KP_Right, CLUTTER_CONTROL_MASK,
- G_CALLBACK (clutter_text_real_move_right));
- clutter_text_add_move_binding (binding_pool, "move-up",
- CLUTTER_KEY_Up, 0,
- G_CALLBACK (clutter_text_real_move_up));
- clutter_text_add_move_binding (binding_pool, "move-up",
- CLUTTER_KEY_KP_Up, 0,
- G_CALLBACK (clutter_text_real_move_up));
- clutter_text_add_move_binding (binding_pool, "move-down",
- CLUTTER_KEY_Down, 0,
- G_CALLBACK (clutter_text_real_move_down));
- clutter_text_add_move_binding (binding_pool, "move-down",
- CLUTTER_KEY_KP_Down, 0,
- G_CALLBACK (clutter_text_real_move_down));
-
- clutter_text_add_move_binding (binding_pool, "line-start",
- CLUTTER_KEY_Home, 0,
- G_CALLBACK (clutter_text_real_line_start));
- clutter_text_add_move_binding (binding_pool, "line-start",
- CLUTTER_KEY_KP_Home, 0,
- G_CALLBACK (clutter_text_real_line_start));
- clutter_text_add_move_binding (binding_pool, "line-start",
- CLUTTER_KEY_Begin, 0,
- G_CALLBACK (clutter_text_real_line_start));
- clutter_text_add_move_binding (binding_pool, "line-end",
- CLUTTER_KEY_End, 0,
- G_CALLBACK (clutter_text_real_line_end));
- clutter_text_add_move_binding (binding_pool, "line-end",
- CLUTTER_KEY_KP_End, 0,
- G_CALLBACK (clutter_text_real_line_end));
-
- clutter_binding_pool_install_action (binding_pool, "select-all",
- CLUTTER_KEY_a, CLUTTER_CONTROL_MASK,
- G_CALLBACK (clutter_text_real_select_all),
- NULL, NULL);
- clutter_binding_pool_install_action (binding_pool, "select-all",
- CLUTTER_KEY_A, CLUTTER_CONTROL_MASK,
- G_CALLBACK (clutter_text_real_select_all),
- NULL, NULL);
-
- clutter_binding_pool_install_action (binding_pool, "delete-next",
- CLUTTER_KEY_Delete, 0,
- G_CALLBACK (clutter_text_real_del_next),
- NULL, NULL);
- clutter_binding_pool_install_action (binding_pool, "delete-next",
- CLUTTER_KEY_Delete, CLUTTER_CONTROL_MASK,
- G_CALLBACK (clutter_text_real_del_word_next),
- NULL, NULL);
- clutter_binding_pool_install_action (binding_pool, "delete-next",
- CLUTTER_KEY_KP_Delete, 0,
- G_CALLBACK (clutter_text_real_del_next),
- NULL, NULL);
- clutter_binding_pool_install_action (binding_pool, "delete-next",
- CLUTTER_KEY_KP_Delete, CLUTTER_CONTROL_MASK,
- G_CALLBACK (clutter_text_real_del_word_next),
- NULL, NULL);
- clutter_binding_pool_install_action (binding_pool, "delete-prev",
- CLUTTER_KEY_BackSpace, 0,
- G_CALLBACK (clutter_text_real_del_prev),
- NULL, NULL);
- clutter_binding_pool_install_action (binding_pool, "delete-prev",
- CLUTTER_KEY_BackSpace, CLUTTER_SHIFT_MASK,
- G_CALLBACK (clutter_text_real_del_prev),
- NULL, NULL);
- clutter_binding_pool_install_action (binding_pool, "delete-prev",
- CLUTTER_KEY_BackSpace, CLUTTER_CONTROL_MASK,
- G_CALLBACK (clutter_text_real_del_word_prev),
- NULL, NULL);
-
- clutter_binding_pool_install_action (binding_pool, "activate",
- CLUTTER_KEY_Return, 0,
- G_CALLBACK (clutter_text_real_activate),
- NULL, NULL);
- clutter_binding_pool_install_action (binding_pool, "activate",
- CLUTTER_KEY_KP_Enter, 0,
- G_CALLBACK (clutter_text_real_activate),
- NULL, NULL);
- clutter_binding_pool_install_action (binding_pool, "activate",
- CLUTTER_KEY_ISO_Enter, 0,
- G_CALLBACK (clutter_text_real_activate),
- NULL, NULL);
-}
-
-static void
-clutter_text_init (ClutterText *self)
-{
- ClutterSettings *settings;
- ClutterTextPrivate *priv;
- gchar *font_name;
- int i, password_hint_time;
-
- self->priv = priv = clutter_text_get_instance_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->buffer = NULL;
-
- priv->text_color = default_text_color;
- priv->cursor_color = default_cursor_color;
- priv->selection_color = default_selection_color;
- priv->selected_text_color = default_selected_text_color;
-
- /* get the default font name from the context; we don't use
- * set_font_description() here because we are initializing
- * the Text and we don't need notifications and sanity checks
- */
- settings = clutter_settings_get_default ();
- g_object_get (settings,
- "font-name", &font_name,
- "password-hint-time", &password_hint_time,
- NULL);
-
- priv->font_name = font_name; /* font_name is allocated */
- priv->font_desc = pango_font_description_from_string (font_name);
- priv->is_default_font = TRUE;
-
- priv->position = -1;
- priv->selection_bound = -1;
-
- priv->x_pos = -1;
- priv->cursor_visible = TRUE;
- priv->editable = FALSE;
- priv->selectable = TRUE;
-
- priv->selection_color_set = FALSE;
- priv->cursor_color_set = FALSE;
- priv->selected_text_color_set = FALSE;
- priv->preedit_set = FALSE;
-
- priv->password_char = 0;
- priv->show_password_hint = password_hint_time > 0;
- priv->password_hint_timeout = password_hint_time;
-
- priv->text_y = 0;
-
- priv->cursor_size = DEFAULT_CURSOR_SIZE;
-
- priv->settings_changed_id =
- g_signal_connect_swapped (clutter_get_default_backend (),
- "settings-changed",
- G_CALLBACK (clutter_text_settings_changed_cb),
- self);
-
- priv->direction_changed_id =
- g_signal_connect (self, "notify::text-direction",
- G_CALLBACK (clutter_text_direction_changed_cb),
- NULL);
-
- priv->input_focus = clutter_text_input_focus_new (self);
-}
-
-/**
- * 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: (allow-none): 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);
-}
-
-static ClutterTextBuffer*
-get_buffer (ClutterText *self)
-{
- ClutterTextPrivate *priv = self->priv;
-
- if (priv->buffer == NULL)
- {
- ClutterTextBuffer *buffer;
- buffer = clutter_text_buffer_new ();
- clutter_text_set_buffer (self, buffer);
- g_object_unref (buffer);
- }
-
- return priv->buffer;
-}
-
-/* GtkEntryBuffer signal handlers
- */
-static void
-buffer_inserted_text (ClutterTextBuffer *buffer,
- guint position,
- const gchar *chars,
- guint n_chars,
- ClutterText *self)
-{
- ClutterTextPrivate *priv;
- gint new_position;
- gint new_selection_bound;
-
- priv = self->priv;
- if (priv->position >= 0 || priv->selection_bound >= 0)
- {
- new_position = priv->position;
- new_selection_bound = priv->selection_bound;
-
- if (position <= new_position)
- new_position += n_chars;
- if (position <= new_selection_bound)
- new_selection_bound += n_chars;
-
- if (priv->position != new_position || priv->selection_bound != new_selection_bound)
- clutter_text_set_positions (self, new_position, new_selection_bound);
- }
-
- /* TODO: What are we supposed to with the out value of position? */
-}
-
-static void
-buffer_deleted_text (ClutterTextBuffer *buffer,
- guint position,
- guint n_chars,
- ClutterText *self)
-{
- ClutterTextPrivate *priv;
- gint new_position;
- gint new_selection_bound;
-
- priv = self->priv;
- if (priv->position >= 0 || priv->selection_bound >= 0)
- {
- new_position = priv->position;
- new_selection_bound = priv->selection_bound;
-
- if (position < new_position)
- new_position -= n_chars;
- if (position < new_selection_bound)
- new_selection_bound -= n_chars;
-
- if (priv->position != new_position || priv->selection_bound != new_selection_bound)
- clutter_text_set_positions (self, new_position, new_selection_bound);
- }
-}
-
-static void
-clutter_text_queue_redraw_or_relayout (ClutterText *self)
-{
- ClutterActor *actor = CLUTTER_ACTOR (self);
- gfloat preferred_width;
- gfloat preferred_height;
-
- clutter_text_dirty_cache (self);
-
- /* we're using our private implementations here to avoid the caching done by ClutterActor */
- clutter_text_get_preferred_width (actor, -1, NULL, &preferred_width);
- clutter_text_get_preferred_height (actor, preferred_width, NULL, &preferred_height);
-
- if (clutter_actor_has_allocation (actor) &&
- fabsf (preferred_width - clutter_actor_get_width (actor)) <= 0.001 &&
- fabsf (preferred_height - clutter_actor_get_height (actor)) <= 0.001)
- clutter_text_queue_redraw (actor);
- else
- clutter_actor_queue_relayout (actor);
-}
-
-static void
-buffer_notify_text (ClutterTextBuffer *buffer,
- GParamSpec *spec,
- ClutterText *self)
-{
- g_object_freeze_notify (G_OBJECT (self));
-
- clutter_text_queue_redraw_or_relayout (self);
-
- g_signal_emit (self, text_signals[TEXT_CHANGED], 0);
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT]);
-
- g_object_thaw_notify (G_OBJECT (self));
-}
-
-static void
-buffer_notify_max_length (ClutterTextBuffer *buffer,
- GParamSpec *spec,
- ClutterText *self)
-{
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAX_LENGTH]);
-}
-
-static void
-buffer_connect_signals (ClutterText *self)
-{
- ClutterTextPrivate *priv = self->priv;
- g_signal_connect (priv->buffer, "inserted-text", G_CALLBACK (buffer_inserted_text), self);
- g_signal_connect (priv->buffer, "deleted-text", G_CALLBACK (buffer_deleted_text), self);
- g_signal_connect (priv->buffer, "notify::text", G_CALLBACK (buffer_notify_text), self);
- g_signal_connect (priv->buffer, "notify::max-length", G_CALLBACK (buffer_notify_max_length), self);
-}
-
-static void
-buffer_disconnect_signals (ClutterText *self)
-{
- ClutterTextPrivate *priv = self->priv;
- g_signal_handlers_disconnect_by_func (priv->buffer, buffer_inserted_text, self);
- g_signal_handlers_disconnect_by_func (priv->buffer, buffer_deleted_text, self);
- g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_text, self);
- g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_max_length, self);
-}
-
-/**
- * clutter_text_new_with_buffer:
- * @buffer: The buffer to use for the new #ClutterText.
- *
- * Creates a new entry with the specified text buffer.
- *
- * Return value: a new #ClutterText
- *
- * Since: 1.10
- */
-ClutterActor *
-clutter_text_new_with_buffer (ClutterTextBuffer *buffer)
-{
- g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), NULL);
- return g_object_new (CLUTTER_TYPE_TEXT, "buffer", buffer, NULL);
-}
-
-/**
- * clutter_text_get_buffer:
- * @self: a #ClutterText
- *
- * Get the #ClutterTextBuffer object which holds the text for
- * this widget.
- *
- * Returns: (transfer none): A #GtkEntryBuffer object.
- *
- * Since: 1.10
- */
-ClutterTextBuffer*
-clutter_text_get_buffer (ClutterText *self)
-{
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
-
- return get_buffer (self);
-}
-
-/**
- * clutter_text_set_buffer:
- * @self: a #ClutterText
- * @buffer: a #ClutterTextBuffer
- *
- * Set the #ClutterTextBuffer object which holds the text for
- * this widget.
- *
- * Since: 1.10
- */
-void
-clutter_text_set_buffer (ClutterText *self,
- ClutterTextBuffer *buffer)
-{
- ClutterTextPrivate *priv;
- GObject *obj;
-
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- priv = self->priv;
-
- if (buffer)
- {
- g_return_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer));
- g_object_ref (buffer);
- }
-
- if (priv->buffer)
- {
- buffer_disconnect_signals (self);
- g_object_unref (priv->buffer);
- }
-
- priv->buffer = buffer;
-
- if (priv->buffer)
- buffer_connect_signals (self);
-
- obj = G_OBJECT (self);
- g_object_freeze_notify (obj);
- g_object_notify_by_pspec (obj, obj_props[PROP_BUFFER]);
- g_object_notify_by_pspec (obj, obj_props[PROP_TEXT]);
- g_object_notify_by_pspec (obj, obj_props[PROP_MAX_LENGTH]);
- g_object_thaw_notify (obj);
-}
-
-/**
- * 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_set_key_focus()
- * will receive key events and will update its contents accordingly.
- *
- * Since: 1.0
- */
-void
-clutter_text_set_editable (ClutterText *self,
- gboolean editable)
-{
- ClutterBackend *backend = clutter_get_default_backend ();
- ClutterInputMethod *method = clutter_backend_get_input_method (backend);
- ClutterTextPrivate *priv;
-
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- priv = self->priv;
-
- if (priv->editable != editable)
- {
- priv->editable = editable;
-
- if (method)
- {
- if (!priv->editable && clutter_input_focus_is_focused (priv->input_focus))
- clutter_input_method_focus_out (method);
- else if (priv->has_focus)
- clutter_text_im_focus (self);
- }
-
- clutter_text_queue_redraw (CLUTTER_ACTOR (self));
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_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;
-
- clutter_text_queue_redraw (CLUTTER_ACTOR (self));
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_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;
-
- clutter_text_queue_redraw (CLUTTER_ACTOR (self));
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_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;
-
- cursor_visible = !!cursor_visible;
-
- if (priv->cursor_visible != cursor_visible)
- {
- priv->cursor_visible = cursor_visible;
-
- clutter_text_queue_redraw_or_relayout (self);
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_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: (allow-none): 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)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- clutter_text_set_color_animated (self, obj_props[PROP_CURSOR_COLOR], color);
-}
-
-/**
- * clutter_text_get_cursor_color:
- * @self: a #ClutterText
- * @color: (out): 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)
-{
- guint n_chars;
-
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- n_chars = clutter_text_buffer_get_length (get_buffer (self));
- if (end_pos < 0)
- end_pos = n_chars;
-
- start_pos = MIN (n_chars, start_pos);
- end_pos = MIN (n_chars, end_pos);
-
- clutter_text_set_positions (self, start_pos, end_pos);
-}
-
-/**
- * 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;
- const gchar *text;
-
- 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 != -1 && end_index < start_index) ||
- start_index == -1)
- {
- gint temp = start_index;
- start_index = end_index;
- end_index = temp;
- }
-
- text = clutter_text_buffer_get_text (get_buffer (self));
- start_offset = offset_to_bytes (text, start_index);
- end_offset = offset_to_bytes (text, end_index);
- len = end_offset - start_offset;
-
- str = g_malloc (len + 1);
- g_utf8_strncpy (str, 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)
- {
- gint len = clutter_text_buffer_get_length (get_buffer (self));
-
- if (selection_bound < 0 || selection_bound >= len)
- priv->selection_bound = -1;
- else
- priv->selection_bound = selection_bound;
-
- clutter_text_queue_redraw (CLUTTER_ACTOR (self));
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_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_set_selection_color:
- * @self: a #ClutterText
- * @color: (allow-none): the color of the selection, or %NULL to unset it
- *
- * Sets the color of the selection of a #ClutterText actor.
- *
- * If @color is %NULL, the selection color will be the same as the
- * cursor color, or if no cursor color is set either then it will be
- * the same as the text color.
- *
- * Since: 1.0
- */
-void
-clutter_text_set_selection_color (ClutterText *self,
- const ClutterColor *color)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- clutter_text_set_color_animated (self, obj_props[PROP_SELECTION_COLOR],
- color);
-}
-
-/**
- * clutter_text_get_selection_color:
- * @self: a #ClutterText
- * @color: (out caller-allocates): return location for a #ClutterColor
- *
- * Retrieves the color of the selection of a #ClutterText actor.
- *
- * Since: 1.0
- */
-void
-clutter_text_get_selection_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->selection_color;
-}
-
-/**
- * clutter_text_set_selected_text_color:
- * @self: a #ClutterText
- * @color: (allow-none): the selected text color, or %NULL to unset it
- *
- * Sets the selected text color of a #ClutterText actor.
- *
- * If @color is %NULL, the selected text color will be the same as the
- * selection color, which then falls back to cursor, and then text color.
- *
- * Since: 1.8
- */
-void
-clutter_text_set_selected_text_color (ClutterText *self,
- const ClutterColor *color)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- clutter_text_set_color_animated (self, obj_props[PROP_SELECTED_TEXT_COLOR],
- color);
-}
-
-/**
- * clutter_text_get_selected_text_color:
- * @self: a #ClutterText
- * @color: (out caller-allocates): return location for a #ClutterColor
- *
- * Retrieves the color of selected text of a #ClutterText actor.
- *
- * Since: 1.8
- */
-void
-clutter_text_get_selected_text_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->selected_text_color;
-}
-
-/**
- * clutter_text_set_font_description:
- * @self: a #ClutterText
- * @font_desc: a #PangoFontDescription
- *
- * Sets @font_desc as the font description for a #ClutterText
- *
- * The #PangoFontDescription is copied by the #ClutterText actor
- * so you can safely call pango_font_description_free() on it after
- * calling this function.
- *
- * Since: 1.2
- */
-void
-clutter_text_set_font_description (ClutterText *self,
- PangoFontDescription *font_desc)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- clutter_text_set_font_description_internal (self, font_desc,
- font_desc == NULL);
-}
-
-/**
- * clutter_text_get_font_description:
- * @self: a #ClutterText
- *
- * Retrieves the #PangoFontDescription used by @self
- *
- * Return value: a #PangoFontDescription. The returned value is owned
- * by the #ClutterText actor and it should not be modified or freed
- *
- * Since: 1.2
- */
-PangoFontDescription *
-clutter_text_get_font_description (ClutterText *self)
-{
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
-
- return self->priv->font_desc;
-}
-
-/**
- * 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
- */
-const 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: (allow-none): 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:
- *
- * |[
- * // Set the font to the system's Sans, 10 points
- * clutter_text_set_font_name (text, "Sans 10");
- *
- * // Set the font to the system's Serif, 16 pixels
- * clutter_text_set_font_name (text, "Serif 16px");
- *
- * // Set the font to Helvetica, 10 points
- * 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;
- gboolean is_default_font;
-
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- /* get the default font name from the backend */
- if (font_name == NULL || font_name[0] == '\0')
- {
- ClutterSettings *settings = clutter_settings_get_default ();
- gchar *default_font_name = NULL;
-
- g_object_get (settings, "font-name", &default_font_name, NULL);
-
- if (default_font_name != NULL)
- font_name = default_font_name;
- else
- {
- /* last fallback */
- font_name = g_strdup ("Sans 12");
- }
-
- is_default_font = TRUE;
- }
- else
- is_default_font = FALSE;
-
- priv = self->priv;
-
- if (g_strcmp0 (priv->font_name, font_name) == 0)
- goto out;
-
- desc = pango_font_description_from_string (font_name);
- if (desc == NULL)
- {
- g_warning ("Attempting to create a PangoFontDescription for "
- "font name '%s', but failed.",
- font_name);
- goto out;
- }
-
- /* this will set the font_name field as well */
- clutter_text_set_font_description_internal (self, desc, is_default_font);
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_NAME]);
-
- pango_font_description_free (desc);
-
-out:
- if (is_default_font)
- g_free ((gchar *) 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.
- *
- * If the #ClutterText actor is empty, this function will return
- * an empty string, and not %NULL.
- *
- * Return value: (transfer none): the contents of the actor. The returned
- * string is owned by the #ClutterText actor and should never be modified
- * or freed
- *
- * Since: 1.0
- */
-const gchar *
-clutter_text_get_text (ClutterText *self)
-{
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
-
- return clutter_text_buffer_get_text (get_buffer (self));
-}
-
-static inline void
-clutter_text_set_use_markup_internal (ClutterText *self,
- gboolean use_markup)
-{
- ClutterTextPrivate *priv = self->priv;
-
- if (priv->use_markup != use_markup)
- {
- priv->use_markup = use_markup;
-
- /* reset the attributes lists so that they can be
- * re-generated
- */
- if (priv->effective_attrs != NULL)
- {
- pango_attr_list_unref (priv->effective_attrs);
- priv->effective_attrs = NULL;
- }
-
- if (priv->markup_attrs)
- {
- pango_attr_list_unref (priv->markup_attrs);
- priv->markup_attrs = NULL;
- }
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_USE_MARKUP]);
- }
-}
-
-/**
- * clutter_text_set_text:
- * @self: a #ClutterText
- * @text: (allow-none): the text to set. Passing %NULL is the same
- * as passing "" (the empty string)
- *
- * Sets the contents of a #ClutterText actor.
- *
- * If the #ClutterText:use-markup property was set to %TRUE it
- * will be reset to %FALSE as a side effect. If you want to
- * maintain the #ClutterText:use-markup you should use the
- * clutter_text_set_markup() function instead
- *
- * Since: 1.0
- */
-void
-clutter_text_set_text (ClutterText *self,
- const gchar *text)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- /* if the text is editable (i.e. there is not markup flag to reset) then
- * changing the contents will result in selection and cursor changes that
- * we should avoid
- */
- if (self->priv->editable)
- {
- if (g_strcmp0 (clutter_text_buffer_get_text (get_buffer (self)), text) == 0)
- return;
- }
-
- clutter_text_set_use_markup_internal (self, FALSE);
- clutter_text_buffer_set_text (get_buffer (self), text ? text : "", -1);
-}
-
-/**
- * clutter_text_set_markup:
- * @self: a #ClutterText
- * @markup: (allow-none): a string containing Pango markup.
- * Passing %NULL is the same as passing "" (the empty string)
- *
- * Sets @markup as the contents of a #ClutterText.
- *
- * This is a convenience function for setting a string containing
- * Pango markup, and it is logically equivalent to:
- *
- * |[
- * /&ast; the order is important &ast;/
- * clutter_text_set_text (CLUTTER_TEXT (actor), markup);
- * clutter_text_set_use_markup (CLUTTER_TEXT (actor), TRUE);
- * ]|
- *
- * Since: 1.0
- */
-void
-clutter_text_set_markup (ClutterText *self,
- const gchar *markup)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- clutter_text_set_use_markup_internal (self, TRUE);
- if (markup != NULL && *markup != '\0')
- clutter_text_set_markup_internal (self, markup);
- else
- clutter_text_buffer_set_text (get_buffer (self), "", 0);
-}
-
-/**
- * clutter_text_get_layout:
- * @self: a #ClutterText
- *
- * Retrieves the current #PangoLayout used by a #ClutterText actor.
- *
- * Return value: (transfer none): 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)
-{
- PangoLayout *layout;
- gfloat width, height;
-
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
-
- if (self->priv->editable && self->priv->single_line_mode)
- return clutter_text_create_layout (self, -1, -1);
-
- clutter_actor_get_size (CLUTTER_ACTOR (self), &width, &height);
- layout = maybe_create_text_layout_with_resource_scale (self, width, height);
-
- if (!layout)
- layout = clutter_text_create_layout (self, width, height);
-
- return layout;
-}
-
-/**
- * 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)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
- g_return_if_fail (color != NULL);
-
- clutter_text_set_color_animated (self, obj_props[PROP_COLOR], color);
-}
-
-/**
- * clutter_text_get_color:
- * @self: a #ClutterText
- * @color: (out caller-allocates): return location for a #ClutterColor
- *
- * Retrieves the text color as set by clutter_text_set_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_by_pspec (G_OBJECT (self), obj_props[PROP_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_by_pspec (G_OBJECT (self), obj_props[PROP_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_by_pspec (G_OBJECT (self), obj_props[PROP_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: (allow-none): a #PangoAttrList or %NULL to unset the attributes
- *
- * Sets the attributes list that are going to be applied to the
- * #ClutterText contents.
- *
- * 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 (pango_attr_list_equal (priv->attrs, attrs))
- return;
-
- if (attrs)
- pango_attr_list_ref (attrs);
-
- if (priv->attrs)
- pango_attr_list_unref (priv->attrs);
-
- priv->attrs = attrs;
-
- /* Clear the effective attributes so they will be regenerated when a
- layout is created */
- if (priv->effective_attrs)
- {
- pango_attr_list_unref (priv->effective_attrs);
- priv->effective_attrs = NULL;
- }
-
- clutter_text_queue_redraw_or_relayout (self);
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ATTRIBUTES]);
-}
-
-/**
- * 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: (transfer none): 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_line_alignment:
- * @self: a #ClutterText
- * @alignment: A #PangoAlignment
- *
- * Sets the way that the lines of a wrapped label are aligned with
- * respect to each other. This does not affect the overall alignment
- * of the label within its allocated or specified width.
- *
- * To align a #ClutterText actor you should add it to a container
- * that supports alignment, or use the anchor point.
- *
- * Since: 1.0
- */
-void
-clutter_text_set_line_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_queue_redraw_or_relayout (self);
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_ALIGNMENT]);
- }
-}
-
-/**
- * clutter_text_get_line_alignment:
- * @self: a #ClutterText
- *
- * Retrieves the alignment of a #ClutterText, as set by
- * clutter_text_set_line_alignment().
- *
- * Return value: a #PangoAlignment
- *
- * Since: 1.0
- */
-PangoAlignment
-clutter_text_get_line_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>.
- *
- * Setting #ClutterText:use-markup on an editable #ClutterText will
- * not have any effect except hiding the markup.
- *
- * See also #ClutterText:use-markup.
- *
- * Since: 1.0
- */
-void
-clutter_text_set_use_markup (ClutterText *self,
- gboolean setting)
-{
- const gchar *text;
-
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- text = clutter_text_buffer_get_text (get_buffer (self));
-
- clutter_text_set_use_markup_internal (self, setting);
-
- if (setting)
- clutter_text_set_markup_internal (self, text);
-
- clutter_text_queue_redraw_or_relayout (self);
-}
-
-/**
- * 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_queue_redraw_or_relayout (self);
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_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;
-
- if (priv->position == position)
- return;
-
- len = clutter_text_buffer_get_length (get_buffer (self));
-
- if (position < 0 || position >= len)
- priv->position = -1;
- else
- priv->position = position;
-
- /* Forget the target x position so that it will be recalculated next
- time the cursor is moved up or down */
- priv->x_pos = -1;
-
- clutter_text_queue_redraw (CLUTTER_ACTOR (self));
-
- /* XXX:2.0 - remove */
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]);
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_POSITION]);
- g_signal_emit (self, text_signals[CURSOR_CHANGED], 0);
-}
-
-/**
- * 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;
-
- clutter_text_queue_redraw (CLUTTER_ACTOR (self));
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_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_by_pspec (G_OBJECT (self), obj_props[PROP_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)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
- clutter_text_buffer_set_max_length (get_buffer (self), max);
-}
-
-/**
- * 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 clutter_text_buffer_get_max_length (get_buffer (self));
-}
-
-static void
-clutter_text_real_insert_text (ClutterText *self,
- guint start_pos,
- const gchar *chars,
- guint n_chars)
-{
- gsize n_bytes;
-
- n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars;
-
- /*
- * insert-text is emitted here instead of as part of a
- * buffer_inserted_text() callback because that should be emitted
- * before the buffer changes, while ClutterTextBuffer::deleted-text
- * is emitter after. See BG#722220 for more info.
- */
- g_signal_emit (self, text_signals[INSERT_TEXT], 0, chars,
- n_bytes, &start_pos);
-
- /*
- * The actual insertion from the buffer. This will end firing the
- * following signal handlers: buffer_inserted_text(),
- * buffer_notify_text(), buffer_notify_max_length()
- */
- clutter_text_buffer_insert_text (get_buffer (self), start_pos, chars, n_chars);
-}
-
-/**
- * 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;
-
- priv = self->priv;
-
- new = g_string_new ("");
- g_string_append_unichar (new, wc);
-
- clutter_text_real_insert_text (self, priv->position, new->str, 1);
-
- 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)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
- g_return_if_fail (text != NULL);
-
- clutter_text_real_insert_text (self, position, text, g_utf8_strlen (text, -1));
-}
-
-static
-void clutter_text_real_delete_text (ClutterText *self,
- gssize start_pos,
- gssize end_pos)
-{
- /*
- * delete-text is emitted here instead of as part of a
- * buffer_deleted_text() callback because that should be emitted
- * before the buffer changes, while ClutterTextBuffer::deleted-text
- * is emitter after. See BG#722220 for more info.
- */
- g_signal_emit (self, text_signals[DELETE_TEXT], 0, start_pos, end_pos);
-
- /*
- * The actual deletion from the buffer. This will end firing the
- * following signal handlers: buffer_deleted_text(),
- * buffer_notify_text(), buffer_notify_max_length()
- */
- clutter_text_buffer_delete_text (get_buffer (self), start_pos, end_pos - start_pos);
-}
-
-
-
-/**
- * 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)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- clutter_text_real_delete_text (self, start_pos, end_pos);
-}
-
-/**
- * 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.
- *
- * Somewhat awkwardly, the cursor position is decremented by the same
- * number of characters you've deleted.
- *
- * Since: 1.0
- */
-void
-clutter_text_delete_chars (ClutterText *self,
- guint n_chars)
-{
- ClutterTextPrivate *priv;
-
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- priv = self->priv;
-
- clutter_text_real_delete_text (self, priv->position, priv->position + n_chars);
-
- if (priv->position > 0)
- clutter_text_set_cursor_position (self, priv->position - n_chars);
-}
-
-/**
- * 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, but not including @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)
-{
- gint start_index, end_index;
- guint n_chars;
- const gchar *text;
-
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL);
-
- n_chars = clutter_text_buffer_get_length (get_buffer (self));
- text = clutter_text_buffer_get_text (get_buffer (self));
-
- if (end_pos < 0)
- end_pos = n_chars;
-
- start_pos = MIN (n_chars, start_pos);
- end_pos = MIN (n_chars, end_pos);
-
- start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
- end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
-
- return g_strndup (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. Only editable #ClutterText<!-- -->s can be in single line
- * mode.
- *
- * A text actor in single line mode will not wrap text and will clip
- * 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_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]);
- }
-
- clutter_text_dirty_cache (self);
- clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
-
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_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;
-}
-
-/**
- * clutter_text_set_preedit_string:
- * @self: a #ClutterText
- * @preedit_str: (allow-none): the pre-edit string, or %NULL to unset it
- * @preedit_attrs: (allow-none): the pre-edit string attributes
- * @cursor_pos: the cursor position for the pre-edit string
- *
- * Sets, or unsets, the pre-edit string. This function is useful
- * for input methods to display a string (with eventual specific
- * Pango attributes) before it is entered inside the #ClutterText
- * buffer.
- *
- * The preedit string and attributes are ignored if the #ClutterText
- * actor is not editable.
- *
- * This function should not be used by applications
- *
- * Since: 1.2
- */
-void
-clutter_text_set_preedit_string (ClutterText *self,
- const gchar *preedit_str,
- PangoAttrList *preedit_attrs,
- guint cursor_pos)
-{
- ClutterTextPrivate *priv;
-
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- priv = self->priv;
-
- g_free (priv->preedit_str);
- priv->preedit_str = NULL;
-
- if (priv->preedit_attrs != NULL)
- {
- pango_attr_list_unref (priv->preedit_attrs);
- priv->preedit_attrs = NULL;
- }
-
- priv->preedit_n_chars = 0;
- priv->preedit_cursor_pos = 0;
-
- if (preedit_str == NULL || *preedit_str == '\0')
- priv->preedit_set = FALSE;
- else
- {
- priv->preedit_str = g_strdup (preedit_str);
-
- if (priv->preedit_str != NULL)
- priv->preedit_n_chars = g_utf8_strlen (priv->preedit_str, -1);
- else
- priv->preedit_n_chars = 0;
-
- if (preedit_attrs != NULL)
- priv->preedit_attrs = pango_attr_list_ref (preedit_attrs);
-
- priv->preedit_cursor_pos =
- CLAMP (cursor_pos, 0, priv->preedit_n_chars);
-
- priv->preedit_set = TRUE;
- }
-
- clutter_text_queue_redraw_or_relayout (self);
-}
-
-
-/**
- * clutter_text_get_layout_offsets:
- * @self: a #ClutterText
- * @x: (out): location to store X offset of layout, or %NULL
- * @y: (out): location to store Y offset of layout, or %NULL
- *
- * Obtains the coordinates where the #ClutterText will draw the #PangoLayout
- * representing the text.
- *
- * Since: 1.8
- */
-void
-clutter_text_get_layout_offsets (ClutterText *self,
- gint *x,
- gint *y)
-{
- ClutterTextPrivate *priv;
-
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- priv = self->priv;
-
- if (x != NULL)
- *x = priv->text_logical_x;
-
- if (y != NULL)
- *y = priv->text_logical_y;
-}
-
-/**
- * clutter_text_get_cursor_rect:
- * @self: a #ClutterText
- * @rect: (out caller-allocates): return location of a #ClutterRect
- *
- * Retrieves the rectangle that contains the cursor.
- *
- * The coordinates of the rectangle's origin are in actor-relative
- * coordinates.
- *
- * Since: 1.16
- */
-void
-clutter_text_get_cursor_rect (ClutterText *self,
- graphene_rect_t *rect)
-{
- float inverse_scale;
-
- g_return_if_fail (CLUTTER_IS_TEXT (self));
- g_return_if_fail (rect != NULL);
-
- inverse_scale = 1.f / clutter_actor_get_resource_scale (CLUTTER_ACTOR (self));
-
- graphene_rect_scale (&self->priv->cursor_rect,
- inverse_scale,
- inverse_scale,
- rect);
-}
-
-void
-clutter_text_set_input_hints (ClutterText *self,
- ClutterInputContentHintFlags hints)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- self->priv->input_hints = hints;
-
- if (clutter_input_focus_is_focused (self->priv->input_focus))
- clutter_input_focus_set_content_hints (self->priv->input_focus, hints);
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_INPUT_HINTS]);
-}
-
-ClutterInputContentHintFlags
-clutter_text_get_input_hints (ClutterText *self)
-{
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
-
- return self->priv->input_hints;
-}
-
-void
-clutter_text_set_input_purpose (ClutterText *self,
- ClutterInputContentPurpose purpose)
-{
- g_return_if_fail (CLUTTER_IS_TEXT (self));
-
- self->priv->input_purpose = purpose;
-
- if (clutter_input_focus_is_focused (self->priv->input_focus))
- clutter_input_focus_set_content_purpose (self->priv->input_focus, purpose);
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_INPUT_PURPOSE]);
-}
-
-ClutterInputContentPurpose
-clutter_text_get_input_purpose (ClutterText *self)
-{
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0);
-
- return self->priv->input_purpose;
-}
-
-gboolean
-clutter_text_has_preedit (ClutterText *self)
-{
- g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE);
-
- return self->priv->preedit_set;
-}