/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * Neil Jagdish Patel priv; if (priv->entry_padding != padding) { priv->entry_padding = padding; if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (entry))) clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); g_object_notify (G_OBJECT (entry), "entry-padding"); } } static void clutter_entry_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterEntry *entry; ClutterEntryPrivate *priv; entry = CLUTTER_ENTRY (object); priv = entry->priv; switch (prop_id) { case PROP_FONT_NAME: clutter_entry_set_font_name (entry, g_value_get_string (value)); break; case PROP_TEXT: clutter_entry_set_text (entry, g_value_get_string (value)); break; case PROP_COLOR: clutter_entry_set_color (entry, g_value_get_boxed (value)); break; case PROP_ALIGNMENT: clutter_entry_set_alignment (entry, g_value_get_enum (value)); break; case PROP_POSITION: clutter_entry_set_position (entry, g_value_get_int (value)); break; case PROP_CURSOR: clutter_entry_set_visible_cursor (entry, g_value_get_boolean (value)); break; case PROP_TEXT_VISIBLE: clutter_entry_set_visibility (entry, g_value_get_boolean (value)); break; case PROP_MAX_LENGTH: clutter_entry_set_max_length (entry, g_value_get_int (value)); break; case PROP_ENTRY_PADDING: clutter_entry_set_entry_padding (entry, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_entry_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterEntry *entry; ClutterEntryPrivate *priv; ClutterColor color; entry = CLUTTER_ENTRY(object); priv = entry->priv; switch (prop_id) { case PROP_FONT_NAME: g_value_set_string (value, priv->font_name); break; case PROP_TEXT: g_value_set_string (value, priv->text); break; case PROP_COLOR: clutter_entry_get_color (entry, &color); g_value_set_boxed (value, &color); break; case PROP_ALIGNMENT: g_value_set_enum (value, priv->alignment); break; case PROP_POSITION: g_value_set_int (value, priv->position); break; case PROP_CURSOR: g_value_set_boolean (value, priv->show_cursor); break; case PROP_TEXT_VISIBLE: g_value_set_boolean (value, priv->text_visible); break; case PROP_MAX_LENGTH: g_value_set_int (value, priv->max_length); break; case PROP_ENTRY_PADDING: g_value_set_uint (value, priv->entry_padding); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_entry_ensure_layout (ClutterEntry *entry, gint width) { ClutterEntryPrivate *priv; priv = entry->priv; if (!priv->layout) { priv->layout = pango_layout_new (_context); if (priv->effective_attrs) pango_layout_set_attributes (priv->layout, priv->effective_attrs); pango_layout_set_alignment (priv->layout, priv->alignment); pango_layout_set_ellipsize (priv->layout, priv->ellipsize); pango_layout_set_single_paragraph_mode (priv->layout, priv->single_line_mode); pango_layout_set_font_description (priv->layout, priv->desc); if (priv->text_visible) pango_layout_set_text (priv->layout, priv->text, -1); else { gint len = g_utf8_strlen (priv->text, -1); gchar *invisible = g_strnfill (len, priv->priv_char); pango_layout_set_markup (priv->layout, invisible, -1); g_free (invisible); } if (priv->wrap) pango_layout_set_wrap (priv->layout, priv->wrap_mode); if (priv->wrap && width > 0) pango_layout_set_width (priv->layout, width * PANGO_SCALE); else pango_layout_set_width (priv->layout, -1); } } static void clutter_entry_clear_layout (ClutterEntry *entry) { if (entry->priv->layout) { g_object_unref (entry->priv->layout); entry->priv->layout = NULL; } } static gint offset_to_bytes (const gchar *text, gint pos) { gchar *c = NULL; gint i, j, len; if (pos < 1) return pos; c = g_utf8_next_char (text); j = 1; len = strlen (text); for (i = 0; i < len; i++) { if (&text[i] == c) { if (j == pos) break; else { c = g_utf8_next_char (c); j++; } } } return i; } static void clutter_entry_ensure_cursor_position (ClutterEntry *entry) { ClutterEntryPrivate *priv; gint index; PangoRectangle rect; priv = entry->priv; if (priv->position == -1) index = strlen (priv->text); else index = offset_to_bytes (priv->text, priv->position); pango_layout_get_cursor_pos (priv->layout, index, &rect, NULL); priv->cursor_pos.x = rect.x / PANGO_SCALE; priv->cursor_pos.y = rect.y / PANGO_SCALE; priv->cursor_pos.width = ENTRY_CURSOR_WIDTH; priv->cursor_pos.height = rect.height / PANGO_SCALE; g_signal_emit (entry, entry_signals[CURSOR_EVENT], 0, &priv->cursor_pos); } static void clutter_entry_clear_cursor_position (ClutterEntry *entry) { entry->priv->cursor_pos.width = 0; } void clutter_entry_paint_cursor (ClutterEntry *entry) { ClutterEntryPrivate *priv; priv = entry->priv; if (priv->show_cursor) { clutter_actor_set_size (CLUTTER_ACTOR (priv->cursor), priv->cursor_pos.width, priv->cursor_pos.height); clutter_actor_set_position (priv->cursor, priv->cursor_pos.x, priv->cursor_pos.y); clutter_actor_paint (priv->cursor); } } static void clutter_entry_paint (ClutterActor *self) { ClutterEntry *entry; ClutterEntryPrivate *priv; PangoRectangle logical; gint actor_width; gint text_width; gint cursor_x; entry = CLUTTER_ENTRY(self); priv = entry->priv; if (priv->desc == NULL || priv->text == NULL) { CLUTTER_NOTE (ACTOR, "layout: %p , desc: %p, text %p", priv->layout, priv->desc, priv->text); return; } clutter_actor_set_clip (self, 0, 0, clutter_actor_get_width (self), clutter_actor_get_height (self)); actor_width = clutter_actor_get_width (self) - (2 * priv->entry_padding); clutter_entry_ensure_layout (entry, actor_width); clutter_entry_ensure_cursor_position (entry); pango_layout_get_extents (priv->layout, NULL, &logical); text_width = logical.width / PANGO_SCALE; if (actor_width < text_width) { /* We need to do some scrolling */ cursor_x = priv->cursor_pos.x; /* If the cursor is at the begining or the end of the text, the placement * is easy, however, if the cursor is in the middle somewhere, we need to * make sure the text doesn't move until the cursor is either in the * far left or far right */ if (priv->position == 0) priv->text_x = 0; else if (priv->position == -1) { priv->text_x = actor_width - text_width; priv->cursor_pos.x += priv->text_x + priv->entry_padding; } else { if (priv->text_x < 0) { gint diff = -1 * priv->text_x; if (cursor_x < diff) priv->text_x += diff - cursor_x; else if (cursor_x > (diff + actor_width)) priv->text_x -= cursor_x - (diff+actor_width); } priv->cursor_pos.x += priv->text_x + priv->entry_padding; } } else { priv->text_x = 0; priv->cursor_pos.x += priv->entry_padding; } priv->fgcol.alpha = clutter_actor_get_opacity (self); pango_clutter_render_layout (priv->layout, priv->text_x + priv->entry_padding, 0, &priv->fgcol, 0); if (CLUTTER_ENTRY_GET_CLASS (entry)->paint_cursor) CLUTTER_ENTRY_GET_CLASS (entry)->paint_cursor (entry); } static void clutter_entry_request_coords (ClutterActor *self, ClutterActorBox *box) { /* do we need to do anything ? */ clutter_entry_clear_layout (CLUTTER_ENTRY (self)); } static void clutter_entry_dispose (GObject *object) { ClutterEntry *self = CLUTTER_ENTRY(object); ClutterEntryPrivate *priv; priv = self->priv; if (priv->layout) { g_object_unref (priv->layout); priv->layout = NULL; } if (priv->context) { g_object_unref (priv->context); priv->context = NULL; } G_OBJECT_CLASS (clutter_entry_parent_class)->dispose (object); } static void clutter_entry_finalize (GObject *object) { ClutterEntryPrivate *priv = CLUTTER_ENTRY (object)->priv; if (priv->desc) pango_font_description_free (priv->desc); g_free (priv->text); g_free (priv->font_name); G_OBJECT_CLASS (clutter_entry_parent_class)->finalize (object); } static void clutter_entry_class_init (ClutterEntryClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); klass->paint_cursor = clutter_entry_paint_cursor; actor_class->paint = clutter_entry_paint; actor_class->request_coords = clutter_entry_request_coords; gobject_class->finalize = clutter_entry_finalize; gobject_class->dispose = clutter_entry_dispose; gobject_class->set_property = clutter_entry_set_property; gobject_class->get_property = clutter_entry_get_property; /** * ClutterEntry:font-name: * * The font to be used by the entry, expressed in a string that * can be parsed by pango_font_description_from_string(). * * Since: 0.4 */ g_object_class_install_property (gobject_class, PROP_FONT_NAME, g_param_spec_string ("font-name", "Font Name", "Pango font description", NULL, G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE)); /** * ClutterEntry:text: * * The text inside the entry. * * Since: 0.4 */ g_object_class_install_property (gobject_class, PROP_TEXT, g_param_spec_string ("text", "Text", "Text to render", NULL, G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE)); /** * ClutterEntry:color: * * The color of the text inside the entry. * * Since: 0.4 */ g_object_class_install_property (gobject_class, PROP_COLOR, g_param_spec_boxed ("color", "Font Colour", "Font Colour", CLUTTER_TYPE_COLOR, CLUTTER_PARAM_READWRITE)); /** * ClutterEntry:alignment: * * The preferred alignment for the string. * * Since: 0.4 */ g_object_class_install_property (gobject_class, PROP_ALIGNMENT, g_param_spec_enum ("alignment", "Alignment", "The preferred alignment for the string,", PANGO_TYPE_ALIGNMENT, PANGO_ALIGN_LEFT, CLUTTER_PARAM_READWRITE)); /** * ClutterEntry:position: * * The current input cursor position. -1 is taken to be the end of the text * * Since: 0.4 */ g_object_class_install_property (gobject_class, PROP_POSITION, g_param_spec_int ("position", "Position", "The cursor position", -1, G_MAXINT, -1, CLUTTER_PARAM_READWRITE)); /** * ClutterEntry:cursor-visible: * * Whether the input cursor is visible or not. * * Since: 0.4 */ g_object_class_install_property (gobject_class, PROP_CURSOR, g_param_spec_boolean ( "cursor-visible", "Cursor Visible", "Whether the input cursor is visible", TRUE, CLUTTER_PARAM_READWRITE)); /** * ClutterEntry:text-visible: * * Whether the text is visible in plain, or replaced by the * character set by clutter_entry_set_invisible_char(). * * Since: 0.4 */ g_object_class_install_property (gobject_class, PROP_TEXT_VISIBLE, g_param_spec_boolean ("text-visible", "Text Visible", "Whether the text is visible in plain text", TRUE, CLUTTER_PARAM_READWRITE)); /** * ClutterEntry:max-length: * * The maximum length of the entry text. * * Since: 0.4 */ g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, g_param_spec_int ("max-length", "Max Length", "The maximum length of the entry text", 0, G_MAXINT, 0, CLUTTER_PARAM_READWRITE)); /** * ClutterEntry:entry-padding: * * The padding space between the text and the entry right and left borders. * * Since: 0.4 */ g_object_class_install_property (gobject_class, PROP_ENTRY_PADDING, g_param_spec_uint ("entry-padding", "Entry Padding", "The padding space between the text and the left and " "right borders", 0, G_MAXUINT, ENTRY_PADDING, CLUTTER_PARAM_READWRITE)); /** * ClutterEntry::text-changed: * @entry: the actor which received the event * * The ::text-changed signal is emitted after the @entrys text changes */ entry_signals[TEXT_CHANGED] = g_signal_new ("text-changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterEntryClass, text_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * ClutterEntry::cursor-event: * @entry: the actor which received the event * @geometry: a #ClutterGeometry * * The ::cursor-event signal is emitted each time the input cursors geometry * changes, this could be a positional or size change. If you would like to * implement your own input cursor, set the cursor-visible property to FALSE, * and connect to this signal to position and size your own cursor. * * Since: 0.4 */ entry_signals[CURSOR_EVENT] = g_signal_new ("cursor-event", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterEntryClass, cursor_event), NULL, NULL, clutter_marshal_VOID__BOXED, G_TYPE_NONE, 1, CLUTTER_TYPE_GEOMETRY | G_SIGNAL_TYPE_STATIC_SCOPE); /** * ClutterEntry::activate: * @entry: the actor which received the event * * The ::activate signal is emitted each time the netry is 'activated' * by the user, normally by pressing the 'Enter' key. This signal will * only be emitted when your are adding text to the entry via * #clutter_entry_handle_key_event(). * * Since: 0.4 */ entry_signals[ACTIVATE] = g_signal_new ("activate", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterEntryClass, activate), NULL, NULL, clutter_marshal_VOID__VOID, G_TYPE_NONE, 0); g_type_class_add_private (gobject_class, sizeof (ClutterEntryPrivate)); } static void clutter_entry_init (ClutterEntry *self) { ClutterEntryPrivate *priv; gdouble resolution; gint font_size; self->priv = priv = CLUTTER_ENTRY_GET_PRIVATE (self); resolution = clutter_backend_get_resolution (clutter_get_default_backend ()); if (resolution < 0) resolution = 96.0; /* fall back */ if (G_UNLIKELY (_context == NULL)) { _font_map = PANGO_CLUTTER_FONT_MAP (pango_clutter_font_map_new ()); /* pango_clutter_font_map_set_resolution (font_map, 96.0, 96.0); */ _context = pango_clutter_font_map_create_context (_font_map); } 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->layout = NULL; priv->text = NULL; priv->attrs = NULL; priv->position = -1; priv->priv_char = '*'; priv->text_visible = TRUE; priv->text_x = 0; priv->max_length = 0; priv->entry_padding = ENTRY_PADDING; priv->fgcol.red = 0; priv->fgcol.green = 0; priv->fgcol.blue = 0; priv->fgcol.alpha = 255; priv->font_name = g_strdup (DEFAULT_FONT_NAME); priv->desc = pango_font_description_from_string (priv->font_name); /* we use the font size to set the default width and height, in case * the user doesn't call clutter_actor_set_size(). */ font_size = PANGO_PIXELS (pango_font_description_get_size (priv->desc)) * resolution / 72.0; clutter_actor_set_size (CLUTTER_ACTOR (self), font_size * 20, 50); priv->cursor = clutter_rectangle_new_with_color (&priv->fgcol); clutter_actor_set_parent (priv->cursor, CLUTTER_ACTOR (self)); priv->show_cursor = TRUE; } /** * clutter_entry_new_with_text: * @font_name: the name (and size) of the font to be used * @text: the text to be displayed * * Creates a new #ClutterEntry displaying @text using @font_name. * * Return value: the newly created #ClutterEntry * * Since: 0.4 */ ClutterActor * clutter_entry_new_with_text (const gchar *font_name, const gchar *text) { ClutterActor *entry = clutter_entry_new (); g_object_set (entry, "font-name", font_name, "text", text, NULL); return entry; } /** * clutter_entry_new_full: * @font_name: the name (and size) of the font to be used * @text: the text to be displayed * @color: #ClutterColor for text * * Creates a new #ClutterEntry displaying @text with color @color * using @font_name. * * Return value: the newly created #ClutterEntry * * Since: 0.4 */ ClutterActor * clutter_entry_new_full (const gchar *font_name, const gchar *text, const ClutterColor *color) { ClutterActor *entry; entry = clutter_entry_new_with_text (font_name, text); clutter_entry_set_color (CLUTTER_ENTRY(entry), color); return entry; } /** * clutter_entry_new: * * Creates a new, empty #ClutterEntry. * * Returns: the newly created #ClutterEntry */ ClutterActor * clutter_entry_new (void) { ClutterActor *entry = g_object_new (CLUTTER_TYPE_ENTRY, NULL); clutter_actor_set_size (entry, 50, 50); return entry; } /** * clutter_entry_get_text: * @entry: a #ClutterEntry * * Retrieves the text displayed by @entry * * Return value: the text of the entry. The returned string is * owned by #ClutterEntry and should not be modified or freed. * * Since: 0.4 */ G_CONST_RETURN gchar * clutter_entry_get_text (ClutterEntry *entry) { g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); return entry->priv->text; } /** * clutter_entry_set_text: * @entry: a #ClutterEntry * @text: the text to be displayed * * Sets @text as the text to be displayed by @entry. The * ClutterEntry::text-changed signal is emitted. * * Since: 0.4 */ void clutter_entry_set_text (ClutterEntry *entry, const gchar *text) { ClutterEntryPrivate *priv; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; g_object_ref (entry); if (priv->max_length > 0) { gint len = g_utf8_strlen (text, -1); if (len < priv->max_length) { g_free (priv->text); priv->text = g_strdup (text); } else { gchar new[priv->max_length + 1]; g_utf8_strncpy (new, text, priv->max_length); g_free (priv->text); priv->text = g_strdup (new); } } else { g_free (priv->text); priv->text = g_strdup (text); } clutter_entry_clear_layout (entry); clutter_entry_clear_cursor_position (entry); if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR(entry))) clutter_actor_queue_redraw (CLUTTER_ACTOR(entry)); g_signal_emit (G_OBJECT (entry), entry_signals[TEXT_CHANGED], 0); g_object_notify (G_OBJECT (entry), "text"); g_object_unref (entry); } /** * clutter_entry_get_font_name: * @entry: a #ClutterEntry * * Retrieves the font used by @entry. * * Return value: a string containing the font name, in a format * understandable by pango_font_description_from_string(). The * string is owned by #ClutterEntry and should not be modified * or freed. * * Since: 0.4 */ G_CONST_RETURN gchar * clutter_entry_get_font_name (ClutterEntry *entry) { g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); return entry->priv->font_name; } /** * clutter_entry_set_font_name: * @entry: a #ClutterEntry * @font_name: a font name and size, or %NULL for the default font * * Sets @font_name as the font used by @entry. * * @font_name must be a string containing the font name and its * size, similarly to what you would feed to the * pango_font_description_from_string() function. * * Since: 0.4 */ void clutter_entry_set_font_name (ClutterEntry *entry, const gchar *font_name) { ClutterEntryPrivate *priv; PangoFontDescription *desc; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); if (!font_name || font_name[0] == '\0') font_name = DEFAULT_FONT_NAME; priv = entry->priv; if (strcmp (priv->font_name, font_name) == 0) return; desc = pango_font_description_from_string (font_name); if (!desc) { g_warning ("Attempting to create a PangoFontDescription for " "font name `%s', but failed.", font_name); return; } g_object_ref (entry); g_free (priv->font_name); priv->font_name = g_strdup (font_name); if (priv->desc) pango_font_description_free (priv->desc); priv->desc = desc; if (entry->priv->text && entry->priv->text[0] != '\0') { clutter_entry_clear_layout (entry); if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (entry))) clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); } g_object_notify (G_OBJECT (entry), "font-name"); g_object_unref (entry); } /** * clutter_entry_set_color: * @entry: a #ClutterEntry * @color: a #ClutterColor * * Sets the color of @entry. * * Since: 0.4 */ void clutter_entry_set_color (ClutterEntry *entry, const ClutterColor *color) { ClutterActor *actor; ClutterEntryPrivate *priv; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); g_return_if_fail (color != NULL); priv = entry->priv; g_object_ref (entry); priv->fgcol.red = color->red; priv->fgcol.green = color->green; priv->fgcol.blue = color->blue; priv->fgcol.alpha = color->alpha; actor = CLUTTER_ACTOR (entry); clutter_actor_set_opacity (actor, priv->fgcol.alpha); clutter_rectangle_set_color (CLUTTER_RECTANGLE (priv->cursor), &priv->fgcol); if (CLUTTER_ACTOR_IS_VISIBLE (actor)) clutter_actor_queue_redraw (actor); g_object_notify (G_OBJECT (entry), "color"); g_object_unref (entry); } /** * clutter_entry_get_color: * @entry: a #ClutterEntry * @color: return location for a #ClutterColor * * Retrieves the color of @entry. * * Since: 0.4 */ void clutter_entry_get_color (ClutterEntry *entry, ClutterColor *color) { ClutterEntryPrivate *priv; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); g_return_if_fail (color != NULL); priv = entry->priv; color->red = priv->fgcol.red; color->green = priv->fgcol.green; color->blue = priv->fgcol.blue; color->alpha = priv->fgcol.alpha; } /** * clutter_entry_get_layout: * @entry: a #ClutterEntry * * Gets the #PangoLayout used to display the entry. * The layout is useful to e.g. convert text positions to * pixel positions. * The returned layout is owned by the entry so need not be * freed by the caller. * * Return value: the #PangoLayout for this entry * * Since: 0.4 **/ PangoLayout * clutter_entry_get_layout (ClutterEntry *entry) { g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), NULL); clutter_entry_ensure_layout (entry, -1); return entry->priv->layout; } /** * clutter_entry_set_alignment: * @entry: a #ClutterEntry * @alignment: A #PangoAlignment * * Sets text alignment of the entry. * * Since: 0.4 */ void clutter_entry_set_alignment (ClutterEntry *entry, PangoAlignment alignment) { ClutterEntryPrivate *priv; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; if (priv->alignment != alignment) { g_object_ref (entry); priv->alignment = alignment; clutter_entry_clear_layout (entry); if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (entry))) clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); g_object_notify (G_OBJECT (entry), "alignment"); g_object_unref (entry); } } /** * clutter_entry_get_alignment: * @entry: a #ClutterEntry * * Returns the entry's text alignment * * Return value: The entrys #PangoAlignment * * Since 0.4 */ PangoAlignment clutter_entry_get_alignment (ClutterEntry *entry) { g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), FALSE); return entry->priv->alignment; } /** * clutter_entry_set_position: * @entry: a #ClutterEntry * @position: the position of the cursor. * * Sets the position of the cursor. The @position must be less than or * equal to the number of characters in the entry. A value of -1 indicates * that the position should be set after the last character in the entry. * Note that this position is in characters, not in bytes. * * Since: 0.4 */ void clutter_entry_set_position (ClutterEntry *entry, gint position) { ClutterEntryPrivate *priv; gint len; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; if (priv->text == NULL) return; len = g_utf8_strlen (priv->text, -1); if (position < 0 || position >= len) priv->position = -1; else priv->position = position; clutter_entry_clear_cursor_position (entry); if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (entry))) clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); } /** * clutter_entry_get_position: * @entry: a #ClutterEntry * * Gets the position, in characters, of the cursor in @entry. * * Return value: the position of the cursor. * * Since: 0.4 */ gint clutter_entry_get_position (ClutterEntry *entry) { ClutterEntryPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), 0); priv = entry->priv; return priv->position; } /** * clutter_entry_handle_key_event: * @entry: a #ClutterEntry * @kev: a #ClutterKeyEvent * * This function will handle a #ClutterKeyEvent, like those returned in a * key-press/release-event, and will translate it for the @entry. This includes * non-alphanumeric keys, such as the arrows keys, which will move the * input cursor. You should use this function inside a handler for the * ClutterStage::key-press-event or ClutterStage::key-release-event. * * Since: 0.4 */ void clutter_entry_handle_key_event (ClutterEntry *entry, ClutterKeyEvent *kev) { ClutterEntryPrivate *priv; gint pos = 0; gint len = 0; gint keyval = clutter_key_event_symbol (kev); g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; pos = priv->position; if (priv->text) len = g_utf8_strlen (priv->text, -1); switch (keyval) { case CLUTTER_Return: case CLUTTER_KP_Enter: case CLUTTER_ISO_Enter: g_signal_emit (entry, entry_signals[ACTIVATE], 0); break; case CLUTTER_Escape: case CLUTTER_Up: case CLUTTER_KP_Up: case CLUTTER_Down: case CLUTTER_KP_Down: case CLUTTER_Shift_L: case CLUTTER_Shift_R: break; case CLUTTER_BackSpace: if (pos != 0 && len != 0) clutter_entry_delete_chars (entry, 1); break; case CLUTTER_Delete: case CLUTTER_KP_Delete: if (len && pos != -1) clutter_entry_delete_text (entry, pos, pos+1);; break; case CLUTTER_Left: case CLUTTER_KP_Left: if (pos != 0 && len != 0) { if (pos == -1) clutter_entry_set_position (entry, len - 1); else clutter_entry_set_position (entry, pos - 1); } break; case CLUTTER_Right: case CLUTTER_KP_Right: if (pos != -1 && len != 0) { if (pos != len) clutter_entry_set_position (entry, pos + 1); } break; case CLUTTER_End: case CLUTTER_KP_End: clutter_entry_set_position (entry, -1); break; case CLUTTER_Begin: case CLUTTER_Home: case CLUTTER_KP_Home: clutter_entry_set_position (entry, 0); break; default: clutter_entry_insert_unichar (entry, clutter_keysym_to_unicode (keyval)); break; } } /** * clutter_entry_insert_unichar: * @entry: a #ClutterEntry * @wc: a Unicode character * * Insert a character to the right of the current position of the cursor, * and updates the position of the cursor. * * Since: 0.4 */ void clutter_entry_insert_unichar (ClutterEntry *entry, gunichar wc) { ClutterEntryPrivate *priv; GString *new = NULL; glong pos; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); g_return_if_fail (g_unichar_validate (wc)); if (wc == 0) return; priv = entry->priv; g_object_ref (entry); new = g_string_new (priv->text); pos = offset_to_bytes (priv->text, priv->position); new = g_string_insert_unichar (new, pos, wc); clutter_entry_set_text (entry, new->str); if (priv->position >= 0) clutter_entry_set_position (entry, priv->position + 1); g_string_free (new, TRUE); g_object_notify (G_OBJECT (entry), "text"); g_object_unref (entry); } /** * clutter_entry_delete_chars: * @entry: a #ClutterEntry * @len: the number of characters to remove. * * Characters are removed from before the current postion of the cursor. * * Since: 0.4 */ void clutter_entry_delete_chars (ClutterEntry *entry, guint num) { ClutterEntryPrivate *priv; GString *new = NULL; gint len; gint pos; gint num_pos; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; if (!priv->text) return; g_object_ref (entry); len = g_utf8_strlen (priv->text, -1); new = g_string_new (priv->text); if (priv->position == -1) { num_pos = offset_to_bytes (priv->text, len - num); new = g_string_erase (new, num_pos, -1); } else { pos = offset_to_bytes (priv->text, priv->position - num); num_pos = offset_to_bytes (priv->text, priv->position); new = g_string_erase (new, pos, num_pos-pos); } clutter_entry_set_text (entry, new->str); if (priv->position > 0) clutter_entry_set_position (entry, priv->position - num); g_string_free (new, TRUE); g_object_notify (G_OBJECT (entry), "text"); g_object_unref (entry); } /** * clutter_entry_insert_text: * @entry: a #ClutterEntry * @text: the text to insert * @position: the position at which to insert the text. * * Insert text at a specifc position. * * A value of 0 indicates that the text will be inserted before the first * character in the entrys text, and a value of -1 indicates that the text * will be inserted after the last character in the entrys text. * * Since: 0.4 */ void clutter_entry_insert_text (ClutterEntry *entry, const gchar *text, gssize position) { ClutterEntryPrivate *priv; GString *new = NULL; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; new = g_string_new (priv->text); new = g_string_insert (new, position, text); clutter_entry_set_text (entry, new->str); g_string_free (new, TRUE); } /** * clutter_entry_delete_text: * @entry: a #ClutterEntry * @start_pos: the starting position. * @end_pos: the end position. * * Deletes a sequence of characters. The characters that are deleted are * those characters at positions from @start_pos up to, but not including, * @end_pos. If @end_pos is negative, then the characters deleted will be * those characters from @start_pos to the end of the text. * * Since: 0.4 */ void clutter_entry_delete_text (ClutterEntry *entry, gssize start_pos, gssize end_pos) { ClutterEntryPrivate *priv; GString *new = NULL; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; if (!priv->text) return; new = g_string_new (priv->text); new = g_string_erase (new, start_pos, end_pos - start_pos); clutter_entry_set_text (entry, new->str); g_string_free (new, TRUE); } /** * clutter_entry_set_visible_cursor: * @entry: a #ClutterEntry * @visible: whether the input cursor should be visible * * Sets the visibility of the input cursor. * * Since: 0.4 */ void clutter_entry_set_visible_cursor (ClutterEntry *entry, gboolean visible) { ClutterEntryPrivate *priv; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; if (priv->show_cursor != visible) { priv->show_cursor = visible; g_object_notify (G_OBJECT (entry), "cursor-visible"); if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (entry))) clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); } } /** * clutter_entry_get_visible_cursor: * @entry: a #ClutterEntry * * Returns the input cursors visiblity * * Return value: whether the input cursor is visible * * Since: 0.4 */ gboolean clutter_entry_get_visible_cursor (ClutterEntry *entry) { g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), FALSE); return entry->priv->show_cursor; } /** * clutter_entry_set_visibility: * @entry: a #ClutterEntry * @visible: TRUE if the contents of the entry are displayed as plaintext. * * Sets whether the contents of the entry are visible or not. When visibility * is set to FALSE, characters are displayed as the invisible char, and will * also appear that way when the text in the entry widget is copied elsewhere. * * The default invisible char is the asterisk '*', but it can be changed with * #clutter_entry_set_invisible_char(). * * Since: 0.4 */ void clutter_entry_set_visibility (ClutterEntry *entry, gboolean visible) { ClutterEntryPrivate *priv; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; priv->text_visible = visible; clutter_entry_clear_layout (entry); clutter_entry_clear_cursor_position (entry); if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (entry))) clutter_actor_queue_redraw (CLUTTER_ACTOR (entry)); } /** * clutter_entry_get_visibility: * @entry: a #ClutterEntry * * Returns the entry text visiblity * * Return value: TRUE if the contents of the entry are displayed as plaintext. * * Since: 0.4 */ gboolean clutter_entry_get_visibility (ClutterEntry *entry) { g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), TRUE); return entry->priv->text_visible; } /** * clutter_entry_set_invisible_char: * @entry: a #ClutterEntry * @wc: a Unicode character * * Sets the character to use in place of the actual text when * #clutter_entry_set_visibility() has been called to set text visibility * to FALSE. i.e. this is the character used in "password mode" to show the * user how many characters have been typed. The default invisible char is an * asterisk ('*'). If you set the invisible char to 0, then the user will get * no feedback at all; there will be no text on the screen as they type. * * Since: 0.4 */ void clutter_entry_set_invisible_char (ClutterEntry *entry, gunichar wc) { ClutterEntryPrivate *priv; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; priv->priv_char = wc; if (!priv->text_visible) return; clutter_entry_clear_layout (entry); clutter_entry_clear_cursor_position (entry); if (CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR(entry))) clutter_actor_queue_redraw (CLUTTER_ACTOR(entry)); } /** * clutter_entry_get_invisible_char: * @entry: a #ClutterEntry * * Returns the character to use in place of the actual text when text-visibility * is set to FALSE * * Return value: a Unicode character * **/ gunichar clutter_entry_get_invisible_char (ClutterEntry *entry) { ClutterEntryPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), TRUE); priv = entry->priv; return priv->priv_char; } /** * clutter_entry_set_max_length: * @entry: a #ClutterEntry * @max: the maximum number of characters allowed in the entry, or -1 * to disable * * 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: 0.4 */ void clutter_entry_set_max_length (ClutterEntry *entry, gint max) { ClutterEntryPrivate *priv; gchar *new = NULL; g_return_if_fail (CLUTTER_IS_ENTRY (entry)); priv = entry->priv; if (priv->max_length != max) { g_object_ref (entry); if (max < 0) max = g_utf8_strlen (priv->text, -1); priv->max_length = max; new = g_strdup (priv->text); clutter_entry_set_text (entry, new); g_free (new); g_object_notify (G_OBJECT (entry), "max-length"); g_object_unref (entry); } } /** * clutter_entry_get_max_length: * @entry: a #ClutterEntry * * Gets the maximum length of text that can be set into @entry. * See clutter_entry_set_max_length(). * * Return value: the maximum number of characters. * * Since: 0.4 */ gint clutter_entry_get_max_length (ClutterEntry *entry) { g_return_val_if_fail (CLUTTER_IS_ENTRY (entry), -1); return entry->priv->max_length; }