1 /* GAIL - The GNOME Accessibility Implementation Library
2 * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 #include "gtkentryaccessible.h"
23 #include "gtkentryprivate.h"
24 #include "gtkcomboboxaccessible.h"
26 struct _GtkEntryAccessiblePrivate
34 static void insert_text_cb (GtkEditable *editable,
38 static void delete_text_cb (GtkEditable *editable,
42 static gboolean check_for_selection_change (GtkEntryAccessible *entry,
46 static void atk_editable_text_interface_init (AtkEditableTextIface *iface);
47 static void atk_text_interface_init (AtkTextIface *iface);
48 static void atk_action_interface_init (AtkActionIface *iface);
51 G_DEFINE_TYPE_WITH_CODE (GtkEntryAccessible, _gtk_entry_accessible, GTK_TYPE_WIDGET_ACCESSIBLE,
52 G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init)
53 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init)
54 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init))
58 gtk_entry_accessible_ref_state_set (AtkObject *accessible)
60 AtkStateSet *state_set;
64 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
68 state_set = ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->ref_state_set (accessible);
70 g_object_get (G_OBJECT (widget), "editable", &value, NULL);
72 atk_state_set_add_state (state_set, ATK_STATE_EDITABLE);
73 atk_state_set_add_state (state_set, ATK_STATE_SINGLE_LINE);
78 static AtkAttributeSet *
79 gtk_entry_accessible_get_attributes (AtkObject *accessible)
82 AtkAttributeSet *attributes;
83 AtkAttribute *placeholder_text;
86 attributes = ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->get_attributes (accessible);
88 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
92 text = gtk_entry_get_placeholder_text (GTK_ENTRY (widget));
96 placeholder_text = g_malloc (sizeof (AtkAttribute));
97 placeholder_text->name = g_strdup ("placeholder-text");
98 placeholder_text->value = g_strdup (text);
100 attributes = g_slist_append (attributes, placeholder_text);
106 gtk_entry_accessible_initialize (AtkObject *obj,
110 GtkEntryAccessible *gtk_entry_accessible;
111 gint start_pos, end_pos;
113 ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->initialize (obj, data);
115 gtk_entry_accessible = GTK_ENTRY_ACCESSIBLE (obj);
117 entry = GTK_ENTRY (data);
118 gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos);
119 gtk_entry_accessible->priv->cursor_position = end_pos;
120 gtk_entry_accessible->priv->selection_bound = start_pos;
122 /* Set up signal callbacks */
123 g_signal_connect (entry, "insert-text", G_CALLBACK (insert_text_cb), NULL);
124 g_signal_connect (entry, "delete-text", G_CALLBACK (delete_text_cb), NULL);
126 if (gtk_entry_get_visibility (entry))
127 obj->role = ATK_ROLE_TEXT;
129 obj->role = ATK_ROLE_PASSWORD_TEXT;
133 gtk_entry_accessible_notify_gtk (GObject *obj,
139 GtkEntryAccessible* entry;
141 widget = GTK_WIDGET (obj);
142 atk_obj = gtk_widget_get_accessible (widget);
143 gtk_entry = GTK_ENTRY (widget);
144 entry = GTK_ENTRY_ACCESSIBLE (atk_obj);
146 if (g_strcmp0 (pspec->name, "cursor-position") == 0)
148 if (check_for_selection_change (entry, gtk_entry))
149 g_signal_emit_by_name (atk_obj, "text-selection-changed");
151 * The entry cursor position has moved so generate the signal.
153 g_signal_emit_by_name (atk_obj, "text-caret-moved",
154 entry->priv->cursor_position);
156 else if (g_strcmp0 (pspec->name, "selection-bound") == 0)
158 if (check_for_selection_change (entry, gtk_entry))
159 g_signal_emit_by_name (atk_obj, "text-selection-changed");
161 else if (g_strcmp0 (pspec->name, "editable") == 0)
165 g_object_get (obj, "editable", &value, NULL);
166 atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, value);
168 else if (g_strcmp0 (pspec->name, "visibility") == 0)
173 visibility = gtk_entry_get_visibility (gtk_entry);
174 new_role = visibility ? ATK_ROLE_TEXT : ATK_ROLE_PASSWORD_TEXT;
175 atk_object_set_role (atk_obj, new_role);
178 GTK_WIDGET_ACCESSIBLE_CLASS (_gtk_entry_accessible_parent_class)->notify_gtk (obj, pspec);
182 gtk_entry_accessible_get_index_in_parent (AtkObject *accessible)
185 * If the parent widget is a combo box then the index is 1
186 * otherwise do the normal thing.
188 if (accessible->accessible_parent)
189 if (GTK_IS_COMBO_BOX_ACCESSIBLE (accessible->accessible_parent))
192 return ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->get_index_in_parent (accessible);
196 _gtk_entry_accessible_class_init (GtkEntryAccessibleClass *klass)
198 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
199 GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
201 class->ref_state_set = gtk_entry_accessible_ref_state_set;
202 class->get_index_in_parent = gtk_entry_accessible_get_index_in_parent;
203 class->initialize = gtk_entry_accessible_initialize;
204 class->get_attributes = gtk_entry_accessible_get_attributes;
206 widget_class->notify_gtk = gtk_entry_accessible_notify_gtk;
208 g_type_class_add_private (klass, sizeof (GtkEntryAccessiblePrivate));
212 _gtk_entry_accessible_init (GtkEntryAccessible *entry)
214 entry->priv = G_TYPE_INSTANCE_GET_PRIVATE (entry,
215 GTK_TYPE_ENTRY_ACCESSIBLE,
216 GtkEntryAccessiblePrivate);
217 entry->priv->cursor_position = 0;
218 entry->priv->selection_bound = 0;
222 gtk_entry_accessible_get_text (AtkText *atk_text,
228 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
232 return _gtk_entry_get_display_text (GTK_ENTRY (widget), start_pos, end_pos);
236 gtk_entry_accessible_get_text_before_offset (AtkText *text,
238 AtkTextBoundary boundary_type,
244 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
248 return _gtk_pango_get_text_before (gtk_entry_get_layout (GTK_ENTRY (widget)),
249 boundary_type, offset,
250 start_offset, end_offset);
254 gtk_entry_accessible_get_text_at_offset (AtkText *text,
256 AtkTextBoundary boundary_type,
262 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
266 return _gtk_pango_get_text_at (gtk_entry_get_layout (GTK_ENTRY (widget)),
267 boundary_type, offset,
268 start_offset, end_offset);
272 gtk_entry_accessible_get_text_after_offset (AtkText *text,
274 AtkTextBoundary boundary_type,
280 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
284 return _gtk_pango_get_text_after (gtk_entry_get_layout (GTK_ENTRY (widget)),
285 boundary_type, offset,
286 start_offset, end_offset);
290 gtk_entry_accessible_get_character_count (AtkText *atk_text)
296 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
300 text = _gtk_entry_get_display_text (GTK_ENTRY (widget), 0, -1);
305 char_count = g_utf8_strlen (text, -1);
313 gtk_entry_accessible_get_caret_offset (AtkText *text)
317 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
321 return gtk_editable_get_position (GTK_EDITABLE (widget));
325 gtk_entry_accessible_set_caret_offset (AtkText *text,
330 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
334 gtk_editable_set_position (GTK_EDITABLE (widget), offset);
339 static AtkAttributeSet *
340 add_text_attribute (AtkAttributeSet *attributes,
341 AtkTextAttribute attr,
346 at = g_new (AtkAttribute, 1);
347 at->name = g_strdup (atk_text_attribute_get_name (attr));
348 at->value = g_strdup (atk_text_attribute_get_value (attr, i));
350 return g_slist_prepend (attributes, at);
353 static AtkAttributeSet *
354 gtk_entry_accessible_get_run_attributes (AtkText *text,
360 AtkAttributeSet *attributes;
362 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
367 attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
368 gtk_widget_get_direction (widget));
369 attributes = _gtk_pango_get_run_attributes (attributes,
370 gtk_entry_get_layout (GTK_ENTRY (widget)),
378 static AtkAttributeSet *
379 gtk_entry_accessible_get_default_attributes (AtkText *text)
382 AtkAttributeSet *attributes;
384 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
389 attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
390 gtk_widget_get_direction (widget));
391 attributes = _gtk_pango_get_default_attributes (attributes,
392 gtk_entry_get_layout (GTK_ENTRY (widget)));
393 attributes = _gtk_style_context_get_attributes (attributes,
394 gtk_widget_get_style_context (widget),
395 gtk_widget_get_state_flags (widget));
401 gtk_entry_accessible_get_character_extents (AtkText *text,
411 PangoRectangle char_rect;
413 gint index, x_layout, y_layout;
415 gint x_window, y_window;
417 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
421 entry = GTK_ENTRY (widget);
423 gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout);
424 entry_text = _gtk_entry_get_display_text (entry, 0, -1);
425 index = g_utf8_offset_to_pointer (entry_text, offset) - entry_text;
428 pango_layout_index_to_pos (gtk_entry_get_layout (entry), index, &char_rect);
429 pango_extents_to_pixels (&char_rect, NULL);
431 window = gtk_widget_get_window (widget);
432 gdk_window_get_origin (window, &x_window, &y_window);
434 *x = x_window + x_layout + char_rect.x;
435 *y = y_window + y_layout + char_rect.y;
436 *width = char_rect.width;
437 *height = char_rect.height;
439 if (coords == ATK_XY_WINDOW)
441 window = gdk_window_get_toplevel (window);
442 gdk_window_get_origin (window, &x_window, &y_window);
450 gtk_entry_accessible_get_offset_at_point (AtkText *atk_text,
458 gint index, x_layout, y_layout;
459 gint x_window, y_window;
460 gint x_local, y_local;
464 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
468 entry = GTK_ENTRY (widget);
470 gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout);
472 window = gtk_widget_get_window (widget);
473 gdk_window_get_origin (window, &x_window, &y_window);
475 x_local = x - x_layout - x_window;
476 y_local = y - y_layout - y_window;
478 if (coords == ATK_XY_WINDOW)
480 window = gdk_window_get_toplevel (window);
481 gdk_window_get_origin (window, &x_window, &y_window);
486 if (!pango_layout_xy_to_index (gtk_entry_get_layout (entry),
487 x_local * PANGO_SCALE,
488 y_local * PANGO_SCALE,
491 if (x_local < 0 || y_local < 0)
500 text = _gtk_entry_get_display_text (entry, 0, -1);
501 offset = g_utf8_pointer_to_offset (text, text + index);
509 gtk_entry_accessible_get_n_selections (AtkText *text)
514 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
518 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
525 gtk_entry_accessible_get_selection (AtkText *text,
532 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
536 if (selection_num != 0)
539 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), start_pos, end_pos))
540 return gtk_editable_get_chars (GTK_EDITABLE (widget), *start_pos, *end_pos);
546 gtk_entry_accessible_add_selection (AtkText *text,
554 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
558 entry = GTK_ENTRY (widget);
560 if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
562 gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
570 gtk_entry_accessible_remove_selection (AtkText *text,
576 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
580 if (selection_num != 0)
583 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
585 gtk_editable_select_region (GTK_EDITABLE (widget), end, end);
593 gtk_entry_accessible_set_selection (AtkText *text,
601 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
605 if (selection_num != 0)
608 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
610 gtk_editable_select_region (GTK_EDITABLE (widget), start_pos, end_pos);
618 gtk_entry_accessible_get_character_at_offset (AtkText *atk_text,
628 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
632 if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
635 text = _gtk_entry_get_display_text (GTK_ENTRY (widget), 0, -1);
636 if (offset < g_utf8_strlen (text, -1))
638 index = g_utf8_offset_to_pointer (text, offset);
639 result = g_utf8_get_char (index);
647 atk_text_interface_init (AtkTextIface *iface)
649 iface->get_text = gtk_entry_accessible_get_text;
650 iface->get_character_at_offset = gtk_entry_accessible_get_character_at_offset;
651 iface->get_text_before_offset = gtk_entry_accessible_get_text_before_offset;
652 iface->get_text_at_offset = gtk_entry_accessible_get_text_at_offset;
653 iface->get_text_after_offset = gtk_entry_accessible_get_text_after_offset;
654 iface->get_caret_offset = gtk_entry_accessible_get_caret_offset;
655 iface->set_caret_offset = gtk_entry_accessible_set_caret_offset;
656 iface->get_character_count = gtk_entry_accessible_get_character_count;
657 iface->get_n_selections = gtk_entry_accessible_get_n_selections;
658 iface->get_selection = gtk_entry_accessible_get_selection;
659 iface->add_selection = gtk_entry_accessible_add_selection;
660 iface->remove_selection = gtk_entry_accessible_remove_selection;
661 iface->set_selection = gtk_entry_accessible_set_selection;
662 iface->get_run_attributes = gtk_entry_accessible_get_run_attributes;
663 iface->get_default_attributes = gtk_entry_accessible_get_default_attributes;
664 iface->get_character_extents = gtk_entry_accessible_get_character_extents;
665 iface->get_offset_at_point = gtk_entry_accessible_get_offset_at_point;
669 gtk_entry_accessible_set_text_contents (AtkEditableText *text,
674 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
678 if (!gtk_editable_get_editable (GTK_EDITABLE (widget)))
681 gtk_entry_set_text (GTK_ENTRY (widget), string);
685 gtk_entry_accessible_insert_text (AtkEditableText *text,
691 GtkEditable *editable;
693 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
697 editable = GTK_EDITABLE (widget);
698 if (!gtk_editable_get_editable (editable))
701 gtk_editable_insert_text (editable, string, length, position);
702 gtk_editable_set_position (editable, *position);
706 gtk_entry_accessible_copy_text (AtkEditableText *text,
711 GtkEditable *editable;
713 GtkClipboard *clipboard;
715 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
719 if (!gtk_widget_has_screen (widget))
722 editable = GTK_EDITABLE (widget);
723 str = gtk_editable_get_chars (editable, start_pos, end_pos);
724 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
725 gtk_clipboard_set_text (clipboard, str, -1);
730 gtk_entry_accessible_cut_text (AtkEditableText *text,
735 GtkEditable *editable;
737 GtkClipboard *clipboard;
739 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
743 if (!gtk_widget_has_screen (widget))
746 editable = GTK_EDITABLE (widget);
747 if (!gtk_editable_get_editable (editable))
750 str = gtk_editable_get_chars (editable, start_pos, end_pos);
751 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
752 gtk_clipboard_set_text (clipboard, str, -1);
753 gtk_editable_delete_text (editable, start_pos, end_pos);
757 gtk_entry_accessible_delete_text (AtkEditableText *text,
762 GtkEditable *editable;
764 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
768 editable = GTK_EDITABLE (widget);
769 if (!gtk_editable_get_editable (editable))
772 gtk_editable_delete_text (editable, start_pos, end_pos);
782 paste_received_cb (GtkClipboard *clipboard,
786 PasteData *paste = data;
789 gtk_editable_insert_text (GTK_EDITABLE (paste->entry), text, -1,
792 g_object_unref (paste->entry);
797 gtk_entry_accessible_paste_text (AtkEditableText *text,
801 GtkEditable *editable;
803 GtkClipboard *clipboard;
805 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
809 if (!gtk_widget_has_screen (widget))
812 editable = GTK_EDITABLE (widget);
813 if (!gtk_editable_get_editable (editable))
816 paste = g_new0 (PasteData, 1);
817 paste->entry = GTK_ENTRY (widget);
818 paste->position = position;
820 g_object_ref (paste->entry);
821 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
822 gtk_clipboard_request_text (clipboard, paste_received_cb, paste);
826 atk_editable_text_interface_init (AtkEditableTextIface *iface)
828 iface->set_text_contents = gtk_entry_accessible_set_text_contents;
829 iface->insert_text = gtk_entry_accessible_insert_text;
830 iface->copy_text = gtk_entry_accessible_copy_text;
831 iface->cut_text = gtk_entry_accessible_cut_text;
832 iface->delete_text = gtk_entry_accessible_delete_text;
833 iface->paste_text = gtk_entry_accessible_paste_text;
834 iface->set_run_attributes = NULL;
837 /* We connect to GtkEditable::insert-text, since it carries
838 * the information we need. But we delay emitting our own
839 * text_changed::insert signal until the entry has update
840 * all its internal state and emits GtkEntry::changed.
843 insert_text_cb (GtkEditable *editable,
845 gint new_text_length,
848 GtkEntryAccessible *accessible;
850 if (new_text_length == 0)
853 accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
855 g_signal_emit_by_name (accessible,
856 "text-changed::insert",
858 g_utf8_strlen (new_text, new_text_length));
861 /* We connect to GtkEditable::delete-text, since it carries
862 * the information we need. But we delay emitting our own
863 * text_changed::delete signal until the entry has update
864 * all its internal state and emits GtkEntry::changed.
867 delete_text_cb (GtkEditable *editable,
871 GtkEntryAccessible *accessible;
873 accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
879 text = _gtk_entry_get_display_text (GTK_ENTRY (editable), 0, -1);
880 end = g_utf8_strlen (text, -1);
887 g_signal_emit_by_name (accessible,
888 "text-changed::delete",
894 check_for_selection_change (GtkEntryAccessible *accessible,
897 gboolean ret_val = FALSE;
900 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
902 if (end != accessible->priv->cursor_position ||
903 start != accessible->priv->selection_bound)
905 * This check is here as this function can be called
906 * for notification of selection_bound and current_pos.
907 * The values of current_pos and selection_bound may be the same
908 * for both notifications and we only want to generate one
909 * text_selection_changed signal.
915 /* We had a selection */
916 ret_val = (accessible->priv->cursor_position != accessible->priv->selection_bound);
919 accessible->priv->cursor_position = end;
920 accessible->priv->selection_bound = start;
926 gtk_entry_accessible_do_action (AtkAction *action,
931 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
935 if (!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
941 gtk_widget_activate (widget);
947 gtk_entry_accessible_get_n_actions (AtkAction *action)
953 gtk_entry_accessible_get_keybinding (AtkAction *action,
959 AtkRelation *relation;
961 gpointer target_object;
967 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
971 set = atk_object_ref_relation_set (ATK_OBJECT (action));
976 relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY);
979 target = atk_relation_get_target (relation);
981 target_object = g_ptr_array_index (target, 0);
982 label = gtk_accessible_get_widget (GTK_ACCESSIBLE (target_object));
985 g_object_unref (set);
987 if (GTK_IS_LABEL (label))
989 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
990 if (key_val != GDK_KEY_VoidSymbol)
991 return gtk_accelerator_name (key_val, GDK_MOD1_MASK);
998 gtk_entry_accessible_action_get_name (AtkAction *action,
1008 atk_action_interface_init (AtkActionIface *iface)
1010 iface->do_action = gtk_entry_accessible_do_action;
1011 iface->get_n_actions = gtk_entry_accessible_get_n_actions;
1012 iface->get_keybinding = gtk_entry_accessible_get_keybinding;
1013 iface->get_name = gtk_entry_accessible_action_get_name;