From 428bb09a351995d879b0f4cb0f12b89cc5b60e3f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 5 Jun 2020 19:17:31 +0100 Subject: [PATCH] a11y: Make GtkSearchEntryAccessible a full GtkWidgetAccessible We should not inherit from GtkEntryAccessible, given that GtkSearchEntry is *not* a GtkEntry. --- gtk/a11y/gtksearchentryaccessible.c | 938 +++++++++++++++++++++++++++- gtk/a11y/gtksearchentryaccessible.h | 2 +- 2 files changed, 937 insertions(+), 3 deletions(-) diff --git a/gtk/a11y/gtksearchentryaccessible.c b/gtk/a11y/gtksearchentryaccessible.c index 92c68c424f..eab4012e53 100644 --- a/gtk/a11y/gtksearchentryaccessible.c +++ b/gtk/a11y/gtksearchentryaccessible.c @@ -22,14 +22,164 @@ #include "gtksearchentryaccessible.h" +#include "gtkeditable.h" +#include "gtkimage.h" #include "gtkintl.h" +#include "gtklabel.h" +#include "gtkpango.h" +#include "gtksearchentryprivate.h" +#include "gtkstylecontextprivate.h" +#include "gtktextprivate.h" struct _GtkSearchEntryAccessible { - GtkEntryAccessible parent_instance; + GtkWidgetAccessible parent_instance; + + int cursor_position; + int selection_bound; }; -G_DEFINE_TYPE (GtkSearchEntryAccessible, gtk_search_entry_accessible, GTK_TYPE_ENTRY_ACCESSIBLE) +static void atk_editable_text_interface_init (AtkEditableTextIface *iface); +static void atk_text_interface_init (AtkTextIface *iface); +static void atk_action_interface_init (AtkActionIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkSearchEntryAccessible, gtk_search_entry_accessible, GTK_TYPE_WIDGET_ACCESSIBLE, + G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init) + G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init) + G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)) + +static inline GtkText * +get_text_widget (GtkAccessible *accessible) +{ + GtkSearchEntry *search_entry = + GTK_SEARCH_ENTRY (gtk_accessible_get_widget (accessible)); + + if (search_entry == NULL) + return NULL; + + return gtk_search_entry_get_text_widget (search_entry); +} + +static gboolean +check_for_selection_change (GtkSearchEntryAccessible *self, + GtkEditable *editable) +{ + gboolean ret_val = FALSE; + gint start, end; + + if (gtk_editable_get_selection_bounds (editable, &start, &end)) + { + if (end != self->cursor_position || + start != self->selection_bound) + /* + * This check is here as this function can be called + * for notification of selection_bound and current_pos. + * The values of current_pos and selection_bound may be the same + * for both notifications and we only want to generate one + * text_selection_changed signal. + */ + ret_val = TRUE; + } + else + { + /* We had a selection */ + ret_val = (self->cursor_position != self->selection_bound); + } + + self->cursor_position = end; + self->selection_bound = start; + + return ret_val; +} + +static void +insert_text_cb (GtkEditable *editable, + gchar *new_text, + gint new_text_length, + gint *position, + GtkSearchEntryAccessible *self) +{ + int length; + + if (new_text_length == 0) + return; + + length = g_utf8_strlen (new_text, new_text_length); + + g_signal_emit_by_name (self, + "text-changed::insert", + *position - length, + length); +} + +static void +delete_text_cb (GtkEditable *editable, + gint start, + gint end, + GtkSearchEntryAccessible *self) +{ + GtkText *text; + + text = get_text_widget (GTK_ACCESSIBLE (self)); + if (text == NULL) + return; + + if (end < 0) + { + char *contents; + + contents = gtk_text_get_display_text (text, 0, -1); + end = g_utf8_strlen (contents, -1); + + g_free (contents); + } + + if (end == start) + return; + + g_signal_emit_by_name (self, + "text-changed::delete", + start, + end - start); +} + +static int +gtk_search_entry_accessible_get_n_children (AtkObject *object) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (object)); + if (widget == NULL) + return 0; + + int count = 0; + for (GtkWidget *child = gtk_widget_get_first_child (widget); + child != NULL; + child = gtk_widget_get_next_sibling (child)) + { + if (GTK_IS_IMAGE (child) && gtk_widget_get_child_visible (child)) + count += 1; + } + + return count; +} + +static AtkObject * +gtk_search_entry_accessible_ref_child (AtkObject *obj, + int i) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj)); + if (widget == NULL) + return NULL; + + for (GtkWidget *child = gtk_widget_get_first_child (widget); + child != NULL; + child = gtk_widget_get_next_sibling (child)) + { + if (GTK_IS_IMAGE (child) && gtk_widget_get_child_visible (child)) + return g_object_ref (gtk_widget_get_accessible (child)); + } + + return NULL; +} static AtkAttributeSet * gtk_search_entry_accessible_get_attributes (AtkObject *accessible) @@ -58,12 +208,65 @@ gtk_search_entry_accessible_get_attributes (AtkObject *accessible) return attributes; } +static void +on_cursor_position_changed (GObject *gobject, + GParamSpec *pspec, + gpointer data) +{ + GtkSearchEntryAccessible *self = data; + GtkEditable *editable = GTK_EDITABLE (gobject); + + if (check_for_selection_change (self, editable)) + g_signal_emit_by_name (self, "text-selection-changed"); + + // The entry cursor position has moved so generate the signal + g_signal_emit_by_name (self, "text-caret-moved", + gtk_editable_get_position (editable)); +} + +static void +on_selection_bound_changed (GObject *gobject, + GParamSpec *pspec, + gpointer data) +{ + GtkSearchEntryAccessible *self = data; + GtkEditable *editable = GTK_EDITABLE (gobject); + + if (check_for_selection_change (self, editable)) + g_signal_emit_by_name (self, "text-selection-changed"); +} + +static void +gtk_search_entry_accessible_initialize (AtkObject *object, + gpointer data) +{ + GtkSearchEntryAccessible *self = GTK_SEARCH_ENTRY_ACCESSIBLE (object); + GtkEditable *editable = data; + GtkWidget *widget = data; + int start_pos, end_pos; + + gtk_editable_get_selection_bounds (editable, &start_pos, &end_pos); + self->cursor_position = end_pos; + self->selection_bound = start_pos; + + /* Set up signal callbacks */ + g_signal_connect_after (widget, "insert-text", G_CALLBACK (insert_text_cb), self); + g_signal_connect (widget, "delete-text", G_CALLBACK (delete_text_cb), self); + g_signal_connect (widget, "notify::cursor-position", + G_CALLBACK (on_cursor_position_changed), self); + g_signal_connect (widget, "notify::selection-bound", + G_CALLBACK (on_selection_bound_changed), self); +} + static void gtk_search_entry_accessible_class_init (GtkSearchEntryAccessibleClass *klass) { AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + class->initialize = gtk_search_entry_accessible_initialize; class->get_attributes = gtk_search_entry_accessible_get_attributes; + class->get_n_children = gtk_search_entry_accessible_get_n_children; + class->ref_child = gtk_search_entry_accessible_ref_child; } static void @@ -74,3 +277,734 @@ gtk_search_entry_accessible_init (GtkSearchEntryAccessible *self) atk_obj->role = ATK_ROLE_TEXT; atk_object_set_name (atk_obj, _("Search")); } + +static gchar * +gtk_search_entry_accessible_get_text (AtkText *atk_text, + gint start_pos, + gint end_pos) +{ + GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + + if (text == NULL) + return NULL; + + return gtk_text_get_display_text (text, start_pos, end_pos); +} + +static gchar * +gtk_search_entry_accessible_get_text_before_offset (AtkText *atk_text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + + if (text == NULL) + return NULL; + + return _gtk_pango_get_text_before (gtk_text_get_layout (text), + boundary_type, offset, + start_offset, end_offset); +} + +static gchar * +gtk_search_entry_accessible_get_text_at_offset (AtkText *atk_text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + + if (text == NULL) + return NULL; + + return _gtk_pango_get_text_at (gtk_text_get_layout (text), + boundary_type, offset, + start_offset, end_offset); +} + +static gchar * +gtk_search_entry_accessible_get_text_after_offset (AtkText *atk_text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + + if (text == NULL) + return NULL; + + return _gtk_pango_get_text_after (gtk_text_get_layout (text), + boundary_type, offset, + start_offset, end_offset); +} + +static gint +gtk_search_entry_accessible_get_character_count (AtkText *atk_text) +{ + GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + if (text == NULL) + return 0; + + char *display_text = gtk_text_get_display_text (text, 0, -1); + + int char_count = 0; + if (display_text) + { + char_count = g_utf8_strlen (display_text, -1); + g_free (display_text); + } + + return char_count; +} + +static gint +gtk_search_entry_accessible_get_caret_offset (AtkText *atk_text) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text)); + + if (widget == NULL) + return -1; + + int cursor_position = 0; + gboolean result = gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), NULL, &cursor_position); + if (!result) + return -1; + + return cursor_position; +} + +static gboolean +gtk_search_entry_accessible_set_caret_offset (AtkText *atk_text, + gint offset) +{ + GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text)); + if (widget == NULL) + return FALSE; + + gtk_editable_set_position (GTK_EDITABLE (widget), offset); + + return TRUE; +} + +static AtkAttributeSet * +add_text_attribute (AtkAttributeSet *attributes, + AtkTextAttribute attr, + gint i) +{ + AtkAttribute *at; + + at = g_new (AtkAttribute, 1); + at->name = g_strdup (atk_text_attribute_get_name (attr)); + at->value = g_strdup (atk_text_attribute_get_value (attr, i)); + + return g_slist_prepend (attributes, at); +} + +static AtkAttributeSet * +gtk_search_entry_accessible_get_run_attributes (AtkText *atk_text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + + if (text == NULL) + return NULL; + + AtkAttributeSet *attributes = NULL; + attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION, + gtk_widget_get_direction (GTK_WIDGET (text))); + attributes = _gtk_pango_get_run_attributes (attributes, + gtk_text_get_layout (text), + offset, + start_offset, + end_offset); + + return attributes; +} + +static AtkAttributeSet * +gtk_search_entry_accessible_get_default_attributes (AtkText *atk_text) +{ + GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + + if (text == NULL) + return NULL; + + AtkAttributeSet *attributes = NULL; + attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION, + gtk_widget_get_direction (GTK_WIDGET (text))); + attributes = _gtk_pango_get_default_attributes (attributes, + gtk_text_get_layout (text)); + attributes = _gtk_style_context_get_attributes (attributes, + gtk_widget_get_style_context (GTK_WIDGET (text))); + + return attributes; +} + +static void +gtk_search_entry_accessible_get_character_extents (AtkText *atk_text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + PangoRectangle char_rect; + char *entry_text; + int index, x_layout, y_layout; + GtkAllocation allocation; + + if (text == NULL) + return; + + gtk_text_get_layout_offsets (text, &x_layout, &y_layout); + entry_text = gtk_text_get_display_text (text, 0, -1); + index = g_utf8_offset_to_pointer (entry_text, offset) - entry_text; + g_free (entry_text); + + pango_layout_index_to_pos (gtk_text_get_layout (text), index, &char_rect); + pango_extents_to_pixels (&char_rect, NULL); + + gtk_widget_get_allocation (GTK_WIDGET (text), &allocation); + + *x = allocation.x + x_layout + char_rect.x; + *y = allocation.y + y_layout + char_rect.y; + *width = char_rect.width; + *height = char_rect.height; +} + +static gint +gtk_search_entry_accessible_get_offset_at_point (AtkText *atk_text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + int index, x_layout, y_layout; + int x_local, y_local; + glong offset; + + if (text == NULL) + return 1; + + gtk_text_get_layout_offsets (text, &x_layout, &y_layout); + + x_local = x - x_layout; + y_local = y - y_layout; + + if (!pango_layout_xy_to_index (gtk_text_get_layout (text), + x_local * PANGO_SCALE, + y_local * PANGO_SCALE, + &index, NULL)) + { + if (x_local < 0 || y_local < 0) + index = 0; + else + index = -1; + } + + offset = -1; + if (index != -1) + { + char *entry_text = gtk_text_get_display_text (text, 0, -1); + offset = g_utf8_pointer_to_offset (entry_text, entry_text + index); + g_free (entry_text); + } + + return offset; +} + +static gint +gtk_search_entry_accessible_get_n_selections (AtkText *text) +{ + GtkWidget *widget; + gint start, end; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return 0; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end)) + return 1; + + return 0; +} + +static gchar * +gtk_search_entry_accessible_get_selection (AtkText *text, + gint selection_num, + gint *start_pos, + gint *end_pos) +{ + GtkEditable *editable; + GtkWidget *widget; + int start, end; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return NULL; + + if (selection_num != 0) + return NULL; + + editable = GTK_EDITABLE (widget); + if (gtk_editable_get_selection_bounds (editable, &start, &end)) + { + if (start_pos != NULL) + *start_pos = start; + if (end_pos != NULL) + *end_pos = end; + + return gtk_editable_get_chars (editable, start, end); + } + + return NULL; +} + +static gboolean +gtk_search_entry_accessible_add_selection (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkEntry *entry; + GtkWidget *widget; + int start, end; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return FALSE; + + entry = GTK_ENTRY (widget); + + if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) + { + gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gtk_search_entry_accessible_remove_selection (AtkText *text, + gint selection_num) +{ + GtkWidget *widget; + gint start, end; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return FALSE; + + if (selection_num != 0) + return FALSE; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end)) + { + gtk_editable_select_region (GTK_EDITABLE (widget), end, end); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gtk_search_entry_accessible_set_selection (AtkText *text, + gint selection_num, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + gint start, end; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return FALSE; + + if (selection_num != 0) + return FALSE; + + if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end)) + { + gtk_editable_select_region (GTK_EDITABLE (widget), start_pos, end_pos); + return TRUE; + } + else + return FALSE; +} + +static gunichar +gtk_search_entry_accessible_get_character_at_offset (AtkText *atk_text, + gint offset) +{ + GtkText *text; + char *contents; + char *index; + gunichar result; + + result = '\0'; + + text = get_text_widget (GTK_ACCESSIBLE (atk_text)); + if (text == NULL) + return 0; + + if (!gtk_text_get_visibility (text)) + return result; + + contents = gtk_text_get_display_text (text, 0, -1); + if (offset < g_utf8_strlen (contents, -1)) + { + index = g_utf8_offset_to_pointer (contents, offset); + result = g_utf8_get_char (index); + g_free (contents); + } + + return result; +} + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + iface->get_text = gtk_search_entry_accessible_get_text; + iface->get_character_at_offset = gtk_search_entry_accessible_get_character_at_offset; + iface->get_text_before_offset = gtk_search_entry_accessible_get_text_before_offset; + iface->get_text_at_offset = gtk_search_entry_accessible_get_text_at_offset; + iface->get_text_after_offset = gtk_search_entry_accessible_get_text_after_offset; + iface->get_caret_offset = gtk_search_entry_accessible_get_caret_offset; + iface->set_caret_offset = gtk_search_entry_accessible_set_caret_offset; + iface->get_character_count = gtk_search_entry_accessible_get_character_count; + iface->get_n_selections = gtk_search_entry_accessible_get_n_selections; + iface->get_selection = gtk_search_entry_accessible_get_selection; + iface->add_selection = gtk_search_entry_accessible_add_selection; + iface->remove_selection = gtk_search_entry_accessible_remove_selection; + iface->set_selection = gtk_search_entry_accessible_set_selection; + iface->get_run_attributes = gtk_search_entry_accessible_get_run_attributes; + iface->get_default_attributes = gtk_search_entry_accessible_get_default_attributes; + iface->get_character_extents = gtk_search_entry_accessible_get_character_extents; + iface->get_offset_at_point = gtk_search_entry_accessible_get_offset_at_point; +} + +static void +gtk_search_entry_accessible_set_text_contents (AtkEditableText *text, + const gchar *string) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + if (!gtk_editable_get_editable (GTK_EDITABLE (widget))) + return; + + gtk_editable_set_text (GTK_EDITABLE (widget), string); +} + +static void +gtk_search_entry_accessible_insert_text (AtkEditableText *text, + const gchar *string, + gint length, + gint *position) +{ + GtkWidget *widget; + GtkEditable *editable; + int pos = 0; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + if (!gtk_editable_get_editable (editable)) + return; + + if (position != NULL) + pos = *position; + + gtk_editable_insert_text (editable, string, length, &pos); + gtk_editable_set_position (editable, pos); + + if (position != NULL) + *position = pos; +} + +static void +gtk_search_entry_accessible_copy_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkEditable *editable; + GdkClipboard *clipboard; + char *str; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + str = gtk_editable_get_chars (editable, start_pos, end_pos); + clipboard = gtk_widget_get_clipboard (widget); + gdk_clipboard_set_text (clipboard, str); + g_free (str); +} + +static void +gtk_search_entry_accessible_cut_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkEditable *editable; + GdkClipboard *clipboard; + char *str; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + if (!gtk_editable_get_editable (editable)) + return; + + str = gtk_editable_get_chars (editable, start_pos, end_pos); + clipboard = gtk_widget_get_clipboard (widget); + gdk_clipboard_set_text (clipboard, str); + gtk_editable_delete_text (editable, start_pos, end_pos); +} + +static void +gtk_search_entry_accessible_delete_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkEditable *editable; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + if (!gtk_editable_get_editable (editable)) + return; + + gtk_editable_delete_text (editable, start_pos, end_pos); +} + +typedef struct +{ + GtkEditable *entry; + int position; +} PasteData; + +static void +paste_received_cb (GObject *clipboard, + GAsyncResult *result, + gpointer data) +{ + PasteData *paste = data; + char *text; + + text = gdk_clipboard_read_text_finish (GDK_CLIPBOARD (clipboard), result, NULL); + if (text != NULL) + gtk_editable_insert_text (paste->entry, + text, -1, + &(paste->position)); + + g_object_unref (paste->entry); + g_free (paste); + g_free (text); +} + +static void +gtk_search_entry_accessible_paste_text (AtkEditableText *text, + gint position) +{ + GtkWidget *widget; + GtkEditable *editable; + PasteData *paste; + GdkClipboard *clipboard; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text)); + if (widget == NULL) + return; + + editable = GTK_EDITABLE (widget); + if (!gtk_editable_get_editable (editable)) + return; + + paste = g_new0 (PasteData, 1); + paste->entry = GTK_EDITABLE (widget); + paste->position = position; + + g_object_ref (paste->entry); + clipboard = gtk_widget_get_clipboard (widget); + gdk_clipboard_read_text_async (clipboard, NULL, paste_received_cb, paste); +} + +static void +atk_editable_text_interface_init (AtkEditableTextIface *iface) +{ + iface->set_text_contents = gtk_search_entry_accessible_set_text_contents; + iface->insert_text = gtk_search_entry_accessible_insert_text; + iface->copy_text = gtk_search_entry_accessible_copy_text; + iface->cut_text = gtk_search_entry_accessible_cut_text; + iface->delete_text = gtk_search_entry_accessible_delete_text; + iface->paste_text = gtk_search_entry_accessible_paste_text; + iface->set_run_attributes = NULL; +} + +static gboolean +gtk_search_entry_accessible_do_action (AtkAction *action, + gint i) +{ + GtkWidget *widget; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action)); + if (widget == NULL) + return FALSE; + + if (!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget)) + return FALSE; + + if (i == 0) + { + gtk_widget_activate (widget); + return TRUE; + } + + if (i == 1) + { + gtk_editable_set_text (GTK_EDITABLE (widget), ""); + return TRUE; + } + + return FALSE; +} + +static gint +gtk_search_entry_accessible_get_n_actions (AtkAction *action) +{ + GtkAccessible *accessible = GTK_ACCESSIBLE (action); + + GtkWidget *widget = gtk_accessible_get_widget (accessible); + if (widget == NULL) + return 0; + + // If the SearchEntry widget contains text, we have a second action + // to clear its contents + int n_actions = 1; + const char *str = gtk_editable_get_text (GTK_EDITABLE (widget)); + if (str != NULL && *str != '\0') + n_actions += 1; + + return n_actions; +} + +static const gchar * +gtk_search_entry_accessible_get_keybinding (AtkAction *action, + gint i) +{ + GtkWidget *widget; + GtkWidget *label; + AtkRelationSet *set; + AtkRelation *relation; + GPtrArray *target; + gpointer target_object; + guint key_val; + + if (i != 0) + return NULL; + + widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action)); + if (widget == NULL) + return NULL; + + set = atk_object_ref_relation_set (ATK_OBJECT (action)); + if (!set) + return NULL; + + label = NULL; + relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY); + if (relation) + { + target = atk_relation_get_target (relation); + + target_object = g_ptr_array_index (target, 0); + label = gtk_accessible_get_widget (GTK_ACCESSIBLE (target_object)); + } + + g_object_unref (set); + + if (GTK_IS_LABEL (label)) + { + key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); + if (key_val != GDK_KEY_VoidSymbol) + return gtk_accelerator_name (key_val, GDK_ALT_MASK); + } + + return NULL; +} + +static const gchar * +gtk_search_entry_accessible_action_get_name (AtkAction *action, + gint i) +{ + switch (i) + { + case 0: + return "activate"; + + case 1: + return "clear"; + + default: + break; + } + + return NULL; +} + +static const gchar* +gtk_search_entry_accessible_action_get_localized_name (AtkAction *action, + gint i) +{ + if (i == 0) + return C_("Action name", "Activate"); + if (i == 1) + return C_("Action name", "Clear"); + + return NULL; +} + +static const gchar * +gtk_search_entry_accessible_action_get_description (AtkAction *action, + gint i) +{ + if (i == 0) + return C_("Action description", "Activates the entry"); + + if (i == 1) + return C_("Action description", "Clears the entry"); + + return NULL; +} + +static void +atk_action_interface_init (AtkActionIface *iface) +{ + iface->do_action = gtk_search_entry_accessible_do_action; + iface->get_n_actions = gtk_search_entry_accessible_get_n_actions; + iface->get_keybinding = gtk_search_entry_accessible_get_keybinding; + iface->get_name = gtk_search_entry_accessible_action_get_name; + iface->get_localized_name = gtk_search_entry_accessible_action_get_localized_name; + iface->get_description = gtk_search_entry_accessible_action_get_description; +} diff --git a/gtk/a11y/gtksearchentryaccessible.h b/gtk/a11y/gtksearchentryaccessible.h index 49012918e7..9fd2d559df 100644 --- a/gtk/a11y/gtksearchentryaccessible.h +++ b/gtk/a11y/gtksearchentryaccessible.h @@ -37,7 +37,7 @@ typedef struct _GtkSearchEntryAccessibleClass GtkSearchEntryAccessibleClass; struct _GtkSearchEntryAccessibleClass { - GtkEntryAccessibleClass parent_class; + GtkWidgetAccessibleClass parent_class; }; GDK_AVAILABLE_IN_ALL