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, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
22 #include "gtkmenuitemaccessible.h"
23 #include "gtk/gtkmenuitemprivate.h"
25 #define KEYBINDING_SEPARATOR ";"
27 static void menu_item_select (GtkMenuItem *item);
28 static void menu_item_deselect (GtkMenuItem *item);
30 static GtkWidget *get_label_from_container (GtkWidget *container);
31 static gchar *get_text_from_label_widget (GtkWidget *widget);
33 static gint menu_item_add_gtk (GtkContainer *container,
35 static gint menu_item_remove_gtk (GtkContainer *container,
38 static void atk_action_interface_init (AtkActionIface *iface);
39 static void atk_selection_interface_init (AtkSelectionIface *iface);
41 G_DEFINE_TYPE_WITH_CODE (GtkMenuItemAccessible, _gtk_menu_item_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
42 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init);
43 G_IMPLEMENT_INTERFACE (ATK_TYPE_SELECTION, atk_selection_interface_init))
46 gtk_menu_item_accessible_initialize (AtkObject *obj,
53 ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->initialize (obj, data);
55 g_signal_connect (data, "select", G_CALLBACK (menu_item_select), NULL);
56 g_signal_connect (data, "deselect", G_CALLBACK (menu_item_deselect), NULL);
58 widget = GTK_WIDGET (data);
59 parent = gtk_widget_get_parent (widget);
60 if (GTK_IS_MENU (parent))
62 GtkWidget *parent_widget;
64 parent_widget = gtk_menu_get_attach_widget (GTK_MENU (parent));
66 if (!GTK_IS_MENU_ITEM (parent_widget))
67 parent_widget = gtk_widget_get_parent (widget);
69 atk_object_set_parent (obj, gtk_widget_get_accessible (parent_widget));
72 GTK_WIDGET_ACCESSIBLE (obj)->layer = ATK_LAYER_POPUP;
74 obj->role = ATK_ROLE_MENU_ITEM;
76 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (data));
79 g_signal_connect (menu, "add", G_CALLBACK (menu_item_add_gtk), NULL);
80 g_signal_connect (menu, "remove", G_CALLBACK (menu_item_remove_gtk), NULL);
85 gtk_menu_item_accessible_get_n_children (AtkObject *obj)
91 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
95 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
100 children = gtk_container_get_children (GTK_CONTAINER (submenu));
101 count = g_list_length (children);
102 g_list_free (children);
108 gtk_menu_item_accessible_ref_child (AtkObject *obj,
111 AtkObject *accessible;
115 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
120 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
126 children = gtk_container_get_children (GTK_CONTAINER (submenu));
127 tmp_list = g_list_nth (children, i);
130 accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
131 g_object_ref (accessible);
133 g_list_free (children);
140 gtk_menu_item_accessible_ref_state_set (AtkObject *obj)
142 AtkObject *menu_item;
143 AtkStateSet *state_set, *parent_state_set;
145 state_set = ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->ref_state_set (obj);
147 menu_item = atk_object_get_parent (obj);
151 if (!GTK_IS_MENU_ITEM (gtk_accessible_get_widget (GTK_ACCESSIBLE (menu_item))))
154 parent_state_set = atk_object_ref_state_set (menu_item);
155 if (!atk_state_set_contains_state (parent_state_set, ATK_STATE_SELECTED))
157 atk_state_set_remove_state (state_set, ATK_STATE_FOCUSED);
158 atk_state_set_remove_state (state_set, ATK_STATE_SHOWING);
160 g_object_unref (parent_state_set);
167 gtk_menu_item_accessible_get_role (AtkObject *obj)
171 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
172 if (widget != NULL &&
173 gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)))
174 return ATK_ROLE_MENU;
176 return ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->get_role (obj);
180 gtk_menu_item_accessible_get_name (AtkObject *obj)
185 GtkMenuItemAccessible *accessible;
187 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
191 name = ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->get_name (obj);
195 accessible = GTK_MENU_ITEM_ACCESSIBLE (obj);
196 label = get_label_from_container (widget);
198 g_free (accessible->text);
199 accessible->text = get_text_from_label_widget (label);
201 return accessible->text;
205 gtk_menu_item_accessible_finalize (GObject *object)
207 GtkMenuItemAccessible *accessible = GTK_MENU_ITEM_ACCESSIBLE (object);
209 g_free (accessible->text);
211 G_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->finalize (object);
215 gtk_menu_item_accessible_notify_gtk (GObject *obj,
220 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (obj));
222 if (strcmp (pspec->name, "label") == 0)
224 if (atk_obj->name == NULL)
225 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
226 g_signal_emit_by_name (atk_obj, "visible-data-changed");
229 GTK_WIDGET_ACCESSIBLE_CLASS (_gtk_menu_item_accessible_parent_class)->notify_gtk (obj, pspec);
233 _gtk_menu_item_accessible_class_init (GtkMenuItemAccessibleClass *klass)
235 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
236 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
237 GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
239 widget_class->notify_gtk = gtk_menu_item_accessible_notify_gtk;
241 gobject_class->finalize = gtk_menu_item_accessible_finalize;
243 class->get_n_children = gtk_menu_item_accessible_get_n_children;
244 class->ref_child = gtk_menu_item_accessible_ref_child;
245 class->ref_state_set = gtk_menu_item_accessible_ref_state_set;
246 class->initialize = gtk_menu_item_accessible_initialize;
247 class->get_name = gtk_menu_item_accessible_get_name;
248 class->get_role = gtk_menu_item_accessible_get_role;
252 _gtk_menu_item_accessible_init (GtkMenuItemAccessible *menu_item)
257 get_label_from_container (GtkWidget *container)
260 GList *children, *tmp_list;
262 if (!GTK_IS_CONTAINER (container))
265 children = gtk_container_get_children (GTK_CONTAINER (container));
268 for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
270 if (GTK_IS_LABEL (tmp_list->data))
272 label = tmp_list->data;
275 else if (GTK_IS_CELL_VIEW (tmp_list->data))
277 label = tmp_list->data;
280 else if (GTK_IS_BOX (tmp_list->data))
282 label = get_label_from_container (GTK_WIDGET (tmp_list->data));
287 g_list_free (children);
293 get_text_from_label_widget (GtkWidget *label)
295 if (GTK_IS_LABEL (label))
296 return g_strdup (gtk_label_get_text (GTK_LABEL (label)));
297 else if (GTK_IS_CELL_VIEW (label))
306 model = gtk_cell_view_get_model (GTK_CELL_VIEW (label));
307 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (label));
308 gtk_tree_model_get_iter (model, &iter, path);
309 gtk_tree_path_free (path);
311 area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (label));
312 gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
313 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (label));
316 for (l = cells; l; l = l->next)
318 GtkCellRenderer *cell = l->data;
320 if (GTK_IS_CELL_RENDERER_TEXT (cell))
322 g_object_get (cell, "text", &text, NULL);
336 ensure_menus_unposted (GtkMenuItemAccessible *menu_item)
341 parent = atk_object_get_parent (ATK_OBJECT (menu_item));
344 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
345 if (GTK_IS_MENU (widget))
347 if (gtk_widget_get_mapped (widget))
348 gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
352 parent = atk_object_get_parent (parent);
357 gtk_menu_item_accessible_do_action (AtkAction *action,
360 GtkWidget *item, *item_parent;
361 gboolean item_mapped;
363 item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
370 if (!gtk_widget_get_sensitive (item) || !gtk_widget_get_visible (item))
373 item_parent = gtk_widget_get_parent (item);
374 if (!GTK_IS_MENU_SHELL (item_parent))
377 gtk_menu_shell_select_item (GTK_MENU_SHELL (item_parent), item);
378 item_mapped = gtk_widget_get_mapped (item);
380 /* This is what is called when <Return> is pressed for a menu item.
381 * The last argument means 'force hide'.
383 g_signal_emit_by_name (item_parent, "activate-current", 1);
385 ensure_menus_unposted (GTK_MENU_ITEM_ACCESSIBLE (action));
391 gtk_menu_item_accessible_get_n_actions (AtkAction *action)
395 item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
399 if (!_gtk_menu_item_is_selectable (item))
406 gtk_menu_item_accessible_action_get_name (AtkAction *action,
409 if (i != 0 || gtk_menu_item_accessible_get_n_actions (action) == 0)
416 find_accel_by_widget (GtkAccelKey *key,
420 /* We assume that closure->data points to the widget
421 * pending gtk_widget_get_accel_closures being made public
423 return data == (gpointer) closure->data;
427 find_accel_by_closure (GtkAccelKey *key,
431 return data == (gpointer) closure;
434 /* This function returns a string of the form A;B;C where A is
435 * the keybinding for the widget; B is the keybinding to traverse
436 * from the menubar and C is the accelerator. The items in the
437 * keybinding to traverse from the menubar are separated by ":".
440 gtk_menu_item_accessible_get_keybinding (AtkAction *action,
443 gchar *keybinding = NULL;
444 gchar *item_keybinding = NULL;
445 gchar *full_keybinding = NULL;
446 gchar *accelerator = NULL;
448 GtkWidget *temp_item;
452 item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
462 GdkModifierType mnemonic_modifier = 0;
464 gchar *key, *temp_keybinding;
466 child = gtk_bin_get_child (GTK_BIN (temp_item));
470 parent = gtk_widget_get_parent (temp_item);
472 /* parent can be NULL when activating a window from the panel */
475 if (GTK_IS_MENU_BAR (parent))
479 toplevel = gtk_widget_get_toplevel (parent);
480 if (toplevel && GTK_IS_WINDOW (toplevel))
482 gtk_window_get_mnemonic_modifier (GTK_WINDOW (toplevel));
485 if (GTK_IS_LABEL (child))
487 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (child));
488 if (key_val != GDK_KEY_VoidSymbol)
490 key = gtk_accelerator_name (key_val, mnemonic_modifier);
492 temp_keybinding = g_strconcat (key, ":", full_keybinding, NULL);
494 temp_keybinding = g_strdup (key);
496 if (temp_item == item)
497 item_keybinding = g_strdup (key);
500 g_free (full_keybinding);
501 full_keybinding = temp_keybinding;
506 g_free (full_keybinding);
507 full_keybinding = NULL;
512 /* We have reached the menu bar so we are finished */
513 if (GTK_IS_MENU_BAR (parent))
516 g_return_val_if_fail (GTK_IS_MENU (parent), NULL);
517 temp_item = gtk_menu_get_attach_widget (GTK_MENU (parent));
518 if (!GTK_IS_MENU_ITEM (temp_item))
520 /* Menu is attached to something other than a menu item;
521 * probably an option menu
523 g_free (full_keybinding);
524 full_keybinding = NULL;
529 parent = gtk_widget_get_parent (item);
530 if (GTK_IS_MENU (parent))
532 GtkAccelGroup *group;
535 group = gtk_menu_get_accel_group (GTK_MENU (parent));
537 key = gtk_accel_group_find (group, find_accel_by_widget, item);
541 child = gtk_bin_get_child (GTK_BIN (item));
542 if (GTK_IS_ACCEL_LABEL (child))
544 GtkAccelLabel *accel_label;
545 GClosure *accel_closure;
547 accel_label = GTK_ACCEL_LABEL (child);
548 g_object_get (accel_label, "accel-closure", &accel_closure, NULL);
551 key = gtk_accel_group_find (gtk_accel_group_from_accel_closure (accel_closure),
552 find_accel_by_closure,
554 g_closure_unref (accel_closure);
560 accelerator = gtk_accelerator_name (key->accel_key, key->accel_mods);
563 /* Concatenate the bindings */
564 if (item_keybinding || full_keybinding || accelerator)
569 keybinding = g_strconcat (item_keybinding, KEYBINDING_SEPARATOR, NULL);
570 g_free (item_keybinding);
573 keybinding = g_strdup (KEYBINDING_SEPARATOR);
577 temp = g_strconcat (keybinding, full_keybinding,
578 KEYBINDING_SEPARATOR, NULL);
579 g_free (full_keybinding);
582 temp = g_strconcat (keybinding, KEYBINDING_SEPARATOR, NULL);
588 temp = g_strconcat (keybinding, accelerator, NULL);
589 g_free (accelerator);
599 atk_action_interface_init (AtkActionIface *iface)
601 iface->do_action = gtk_menu_item_accessible_do_action;
602 iface->get_n_actions = gtk_menu_item_accessible_get_n_actions;
603 iface->get_name = gtk_menu_item_accessible_action_get_name;
604 iface->get_keybinding = gtk_menu_item_accessible_get_keybinding;
608 menu_item_selection (GtkMenuItem *item,
611 AtkObject *obj, *parent;
614 obj = gtk_widget_get_accessible (GTK_WIDGET (item));
615 atk_object_notify_state_change (obj, ATK_STATE_SELECTED, selected);
617 for (i = 0; i < atk_object_get_n_accessible_children (obj); i++)
620 child = atk_object_ref_accessible_child (obj, i);
621 atk_object_notify_state_change (child, ATK_STATE_SHOWING, selected);
622 g_object_unref (child);
624 parent = atk_object_get_parent (obj);
625 g_signal_emit_by_name (parent, "selection-changed");
629 gtk_menu_item_accessible_add_selection (AtkSelection *selection,
639 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
643 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
647 shell = GTK_MENU_SHELL (menu);
648 kids = gtk_container_get_children (GTK_CONTAINER (shell));
649 length = g_list_length (kids);
650 if (i < 0 || i > length)
656 child = g_list_nth_data (kids, i);
658 g_return_val_if_fail (GTK_IS_MENU_ITEM (child), FALSE);
659 gtk_menu_shell_select_item (shell, GTK_WIDGET (child));
664 gtk_menu_item_accessible_clear_selection (AtkSelection *selection)
669 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
673 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
677 gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
683 gtk_menu_item_accessible_ref_selection (AtkSelection *selection,
695 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
699 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
703 shell = GTK_MENU_SHELL (menu);
705 item = gtk_menu_shell_get_selected_item (shell);
708 obj = gtk_widget_get_accessible (item);
717 gtk_menu_item_accessible_get_selection_count (AtkSelection *selection)
723 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
727 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
731 shell = GTK_MENU_SHELL (menu);
733 if (gtk_menu_shell_get_selected_item (shell) != NULL)
740 gtk_menu_item_accessible_is_child_selected (AtkSelection *selection,
750 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
754 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
758 shell = GTK_MENU_SHELL (menu);
760 item = gtk_menu_shell_get_selected_item (shell);
764 kids = gtk_container_get_children (GTK_CONTAINER (shell));
765 j = g_list_index (kids, item);
772 gtk_menu_item_accessible_remove_selection (AtkSelection *selection,
783 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
787 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
791 shell = GTK_MENU_SHELL (menu);
793 item = gtk_menu_shell_get_selected_item (shell);
794 if (item && gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)))
795 gtk_menu_shell_deselect (shell);
801 atk_selection_interface_init (AtkSelectionIface *iface)
803 iface->add_selection = gtk_menu_item_accessible_add_selection;
804 iface->clear_selection = gtk_menu_item_accessible_clear_selection;
805 iface->ref_selection = gtk_menu_item_accessible_ref_selection;
806 iface->get_selection_count = gtk_menu_item_accessible_get_selection_count;
807 iface->is_child_selected = gtk_menu_item_accessible_is_child_selected;
808 iface->remove_selection = gtk_menu_item_accessible_remove_selection;
812 menu_item_add_gtk (GtkContainer *container,
815 GtkWidget *parent_widget;
816 AtkObject *atk_parent;
817 AtkObject *atk_child;
818 GtkContainerAccessible *container_accessible;
821 g_return_val_if_fail (GTK_IS_MENU (container), 1);
823 parent_widget = gtk_menu_get_attach_widget (GTK_MENU (container));
824 if (GTK_IS_MENU_ITEM (parent_widget))
826 atk_parent = gtk_widget_get_accessible (parent_widget);
827 atk_child = gtk_widget_get_accessible (widget);
829 g_object_notify (G_OBJECT (atk_child), "accessible-parent");
830 container_accessible = GTK_CONTAINER_ACCESSIBLE (atk_parent);
831 g_list_free (container_accessible->children);
832 container_accessible->children = gtk_container_get_children (container);
833 index = g_list_index (container_accessible->children, widget);
834 g_signal_emit_by_name (atk_parent, "children-changed::add",
835 index, atk_child, NULL);
841 menu_item_remove_gtk (GtkContainer *container,
844 GtkWidget *parent_widget;
845 AtkObject *atk_parent;
846 AtkObject *atk_child;
847 GtkContainerAccessible *container_accessible;
851 g_return_val_if_fail (GTK_IS_MENU (container), 1);
853 parent_widget = gtk_menu_get_attach_widget (GTK_MENU (container));
854 if (GTK_IS_MENU_ITEM (parent_widget))
856 atk_parent = gtk_widget_get_accessible (parent_widget);
857 atk_child = gtk_widget_get_accessible (widget);
859 g_object_notify (G_OBJECT (atk_child), "accessible-parent");
861 container_accessible = GTK_CONTAINER_ACCESSIBLE (atk_parent);
862 index = g_list_index (container_accessible->children, widget);
863 list_length = g_list_length (container_accessible->children);
864 g_list_free (container_accessible->children);
865 container_accessible->children = gtk_container_get_children (container);
866 if (index >= 0 && index <= list_length)
867 g_signal_emit_by_name (atk_parent, "children-changed::remove",
868 index, atk_child, NULL);
873 menu_item_select (GtkMenuItem *item)
875 menu_item_selection (item, TRUE);
879 menu_item_deselect (GtkMenuItem *item)
881 menu_item_selection (item, FALSE);