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 "gtksubmenuitemaccessible.h"
24 #include "gtk/gtkmenuitemprivate.h"
26 #define KEYBINDING_SEPARATOR ";"
28 static void menu_item_select (GtkMenuItem *item);
29 static void menu_item_deselect (GtkMenuItem *item);
31 static GtkWidget *get_label_from_container (GtkWidget *container);
32 static gchar *get_text_from_label_widget (GtkWidget *widget);
35 static void atk_action_interface_init (AtkActionIface *iface);
37 G_DEFINE_TYPE_WITH_CODE (GtkMenuItemAccessible, _gtk_menu_item_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
38 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init))
41 gtk_menu_item_accessible_initialize (AtkObject *obj,
47 ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->initialize (obj, data);
49 g_signal_connect (data, "select", G_CALLBACK (menu_item_select), NULL);
50 g_signal_connect (data, "deselect", G_CALLBACK (menu_item_deselect), NULL);
52 widget = GTK_WIDGET (data);
53 parent = gtk_widget_get_parent (widget);
54 if (GTK_IS_MENU (parent))
56 GtkWidget *parent_widget;
58 parent_widget = gtk_menu_get_attach_widget (GTK_MENU (parent));
60 if (!GTK_IS_MENU_ITEM (parent_widget))
61 parent_widget = gtk_widget_get_parent (widget);
63 atk_object_set_parent (obj, gtk_widget_get_accessible (parent_widget));
66 GTK_WIDGET_ACCESSIBLE (obj)->layer = ATK_LAYER_POPUP;
68 if (GTK_IS_TEAROFF_MENU_ITEM (data))
69 obj->role = ATK_ROLE_TEAR_OFF_MENU_ITEM;
70 else if (GTK_IS_SEPARATOR_MENU_ITEM (data))
71 obj->role = ATK_ROLE_SEPARATOR;
73 obj->role = ATK_ROLE_MENU_ITEM;
77 gtk_menu_item_accessible_get_n_children (AtkObject *obj)
83 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
87 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
92 children = gtk_container_get_children (GTK_CONTAINER (submenu));
93 count = g_list_length (children);
94 g_list_free (children);
100 gtk_menu_item_accessible_ref_child (AtkObject *obj,
103 AtkObject *accessible;
107 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
112 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
118 children = gtk_container_get_children (GTK_CONTAINER (submenu));
119 tmp_list = g_list_nth (children, i);
122 accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
123 g_object_ref (accessible);
125 g_list_free (children);
132 gtk_menu_item_accessible_ref_state_set (AtkObject *obj)
134 AtkObject *menu_item;
135 AtkStateSet *state_set, *parent_state_set;
137 state_set = ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->ref_state_set (obj);
139 menu_item = atk_object_get_parent (obj);
143 if (!GTK_IS_MENU_ITEM (gtk_accessible_get_widget (GTK_ACCESSIBLE (menu_item))))
146 parent_state_set = atk_object_ref_state_set (menu_item);
147 if (!atk_state_set_contains_state (parent_state_set, ATK_STATE_SELECTED))
149 atk_state_set_remove_state (state_set, ATK_STATE_FOCUSED);
150 atk_state_set_remove_state (state_set, ATK_STATE_SHOWING);
152 g_object_unref (parent_state_set);
159 gtk_menu_item_accessible_get_role (AtkObject *obj)
163 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
164 if (widget != NULL &&
165 gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)))
166 return ATK_ROLE_MENU;
168 return ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->get_role (obj);
172 gtk_menu_item_accessible_get_name (AtkObject *obj)
177 GtkMenuItemAccessible *accessible;
179 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
183 name = ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->get_name (obj);
187 accessible = GTK_MENU_ITEM_ACCESSIBLE (obj);
188 label = get_label_from_container (widget);
190 g_free (accessible->text);
191 accessible->text = get_text_from_label_widget (label);
193 return accessible->text;
197 gtk_menu_item_accessible_finalize (GObject *object)
199 GtkMenuItemAccessible *accessible = GTK_MENU_ITEM_ACCESSIBLE (object);
201 g_free (accessible->text);
203 G_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->finalize (object);
207 gtk_menu_item_accessible_notify_gtk (GObject *obj,
212 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (obj));
214 if (strcmp (pspec->name, "label") == 0)
216 if (atk_obj->name == NULL)
217 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
218 g_signal_emit_by_name (atk_obj, "visible-data-changed");
221 GTK_WIDGET_ACCESSIBLE_CLASS (_gtk_menu_item_accessible_parent_class)->notify_gtk (obj, pspec);
225 _gtk_menu_item_accessible_class_init (GtkMenuItemAccessibleClass *klass)
227 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
228 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
229 GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
231 widget_class->notify_gtk = gtk_menu_item_accessible_notify_gtk;
233 gobject_class->finalize = gtk_menu_item_accessible_finalize;
235 class->get_n_children = gtk_menu_item_accessible_get_n_children;
236 class->ref_child = gtk_menu_item_accessible_ref_child;
237 class->ref_state_set = gtk_menu_item_accessible_ref_state_set;
238 class->initialize = gtk_menu_item_accessible_initialize;
239 class->get_name = gtk_menu_item_accessible_get_name;
240 class->get_role = gtk_menu_item_accessible_get_role;
244 _gtk_menu_item_accessible_init (GtkMenuItemAccessible *menu_item)
249 get_label_from_container (GtkWidget *container)
252 GList *children, *tmp_list;
254 if (!GTK_IS_CONTAINER (container))
257 children = gtk_container_get_children (GTK_CONTAINER (container));
260 for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
262 if (GTK_IS_LABEL (tmp_list->data))
264 label = tmp_list->data;
267 else if (GTK_IS_CELL_VIEW (tmp_list->data))
269 label = tmp_list->data;
272 else if (GTK_IS_BOX (tmp_list->data))
274 label = get_label_from_container (GTK_WIDGET (tmp_list->data));
279 g_list_free (children);
285 get_text_from_label_widget (GtkWidget *label)
287 if (GTK_IS_LABEL (label))
288 return g_strdup (gtk_label_get_text (GTK_LABEL (label)));
289 else if (GTK_IS_CELL_VIEW (label))
298 model = gtk_cell_view_get_model (GTK_CELL_VIEW (label));
299 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (label));
300 gtk_tree_model_get_iter (model, &iter, path);
301 gtk_tree_path_free (path);
303 area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (label));
304 gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
305 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (label));
308 for (l = cells; l; l = l->next)
310 GtkCellRenderer *cell = l->data;
312 if (GTK_IS_CELL_RENDERER_TEXT (cell))
314 g_object_get (cell, "text", &text, NULL);
328 ensure_menus_unposted (GtkMenuItemAccessible *menu_item)
333 parent = atk_object_get_parent (ATK_OBJECT (menu_item));
336 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
337 if (GTK_IS_MENU (widget))
339 if (gtk_widget_get_mapped (widget))
340 gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
344 parent = atk_object_get_parent (parent);
349 gtk_menu_item_accessible_do_action (AtkAction *action,
352 GtkWidget *item, *item_parent;
353 gboolean item_mapped;
355 item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
362 if (!gtk_widget_get_sensitive (item) || !gtk_widget_get_visible (item))
365 item_parent = gtk_widget_get_parent (item);
366 if (!GTK_IS_MENU_SHELL (item_parent))
369 gtk_menu_shell_select_item (GTK_MENU_SHELL (item_parent), item);
370 item_mapped = gtk_widget_get_mapped (item);
372 /* This is what is called when <Return> is pressed for a menu item.
373 * The last argument means 'force hide'.
375 g_signal_emit_by_name (item_parent, "activate-current", 1);
377 ensure_menus_unposted (GTK_MENU_ITEM_ACCESSIBLE (action));
383 gtk_menu_item_accessible_get_n_actions (AtkAction *action)
387 item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
391 if (!_gtk_menu_item_is_selectable (item))
398 gtk_menu_item_accessible_action_get_name (AtkAction *action,
401 if (i != 0 || gtk_menu_item_accessible_get_n_actions (action) == 0)
408 find_accel_by_widget (GtkAccelKey *key,
412 /* We assume that closure->data points to the widget
413 * pending gtk_widget_get_accel_closures being made public
415 return data == (gpointer) closure->data;
419 find_accel_by_closure (GtkAccelKey *key,
423 return data == (gpointer) closure;
426 /* This function returns a string of the form A;B;C where A is
427 * the keybinding for the widget; B is the keybinding to traverse
428 * from the menubar and C is the accelerator. The items in the
429 * keybinding to traverse from the menubar are separated by ":".
432 gtk_menu_item_accessible_get_keybinding (AtkAction *action,
435 gchar *keybinding = NULL;
436 gchar *item_keybinding = NULL;
437 gchar *full_keybinding = NULL;
438 gchar *accelerator = NULL;
440 GtkWidget *temp_item;
444 item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
454 GdkModifierType mnemonic_modifier = 0;
456 gchar *key, *temp_keybinding;
458 child = gtk_bin_get_child (GTK_BIN (temp_item));
462 parent = gtk_widget_get_parent (temp_item);
464 /* parent can be NULL when activating a window from the panel */
467 if (GTK_IS_MENU_BAR (parent))
471 toplevel = gtk_widget_get_toplevel (parent);
472 if (toplevel && GTK_IS_WINDOW (toplevel))
474 gtk_window_get_mnemonic_modifier (GTK_WINDOW (toplevel));
477 if (GTK_IS_LABEL (child))
479 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (child));
480 if (key_val != GDK_KEY_VoidSymbol)
482 key = gtk_accelerator_name (key_val, mnemonic_modifier);
484 temp_keybinding = g_strconcat (key, ":", full_keybinding, NULL);
486 temp_keybinding = g_strdup (key);
488 if (temp_item == item)
489 item_keybinding = g_strdup (key);
492 g_free (full_keybinding);
493 full_keybinding = temp_keybinding;
498 g_free (full_keybinding);
499 full_keybinding = NULL;
504 /* We have reached the menu bar so we are finished */
505 if (GTK_IS_MENU_BAR (parent))
508 g_return_val_if_fail (GTK_IS_MENU (parent), NULL);
509 temp_item = gtk_menu_get_attach_widget (GTK_MENU (parent));
510 if (!GTK_IS_MENU_ITEM (temp_item))
512 /* Menu is attached to something other than a menu item;
513 * probably an option menu
515 g_free (full_keybinding);
516 full_keybinding = NULL;
521 parent = gtk_widget_get_parent (item);
522 if (GTK_IS_MENU (parent))
524 GtkAccelGroup *group;
527 group = gtk_menu_get_accel_group (GTK_MENU (parent));
529 key = gtk_accel_group_find (group, find_accel_by_widget, item);
533 child = gtk_bin_get_child (GTK_BIN (item));
534 if (GTK_IS_ACCEL_LABEL (child))
536 GtkAccelLabel *accel_label;
537 GClosure *accel_closure;
539 accel_label = GTK_ACCEL_LABEL (child);
540 g_object_get (accel_label, "accel-closure", &accel_closure, NULL);
543 key = gtk_accel_group_find (gtk_accel_group_from_accel_closure (accel_closure),
544 find_accel_by_closure,
546 g_closure_unref (accel_closure);
552 accelerator = gtk_accelerator_name (key->accel_key, key->accel_mods);
555 /* Concatenate the bindings */
556 if (item_keybinding || full_keybinding || accelerator)
561 keybinding = g_strconcat (item_keybinding, KEYBINDING_SEPARATOR, NULL);
562 g_free (item_keybinding);
565 keybinding = g_strdup (KEYBINDING_SEPARATOR);
569 temp = g_strconcat (keybinding, full_keybinding,
570 KEYBINDING_SEPARATOR, NULL);
571 g_free (full_keybinding);
574 temp = g_strconcat (keybinding, KEYBINDING_SEPARATOR, NULL);
580 temp = g_strconcat (keybinding, accelerator, NULL);
581 g_free (accelerator);
591 atk_action_interface_init (AtkActionIface *iface)
593 iface->do_action = gtk_menu_item_accessible_do_action;
594 iface->get_n_actions = gtk_menu_item_accessible_get_n_actions;
595 iface->get_name = gtk_menu_item_accessible_action_get_name;
596 iface->get_keybinding = gtk_menu_item_accessible_get_keybinding;
600 menu_item_selection (GtkMenuItem *item,
603 AtkObject *obj, *parent;
606 obj = gtk_widget_get_accessible (GTK_WIDGET (item));
607 atk_object_notify_state_change (obj, ATK_STATE_SELECTED, selected);
609 for (i = 0; i < atk_object_get_n_accessible_children (obj); i++)
612 child = atk_object_ref_accessible_child (obj, i);
613 atk_object_notify_state_change (child, ATK_STATE_SHOWING, selected);
614 g_object_unref (child);
616 parent = atk_object_get_parent (obj);
617 g_signal_emit_by_name (parent, "selection-changed");
621 menu_item_select (GtkMenuItem *item)
623 menu_item_selection (item, TRUE);
627 menu_item_deselect (GtkMenuItem *item)
629 menu_item_selection (item, FALSE);