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"
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);
34 static void atk_action_interface_init (AtkActionIface *iface);
36 G_DEFINE_TYPE_WITH_CODE (GtkMenuItemAccessible, gtk_menu_item_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
37 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init))
40 gtk_menu_item_accessible_initialize (AtkObject *obj,
46 ATK_OBJECT_CLASS (gtk_menu_item_accessible_parent_class)->initialize (obj, data);
48 g_signal_connect (data, "select", G_CALLBACK (menu_item_select), NULL);
49 g_signal_connect (data, "deselect", G_CALLBACK (menu_item_deselect), NULL);
51 widget = GTK_WIDGET (data);
52 parent = gtk_widget_get_parent (widget);
53 if (GTK_IS_MENU (parent))
55 GtkWidget *parent_widget;
57 parent_widget = gtk_menu_get_attach_widget (GTK_MENU (parent));
59 if (!GTK_IS_MENU_ITEM (parent_widget))
60 parent_widget = gtk_widget_get_parent (widget);
62 atk_object_set_parent (obj, gtk_widget_get_accessible (parent_widget));
65 gtk_widget_accessible_set_layer (GTK_WIDGET_ACCESSIBLE (obj), ATK_LAYER_POPUP);
67 if (GTK_IS_TEAROFF_MENU_ITEM (data))
68 obj->role = ATK_ROLE_TEAR_OFF_MENU_ITEM;
69 else if (GTK_IS_SEPARATOR_MENU_ITEM (data))
70 obj->role = ATK_ROLE_SEPARATOR;
72 obj->role = ATK_ROLE_MENU_ITEM;
76 gtk_menu_item_accessible_get_n_children (AtkObject *obj)
82 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
86 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
91 children = gtk_container_get_children (GTK_CONTAINER (submenu));
92 count = g_list_length (children);
93 g_list_free (children);
99 gtk_menu_item_accessible_ref_child (AtkObject *obj,
102 AtkObject *accessible;
106 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
111 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
117 children = gtk_container_get_children (GTK_CONTAINER (submenu));
118 tmp_list = g_list_nth (children, i);
121 accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
122 g_object_ref (accessible);
124 g_list_free (children);
131 gtk_menu_item_accessible_ref_state_set (AtkObject *obj)
133 AtkObject *menu_item;
134 AtkStateSet *state_set, *parent_state_set;
136 state_set = ATK_OBJECT_CLASS (gtk_menu_item_accessible_parent_class)->ref_state_set (obj);
138 menu_item = atk_object_get_parent (obj);
142 if (!GTK_IS_MENU_ITEM (gtk_accessible_get_widget (GTK_ACCESSIBLE (menu_item))))
145 parent_state_set = atk_object_ref_state_set (menu_item);
146 if (!atk_state_set_contains_state (parent_state_set, ATK_STATE_SELECTED))
148 atk_state_set_remove_state (state_set, ATK_STATE_FOCUSED);
149 atk_state_set_remove_state (state_set, ATK_STATE_SHOWING);
151 g_object_unref (parent_state_set);
158 gtk_menu_item_accessible_get_name (AtkObject *obj)
163 GtkMenuItemAccessible *accessible;
165 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
169 name = ATK_OBJECT_CLASS (gtk_menu_item_accessible_parent_class)->get_name (obj);
173 accessible = GTK_MENU_ITEM_ACCESSIBLE (obj);
174 label = get_label_from_container (widget);
176 g_free (accessible->text);
177 accessible->text = get_text_from_label_widget (label);
179 return accessible->text;
183 gtk_menu_item_accessible_finalize (GObject *object)
185 GtkMenuItemAccessible *accessible = GTK_MENU_ITEM_ACCESSIBLE (object);
187 g_free (accessible->text);
189 G_OBJECT_CLASS (gtk_menu_item_accessible_parent_class)->finalize (object);
193 gtk_menu_item_accessible_notify_gtk (GObject *obj,
198 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (obj));
200 if (strcmp (pspec->name, "label") == 0)
202 if (atk_obj->name == NULL)
203 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
204 g_signal_emit_by_name (atk_obj, "visible_data_changed");
207 GTK_WIDGET_ACCESSIBLE_CLASS (gtk_menu_item_accessible_parent_class)->notify_gtk (obj, pspec);
211 gtk_menu_item_accessible_class_init (GtkMenuItemAccessibleClass *klass)
213 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
214 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
215 GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
217 widget_class->notify_gtk = gtk_menu_item_accessible_notify_gtk;
219 gobject_class->finalize = gtk_menu_item_accessible_finalize;
221 class->get_n_children = gtk_menu_item_accessible_get_n_children;
222 class->ref_child = gtk_menu_item_accessible_ref_child;
223 class->ref_state_set = gtk_menu_item_accessible_ref_state_set;
224 class->initialize = gtk_menu_item_accessible_initialize;
225 class->get_name = gtk_menu_item_accessible_get_name;
229 gtk_menu_item_accessible_init (GtkMenuItemAccessible *menu_item)
234 get_label_from_container (GtkWidget *container)
237 GList *children, *tmp_list;
239 if (!GTK_IS_CONTAINER (container))
242 children = gtk_container_get_children (GTK_CONTAINER (container));
245 for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
247 if (GTK_IS_LABEL (tmp_list->data))
249 label = tmp_list->data;
252 else if (GTK_IS_CELL_VIEW (tmp_list->data))
254 label = tmp_list->data;
257 else if (GTK_IS_BOX (tmp_list->data))
259 label = get_label_from_container (GTK_WIDGET (tmp_list->data));
264 g_list_free (children);
270 get_text_from_label_widget (GtkWidget *label)
272 if (GTK_IS_LABEL (label))
273 return g_strdup (gtk_label_get_text (GTK_LABEL (label)));
274 else if (GTK_IS_CELL_VIEW (label))
283 model = gtk_cell_view_get_model (GTK_CELL_VIEW (label));
284 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (label));
285 gtk_tree_model_get_iter (model, &iter, path);
286 gtk_tree_path_free (path);
288 area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (label));
289 gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
290 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (label));
293 for (l = cells; l; l = l->next)
295 GtkCellRenderer *cell = l->data;
297 if (GTK_IS_CELL_RENDERER_TEXT (cell))
299 g_object_get (cell, "text", &text, NULL);
313 ensure_menus_unposted (GtkMenuItemAccessible *menu_item)
318 parent = atk_object_get_parent (ATK_OBJECT (menu_item));
321 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
322 if (GTK_IS_MENU (widget))
324 if (gtk_widget_get_mapped (widget))
325 gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
329 parent = atk_object_get_parent (parent);
334 gtk_menu_item_accessible_do_action (AtkAction *action,
337 GtkWidget *item, *item_parent;
338 gboolean item_mapped;
340 item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
347 if (!gtk_widget_get_sensitive (item) || !gtk_widget_get_visible (item))
350 item_parent = gtk_widget_get_parent (item);
351 if (!GTK_IS_MENU_SHELL (item_parent))
354 gtk_menu_shell_select_item (GTK_MENU_SHELL (item_parent), item);
355 item_mapped = gtk_widget_get_mapped (item);
357 /* This is what is called when <Return> is pressed for a menu item.
358 * The last argument means 'force hide'.
360 g_signal_emit_by_name (item_parent, "activate_current", 1);
362 ensure_menus_unposted (GTK_MENU_ITEM_ACCESSIBLE (action));
368 gtk_menu_item_accessible_get_n_actions (AtkAction *action)
374 gtk_menu_item_accessible_action_get_name (AtkAction *action,
384 find_accel_by_widget (GtkAccelKey *key,
388 /* We assume that closure->data points to the widget
389 * pending gtk_widget_get_accel_closures being made public
391 return data == (gpointer) closure->data;
395 find_accel_by_closure (GtkAccelKey *key,
399 return data == (gpointer) closure;
402 /* This function returns a string of the form A;B;C where A is
403 * the keybinding for the widget; B is the keybinding to traverse
404 * from the menubar and C is the accelerator. The items in the
405 * keybinding to traverse from the menubar are separated by ":".
408 gtk_menu_item_accessible_get_keybinding (AtkAction *action,
411 gchar *keybinding = NULL;
412 gchar *item_keybinding = NULL;
413 gchar *full_keybinding = NULL;
414 gchar *accelerator = NULL;
416 GtkWidget *temp_item;
420 item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
430 GdkModifierType mnemonic_modifier = 0;
432 gchar *key, *temp_keybinding;
434 child = gtk_bin_get_child (GTK_BIN (temp_item));
438 parent = gtk_widget_get_parent (temp_item);
440 /* parent can be NULL when activating a window from the panel */
443 if (GTK_IS_MENU_BAR (parent))
447 toplevel = gtk_widget_get_toplevel (parent);
448 if (toplevel && GTK_IS_WINDOW (toplevel))
450 gtk_window_get_mnemonic_modifier (GTK_WINDOW (toplevel));
453 if (GTK_IS_LABEL (child))
455 key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (child));
456 if (key_val != GDK_KEY_VoidSymbol)
458 key = gtk_accelerator_name (key_val, mnemonic_modifier);
460 temp_keybinding = g_strconcat (key, ":", full_keybinding, NULL);
462 temp_keybinding = g_strdup (key);
464 if (temp_item == item)
465 item_keybinding = g_strdup (key);
468 g_free (full_keybinding);
469 full_keybinding = temp_keybinding;
474 g_free (full_keybinding);
475 full_keybinding = NULL;
480 /* We have reached the menu bar so we are finished */
481 if (GTK_IS_MENU_BAR (parent))
484 g_return_val_if_fail (GTK_IS_MENU (parent), NULL);
485 temp_item = gtk_menu_get_attach_widget (GTK_MENU (parent));
486 if (!GTK_IS_MENU_ITEM (temp_item))
488 /* Menu is attached to something other than a menu item;
489 * probably an option menu
491 g_free (full_keybinding);
492 full_keybinding = NULL;
497 parent = gtk_widget_get_parent (item);
498 if (GTK_IS_MENU (parent))
500 GtkAccelGroup *group;
503 group = gtk_menu_get_accel_group (GTK_MENU (parent));
505 key = gtk_accel_group_find (group, find_accel_by_widget, item);
509 child = gtk_bin_get_child (GTK_BIN (item));
510 if (GTK_IS_ACCEL_LABEL (child))
512 GtkAccelLabel *accel_label;
513 GClosure *accel_closure;
515 accel_label = GTK_ACCEL_LABEL (child);
516 g_object_get (accel_label, "accel-closure", &accel_closure, NULL);
519 key = gtk_accel_group_find (gtk_accel_group_from_accel_closure (accel_closure),
520 find_accel_by_closure,
522 g_closure_unref (accel_closure);
528 accelerator = gtk_accelerator_name (key->accel_key, key->accel_mods);
531 /* Concatenate the bindings */
532 if (item_keybinding || full_keybinding || accelerator)
537 keybinding = g_strconcat (item_keybinding, KEYBINDING_SEPARATOR, NULL);
538 g_free (item_keybinding);
541 keybinding = g_strdup (KEYBINDING_SEPARATOR);
545 temp = g_strconcat (keybinding, full_keybinding,
546 KEYBINDING_SEPARATOR, NULL);
547 g_free (full_keybinding);
550 temp = g_strconcat (keybinding, KEYBINDING_SEPARATOR, NULL);
556 temp = g_strconcat (keybinding, accelerator, NULL);
557 g_free (accelerator);
567 atk_action_interface_init (AtkActionIface *iface)
569 iface->do_action = gtk_menu_item_accessible_do_action;
570 iface->get_n_actions = gtk_menu_item_accessible_get_n_actions;
571 iface->get_name = gtk_menu_item_accessible_action_get_name;
572 iface->get_keybinding = gtk_menu_item_accessible_get_keybinding;
576 menu_item_selection (GtkMenuItem *item,
579 AtkObject *obj, *parent;
582 obj = gtk_widget_get_accessible (GTK_WIDGET (item));
583 atk_object_notify_state_change (obj, ATK_STATE_SELECTED, selected);
585 for (i = 0; i < atk_object_get_n_accessible_children (obj); i++)
588 child = atk_object_ref_accessible_child (obj, i);
589 atk_object_notify_state_change (child, ATK_STATE_SHOWING, selected);
590 g_object_unref (child);
592 parent = atk_object_get_parent (obj);
593 g_signal_emit_by_name (parent, "selection_changed");
597 menu_item_select (GtkMenuItem *item)
599 menu_item_selection (item, TRUE);
603 menu_item_deselect (GtkMenuItem *item)
605 menu_item_selection (item, FALSE);