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 "gtkcomboboxaccessible.h"
27 static void insert_text_cb (GtkEditable *editable,
31 static void delete_text_cb (GtkEditable *editable,
35 static gboolean check_for_selection_change (GtkEntryAccessible *entry,
39 static void atk_editable_text_interface_init (AtkEditableTextIface *iface);
40 static void atk_text_interface_init (AtkTextIface *iface);
41 static void atk_action_interface_init (AtkActionIface *iface);
44 G_DEFINE_TYPE_WITH_CODE (GtkEntryAccessible, _gtk_entry_accessible, GTK_TYPE_WIDGET_ACCESSIBLE,
45 G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init)
46 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init)
47 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init))
51 gtk_entry_accessible_ref_state_set (AtkObject *accessible)
53 AtkStateSet *state_set;
57 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
61 state_set = ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->ref_state_set (accessible);
63 g_object_get (G_OBJECT (widget), "editable", &value, NULL);
65 atk_state_set_add_state (state_set, ATK_STATE_EDITABLE);
66 atk_state_set_add_state (state_set, ATK_STATE_SINGLE_LINE);
71 static AtkAttributeSet *
72 gtk_entry_accessible_get_attributes (AtkObject *accessible)
75 AtkAttributeSet *attributes;
76 AtkAttribute *placeholder_text;
79 attributes = ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->get_attributes (accessible);
81 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
85 text = gtk_entry_get_placeholder_text (GTK_ENTRY (widget));
89 placeholder_text = g_malloc (sizeof (AtkAttribute));
90 placeholder_text->name = g_strdup ("placeholder-text");
91 placeholder_text->value = g_strdup (text);
93 attributes = g_slist_append (attributes, placeholder_text);
99 gtk_entry_accessible_initialize (AtkObject *obj,
103 GtkEntryAccessible *gtk_entry_accessible;
104 gint start_pos, end_pos;
106 ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->initialize (obj, data);
108 gtk_entry_accessible = GTK_ENTRY_ACCESSIBLE (obj);
110 entry = GTK_ENTRY (data);
111 gtk_editable_get_selection_bounds (GTK_EDITABLE (entry),
112 &start_pos, &end_pos);
113 gtk_entry_accessible->cursor_position = end_pos;
114 gtk_entry_accessible->selection_bound = start_pos;
116 /* Set up signal callbacks */
117 g_signal_connect (entry, "insert-text", G_CALLBACK (insert_text_cb), NULL);
118 g_signal_connect (entry, "delete-text", G_CALLBACK (delete_text_cb), NULL);
120 if (gtk_entry_get_visibility (entry))
121 obj->role = ATK_ROLE_TEXT;
123 obj->role = ATK_ROLE_PASSWORD_TEXT;
127 gtk_entry_accessible_notify_gtk (GObject *obj,
133 GtkEntryAccessible* entry;
135 widget = GTK_WIDGET (obj);
136 atk_obj = gtk_widget_get_accessible (widget);
137 gtk_entry = GTK_ENTRY (widget);
138 entry = GTK_ENTRY_ACCESSIBLE (atk_obj);
140 if (g_strcmp0 (pspec->name, "cursor-position") == 0)
142 if (check_for_selection_change (entry, gtk_entry))
143 g_signal_emit_by_name (atk_obj, "text-selection-changed");
145 * The entry cursor position has moved so generate the signal.
147 g_signal_emit_by_name (atk_obj, "text-caret-moved",
148 entry->cursor_position);
150 else if (g_strcmp0 (pspec->name, "selection-bound") == 0)
152 if (check_for_selection_change (entry, gtk_entry))
153 g_signal_emit_by_name (atk_obj, "text-selection-changed");
155 else if (g_strcmp0 (pspec->name, "editable") == 0)
159 g_object_get (obj, "editable", &value, NULL);
160 atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, value);
162 else if (g_strcmp0 (pspec->name, "visibility") == 0)
167 visibility = gtk_entry_get_visibility (gtk_entry);
168 new_role = visibility ? ATK_ROLE_TEXT : ATK_ROLE_PASSWORD_TEXT;
169 atk_object_set_role (atk_obj, new_role);
172 GTK_WIDGET_ACCESSIBLE_CLASS (_gtk_entry_accessible_parent_class)->notify_gtk (obj, pspec);
176 gtk_entry_accessible_get_index_in_parent (AtkObject *accessible)
179 * If the parent widget is a combo box then the index is 1
180 * otherwise do the normal thing.
182 if (accessible->accessible_parent)
183 if (GTK_IS_COMBO_BOX_ACCESSIBLE (accessible->accessible_parent))
186 return ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->get_index_in_parent (accessible);
190 _gtk_entry_accessible_class_init (GtkEntryAccessibleClass *klass)
192 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
193 GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
195 class->ref_state_set = gtk_entry_accessible_ref_state_set;
196 class->get_index_in_parent = gtk_entry_accessible_get_index_in_parent;
197 class->initialize = gtk_entry_accessible_initialize;
198 class->get_attributes = gtk_entry_accessible_get_attributes;
200 widget_class->notify_gtk = gtk_entry_accessible_notify_gtk;
204 _gtk_entry_accessible_init (GtkEntryAccessible *entry)
206 entry->cursor_position = 0;
207 entry->selection_bound = 0;
211 gtk_entry_accessible_get_text (AtkText *atk_text,
218 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
222 /* FIXME: is this acceptable ? */
223 if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
224 return g_strdup ("");
226 text = gtk_entry_get_text (GTK_ENTRY (widget));
229 return g_utf8_substring (text, start_pos, end_pos > -1 ? end_pos : g_utf8_strlen (text, -1));
235 gtk_entry_accessible_get_text_before_offset (AtkText *text,
237 AtkTextBoundary boundary_type,
243 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
247 /* FIXME: is this acceptable ? */
248 if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
249 return g_strdup ("");
251 return _gtk_pango_get_text_before (gtk_entry_get_layout (GTK_ENTRY (widget)),
252 boundary_type, offset,
253 start_offset, end_offset);
257 gtk_entry_accessible_get_text_at_offset (AtkText *text,
259 AtkTextBoundary boundary_type,
265 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
269 /* FIXME: is this acceptable ? */
270 if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
271 return g_strdup ("");
273 return _gtk_pango_get_text_at (gtk_entry_get_layout (GTK_ENTRY (widget)),
274 boundary_type, offset,
275 start_offset, end_offset);
279 gtk_entry_accessible_get_text_after_offset (AtkText *text,
281 AtkTextBoundary boundary_type,
287 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
291 /* FIXME: is this acceptable ? */
292 if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
293 return g_strdup ("");
295 return _gtk_pango_get_text_after (gtk_entry_get_layout (GTK_ENTRY (widget)),
296 boundary_type, offset,
297 start_offset, end_offset);
301 gtk_entry_accessible_get_character_count (AtkText *atk_text)
306 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
310 text = gtk_entry_get_text (GTK_ENTRY (widget));
313 return g_utf8_strlen (text, -1);
319 gtk_entry_accessible_get_caret_offset (AtkText *text)
323 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
327 return gtk_editable_get_position (GTK_EDITABLE (widget));
331 gtk_entry_accessible_set_caret_offset (AtkText *text,
336 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
340 gtk_editable_set_position (GTK_EDITABLE (widget), offset);
345 static AtkAttributeSet *
346 add_text_attribute (AtkAttributeSet *attributes,
347 AtkTextAttribute attr,
352 at = g_new (AtkAttribute, 1);
353 at->name = g_strdup (atk_text_attribute_get_name (attr));
354 at->value = g_strdup (atk_text_attribute_get_value (attr, i));
356 return g_slist_prepend (attributes, at);
359 static AtkAttributeSet *
360 gtk_entry_accessible_get_run_attributes (AtkText *text,
366 AtkAttributeSet *attributes;
368 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
373 attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
374 gtk_widget_get_direction (widget));
375 attributes = _gtk_pango_get_run_attributes (attributes,
376 gtk_entry_get_layout (GTK_ENTRY (widget)),
384 static AtkAttributeSet *
385 gtk_entry_accessible_get_default_attributes (AtkText *text)
388 AtkAttributeSet *attributes;
390 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
395 attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
396 gtk_widget_get_direction (widget));
397 attributes = _gtk_pango_get_default_attributes (attributes,
398 gtk_entry_get_layout (GTK_ENTRY (widget)));
399 attributes = _gtk_style_context_get_attributes (attributes,
400 gtk_widget_get_style_context (widget),
401 gtk_widget_get_state_flags (widget));
407 gtk_entry_accessible_get_character_extents (AtkText *text,
417 PangoRectangle char_rect;
418 const gchar *entry_text;
419 gint index, x_layout, y_layout;
421 gint x_window, y_window;
423 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
427 entry = GTK_ENTRY (widget);
429 gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout);
430 entry_text = gtk_entry_get_text (entry);
431 index = g_utf8_offset_to_pointer (entry_text, offset) - entry_text;
432 pango_layout_index_to_pos (gtk_entry_get_layout (entry), index, &char_rect);
433 pango_extents_to_pixels (&char_rect, NULL);
435 window = gtk_widget_get_window (widget);
436 gdk_window_get_origin (window, &x_window, &y_window);
438 *x = x_window + x_layout + char_rect.x;
439 *y = y_window + y_layout + char_rect.y;
440 *width = char_rect.width;
441 *height = char_rect.height;
443 if (coords == ATK_XY_WINDOW)
445 window = gdk_window_get_toplevel (window);
446 gdk_window_get_origin (window, &x_window, &y_window);
454 gtk_entry_accessible_get_offset_at_point (AtkText *atk_text,
462 gint index, x_layout, y_layout;
463 gint x_window, y_window;
464 gint x_local, y_local;
467 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
471 entry = GTK_ENTRY (widget);
473 gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout);
475 window = gtk_widget_get_window (widget);
476 gdk_window_get_origin (window, &x_window, &y_window);
478 x_local = x - x_layout - x_window;
479 y_local = y - y_layout - y_window;
481 if (coords == ATK_XY_WINDOW)
483 window = gdk_window_get_toplevel (window);
484 gdk_window_get_origin (window, &x_window, &y_window);
489 if (!pango_layout_xy_to_index (gtk_entry_get_layout (entry),
490 x_local * PANGO_SCALE,
491 y_local * PANGO_SCALE,
494 if (x_local < 0 || y_local < 0)
502 text = gtk_entry_get_text (entry);
503 return g_utf8_pointer_to_offset (text, text + index);
510 gtk_entry_accessible_get_n_selections (AtkText *text)
515 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
519 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
526 gtk_entry_accessible_get_selection (AtkText *text,
533 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
537 if (selection_num != 0)
540 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), start_pos, end_pos))
541 return gtk_editable_get_chars (GTK_EDITABLE (widget), *start_pos, *end_pos);
547 gtk_entry_accessible_add_selection (AtkText *text,
555 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
559 entry = GTK_ENTRY (widget);
561 if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
563 gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
571 gtk_entry_accessible_remove_selection (AtkText *text,
577 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
581 if (selection_num != 0)
584 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
586 gtk_editable_select_region (GTK_EDITABLE (widget), end, end);
594 gtk_entry_accessible_set_selection (AtkText *text,
602 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
606 if (selection_num != 0)
609 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
611 gtk_editable_select_region (GTK_EDITABLE (widget), start_pos, end_pos);
619 gtk_entry_accessible_get_character_at_offset (AtkText *atk_text,
626 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
630 if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
633 text = gtk_entry_get_text (GTK_ENTRY (widget));
634 if (offset >= g_utf8_strlen (text, -1))
637 index = g_utf8_offset_to_pointer (text, offset);
639 return g_utf8_get_char (index);
643 atk_text_interface_init (AtkTextIface *iface)
645 iface->get_text = gtk_entry_accessible_get_text;
646 iface->get_character_at_offset = gtk_entry_accessible_get_character_at_offset;
647 iface->get_text_before_offset = gtk_entry_accessible_get_text_before_offset;
648 iface->get_text_at_offset = gtk_entry_accessible_get_text_at_offset;
649 iface->get_text_after_offset = gtk_entry_accessible_get_text_after_offset;
650 iface->get_caret_offset = gtk_entry_accessible_get_caret_offset;
651 iface->set_caret_offset = gtk_entry_accessible_set_caret_offset;
652 iface->get_character_count = gtk_entry_accessible_get_character_count;
653 iface->get_n_selections = gtk_entry_accessible_get_n_selections;
654 iface->get_selection = gtk_entry_accessible_get_selection;
655 iface->add_selection = gtk_entry_accessible_add_selection;
656 iface->remove_selection = gtk_entry_accessible_remove_selection;
657 iface->set_selection = gtk_entry_accessible_set_selection;
658 iface->get_run_attributes = gtk_entry_accessible_get_run_attributes;
659 iface->get_default_attributes = gtk_entry_accessible_get_default_attributes;
660 iface->get_character_extents = gtk_entry_accessible_get_character_extents;
661 iface->get_offset_at_point = gtk_entry_accessible_get_offset_at_point;
665 gtk_entry_accessible_set_text_contents (AtkEditableText *text,
670 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
674 if (!gtk_editable_get_editable (GTK_EDITABLE (widget)))
677 gtk_entry_set_text (GTK_ENTRY (widget), string);
681 gtk_entry_accessible_insert_text (AtkEditableText *text,
687 GtkEditable *editable;
689 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
693 editable = GTK_EDITABLE (widget);
694 if (!gtk_editable_get_editable (editable))
697 gtk_editable_insert_text (editable, string, length, position);
698 gtk_editable_set_position (editable, *position);
702 gtk_entry_accessible_copy_text (AtkEditableText *text,
707 GtkEditable *editable;
709 GtkClipboard *clipboard;
711 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
715 if (!gtk_widget_has_screen (widget))
718 editable = GTK_EDITABLE (widget);
719 str = gtk_editable_get_chars (editable, start_pos, end_pos);
720 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
721 gtk_clipboard_set_text (clipboard, str, -1);
726 gtk_entry_accessible_cut_text (AtkEditableText *text,
731 GtkEditable *editable;
733 GtkClipboard *clipboard;
735 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
739 if (!gtk_widget_has_screen (widget))
742 editable = GTK_EDITABLE (widget);
743 if (!gtk_editable_get_editable (editable))
746 str = gtk_editable_get_chars (editable, start_pos, end_pos);
747 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
748 gtk_clipboard_set_text (clipboard, str, -1);
749 gtk_editable_delete_text (editable, start_pos, end_pos);
753 gtk_entry_accessible_delete_text (AtkEditableText *text,
758 GtkEditable *editable;
760 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
764 editable = GTK_EDITABLE (widget);
765 if (!gtk_editable_get_editable (editable))
768 gtk_editable_delete_text (editable, start_pos, end_pos);
778 paste_received_cb (GtkClipboard *clipboard,
782 PasteData *paste = data;
785 gtk_editable_insert_text (GTK_EDITABLE (paste->entry), text, -1,
788 g_object_unref (paste->entry);
793 gtk_entry_accessible_paste_text (AtkEditableText *text,
797 GtkEditable *editable;
799 GtkClipboard *clipboard;
801 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
805 if (!gtk_widget_has_screen (widget))
808 editable = GTK_EDITABLE (widget);
809 if (!gtk_editable_get_editable (editable))
812 paste = g_new0 (PasteData, 1);
813 paste->entry = GTK_ENTRY (widget);
814 paste->position = position;
816 g_object_ref (paste->entry);
817 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
818 gtk_clipboard_request_text (clipboard, paste_received_cb, paste);
822 atk_editable_text_interface_init (AtkEditableTextIface *iface)
824 iface->set_text_contents = gtk_entry_accessible_set_text_contents;
825 iface->insert_text = gtk_entry_accessible_insert_text;
826 iface->copy_text = gtk_entry_accessible_copy_text;
827 iface->cut_text = gtk_entry_accessible_cut_text;
828 iface->delete_text = gtk_entry_accessible_delete_text;
829 iface->paste_text = gtk_entry_accessible_paste_text;
830 iface->set_run_attributes = NULL;
833 /* We connect to GtkEditable::insert-text, since it carries
834 * the information we need. But we delay emitting our own
835 * text_changed::insert signal until the entry has update
836 * all its internal state and emits GtkEntry::changed.
839 insert_text_cb (GtkEditable *editable,
841 gint new_text_length,
844 GtkEntryAccessible *accessible;
846 if (new_text_length == 0)
849 accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
851 g_signal_emit_by_name (accessible,
852 "text-changed::insert",
854 g_utf8_strlen (new_text, new_text_length));
857 /* We connect to GtkEditable::delete-text, since it carries
858 * the information we need. But we delay emitting our own
859 * text_changed::delete signal until the entry has update
860 * all its internal state and emits GtkEntry::changed.
863 delete_text_cb (GtkEditable *editable,
867 GtkEntryAccessible *accessible;
869 accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
875 text = gtk_entry_get_text (GTK_ENTRY (editable));
876 end = g_utf8_strlen (text, -1);
882 g_signal_emit_by_name (accessible,
883 "text-changed::delete",
889 check_for_selection_change (GtkEntryAccessible *accessible,
892 gboolean ret_val = FALSE;
895 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
897 if (end != accessible->cursor_position ||
898 start != accessible->selection_bound)
900 * This check is here as this function can be called
901 * for notification of selection_bound and current_pos.
902 * The values of current_pos and selection_bound may be the same
903 * for both notifications and we only want to generate one
904 * text_selection_changed signal.
910 /* We had a selection */
911 ret_val = (accessible->cursor_position != accessible->selection_bound);
914 accessible->cursor_position = end;
915 accessible->selection_bound = start;
921 gtk_entry_accessible_do_action (AtkAction *action,
926 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
930 if (!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
936 gtk_widget_activate (widget);
942 gtk_entry_accessible_get_n_actions (AtkAction *action)
948 gtk_entry_accessible_get_keybinding (AtkAction *action,
954 AtkRelation *relation;
956 gpointer target_object;
962 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
966 set = atk_object_ref_relation_set (ATK_OBJECT (action));
971 relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY);
974 target = atk_relation_get_target (relation);
976 target_object = g_ptr_array_index (target, 0);
977 label = gtk_accessible_get_widget (GTK_ACCESSIBLE (target_object));
980 g_object_unref (set);
982 if (GTK_IS_LABEL (label))
984 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
985 if (key_val != GDK_KEY_VoidSymbol)
986 return gtk_accelerator_name (key_val, GDK_MOD1_MASK);
993 gtk_entry_accessible_action_get_name (AtkAction *action,
1003 atk_action_interface_init (AtkActionIface *iface)
1005 iface->do_action = gtk_entry_accessible_do_action;
1006 iface->get_n_actions = gtk_entry_accessible_get_n_actions;
1007 iface->get_keybinding = gtk_entry_accessible_get_keybinding;
1008 iface->get_name = gtk_entry_accessible_action_get_name;