]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtkmenuitemaccessible.c
a11y: fix private strict regression
[~andy/gtk] / gtk / a11y / gtkmenuitemaccessible.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 #include <string.h>
19 #include <gtk/gtk.h>
20 #include "gtkmenuitemaccessible.h"
21 #include "gtk/gtkmenuitemprivate.h"
22
23 struct _GtkMenuItemAccessiblePrivate
24 {
25   gchar *text;
26 };
27
28 #define KEYBINDING_SEPARATOR ";"
29
30 static void menu_item_select   (GtkMenuItem *item);
31 static void menu_item_deselect (GtkMenuItem *item);
32
33 static GtkWidget *get_label_from_container   (GtkWidget *container);
34 static gchar     *get_text_from_label_widget (GtkWidget *widget);
35
36 static gint menu_item_add_gtk    (GtkContainer   *container,
37                                   GtkWidget      *widget);
38 static gint menu_item_remove_gtk (GtkContainer   *container,
39                                   GtkWidget      *widget);
40
41 static void atk_action_interface_init    (AtkActionIface *iface);
42 static void atk_selection_interface_init (AtkSelectionIface *iface);
43
44 G_DEFINE_TYPE_WITH_CODE (GtkMenuItemAccessible, _gtk_menu_item_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
45                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init);
46                          G_IMPLEMENT_INTERFACE (ATK_TYPE_SELECTION, atk_selection_interface_init))
47
48 static void
49 gtk_menu_item_accessible_initialize (AtkObject *obj,
50                                      gpointer   data)
51 {
52   GtkWidget *widget;
53   GtkWidget *parent;
54   GtkWidget *menu;
55
56   ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->initialize (obj, data);
57
58   g_signal_connect (data, "select", G_CALLBACK (menu_item_select), NULL);
59   g_signal_connect (data, "deselect", G_CALLBACK (menu_item_deselect), NULL);
60
61   widget = GTK_WIDGET (data);
62   parent = gtk_widget_get_parent (widget);
63   if (GTK_IS_MENU (parent))
64     {
65       GtkWidget *parent_widget;
66
67       parent_widget =  gtk_menu_get_attach_widget (GTK_MENU (parent));
68
69       if (!GTK_IS_MENU_ITEM (parent_widget))
70         parent_widget = gtk_widget_get_parent (widget);
71       if (parent_widget)
72         atk_object_set_parent (obj, gtk_widget_get_accessible (parent_widget));
73     }
74
75   _gtk_widget_accessible_set_layer (GTK_WIDGET_ACCESSIBLE (obj), ATK_LAYER_POPUP);
76
77   obj->role = ATK_ROLE_MENU_ITEM;
78
79   menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (data));
80   if (menu)
81     {
82       g_signal_connect (menu, "add", G_CALLBACK (menu_item_add_gtk), NULL);
83       g_signal_connect (menu, "remove", G_CALLBACK (menu_item_remove_gtk), NULL);
84     }
85 }
86
87 static gint
88 gtk_menu_item_accessible_get_n_children (AtkObject *obj)
89 {
90   GtkWidget *widget;
91   GtkWidget *submenu;
92   gint count = 0;
93
94   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
95   if (widget == NULL)
96     return count;
97
98   submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
99   if (submenu)
100     {
101       GList *children;
102
103       children = gtk_container_get_children (GTK_CONTAINER (submenu));
104       count = g_list_length (children);
105       g_list_free (children);
106     }
107   return count;
108 }
109
110 static AtkObject *
111 gtk_menu_item_accessible_ref_child (AtkObject *obj,
112                                     gint       i)
113 {
114   AtkObject  *accessible;
115   GtkWidget *widget;
116   GtkWidget *submenu;
117
118   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
119   if (widget == NULL)
120     return NULL;
121
122   accessible = NULL;
123   submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
124   if (submenu)
125     {
126       GList *children;
127       GList *tmp_list;
128
129       children = gtk_container_get_children (GTK_CONTAINER (submenu));
130       tmp_list = g_list_nth (children, i);
131       if (tmp_list)
132         {
133           accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
134           g_object_ref (accessible);
135         }
136       g_list_free (children);
137     }
138
139   return accessible;
140 }
141
142 static AtkStateSet *
143 gtk_menu_item_accessible_ref_state_set (AtkObject *obj)
144 {
145   AtkObject *menu_item;
146   AtkStateSet *state_set, *parent_state_set;
147
148   state_set = ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->ref_state_set (obj);
149
150   menu_item = atk_object_get_parent (obj);
151
152   if (menu_item)
153     {
154       if (!GTK_IS_MENU_ITEM (gtk_accessible_get_widget (GTK_ACCESSIBLE (menu_item))))
155         return state_set;
156
157       parent_state_set = atk_object_ref_state_set (menu_item);
158       if (!atk_state_set_contains_state (parent_state_set, ATK_STATE_SELECTED))
159         {
160           atk_state_set_remove_state (state_set, ATK_STATE_FOCUSED);
161           atk_state_set_remove_state (state_set, ATK_STATE_SHOWING);
162         }
163       g_object_unref (parent_state_set);
164     }
165
166   return state_set;
167 }
168
169 static AtkRole
170 gtk_menu_item_accessible_get_role (AtkObject *obj)
171 {
172   GtkWidget *widget;
173
174   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
175   if (widget != NULL &&
176       gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)))
177     return ATK_ROLE_MENU;
178
179   return ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->get_role (obj);
180 }
181
182 static const gchar *
183 gtk_menu_item_accessible_get_name (AtkObject *obj)
184 {
185   const gchar *name;
186   GtkWidget *widget;
187   GtkWidget *label;
188   GtkMenuItemAccessible *accessible;
189
190   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
191   if (widget == NULL)
192     return NULL;
193
194   name = ATK_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->get_name (obj);
195   if (name)
196     return name;
197
198   accessible = GTK_MENU_ITEM_ACCESSIBLE (obj);
199   label = get_label_from_container (widget);
200
201   g_free (accessible->priv->text);
202   accessible->priv->text = get_text_from_label_widget (label);
203
204   return accessible->priv->text;
205 }
206
207 static void
208 gtk_menu_item_accessible_finalize (GObject *object)
209 {
210   GtkMenuItemAccessible *accessible = GTK_MENU_ITEM_ACCESSIBLE (object);
211
212   g_free (accessible->priv->text);
213
214   G_OBJECT_CLASS (_gtk_menu_item_accessible_parent_class)->finalize (object);
215 }
216
217 static void
218 gtk_menu_item_accessible_notify_gtk (GObject    *obj,
219                                      GParamSpec *pspec)
220 {
221   AtkObject* atk_obj;
222
223   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (obj));
224
225   if (strcmp (pspec->name, "label") == 0)
226     {
227       if (atk_obj->name == NULL)
228         g_object_notify (G_OBJECT (atk_obj), "accessible-name");
229       g_signal_emit_by_name (atk_obj, "visible-data-changed");
230     }
231   else
232     GTK_WIDGET_ACCESSIBLE_CLASS (_gtk_menu_item_accessible_parent_class)->notify_gtk (obj, pspec);
233 }
234
235 static void
236 _gtk_menu_item_accessible_class_init (GtkMenuItemAccessibleClass *klass)
237 {
238   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
239   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
240   GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
241
242   widget_class->notify_gtk = gtk_menu_item_accessible_notify_gtk;
243
244   gobject_class->finalize = gtk_menu_item_accessible_finalize;
245
246   class->get_n_children = gtk_menu_item_accessible_get_n_children;
247   class->ref_child = gtk_menu_item_accessible_ref_child;
248   class->ref_state_set = gtk_menu_item_accessible_ref_state_set;
249   class->initialize = gtk_menu_item_accessible_initialize;
250   class->get_name = gtk_menu_item_accessible_get_name;
251   class->get_role = gtk_menu_item_accessible_get_role;
252
253   g_type_class_add_private (klass, sizeof (GtkMenuItemAccessiblePrivate));
254 }
255
256 static void
257 _gtk_menu_item_accessible_init (GtkMenuItemAccessible *menu_item)
258 {
259   menu_item->priv = G_TYPE_INSTANCE_GET_PRIVATE (menu_item,
260                                                  GTK_TYPE_MENU_ITEM_ACCESSIBLE,
261                                                  GtkMenuItemAccessiblePrivate);
262 }
263
264 static GtkWidget *
265 get_label_from_container (GtkWidget *container)
266 {
267   GtkWidget *label;
268   GList *children, *tmp_list;
269
270   if (!GTK_IS_CONTAINER (container))
271     return NULL;
272
273   children = gtk_container_get_children (GTK_CONTAINER (container));
274   label = NULL;
275
276   for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next)
277     {
278       if (GTK_IS_LABEL (tmp_list->data))
279         {
280           label = tmp_list->data;
281           break;
282         }
283       else if (GTK_IS_CELL_VIEW (tmp_list->data))
284         {
285           label = tmp_list->data;
286           break;
287         }
288       else if (GTK_IS_BOX (tmp_list->data))
289         {
290           label = get_label_from_container (GTK_WIDGET (tmp_list->data));
291           if (label)
292             break;
293         }
294     }
295   g_list_free (children);
296
297   return label;
298 }
299
300 static gchar *
301 get_text_from_label_widget (GtkWidget *label)
302 {
303   if (GTK_IS_LABEL (label))
304     return g_strdup (gtk_label_get_text (GTK_LABEL (label)));
305   else if (GTK_IS_CELL_VIEW (label))
306     {
307       GList *cells, *l;
308       GtkTreeModel *model;
309       GtkTreeIter iter;
310       GtkTreePath *path;
311       GtkCellArea *area;
312       gchar *text;
313
314       model = gtk_cell_view_get_model (GTK_CELL_VIEW (label));
315       path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (label));
316       gtk_tree_model_get_iter (model, &iter, path);
317       gtk_tree_path_free (path);
318
319       area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (label));
320       gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
321       cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (label));
322
323       text = NULL;
324       for (l = cells; l; l = l->next)
325         {
326           GtkCellRenderer *cell = l->data;
327
328           if (GTK_IS_CELL_RENDERER_TEXT (cell))
329             {
330               g_object_get (cell, "text", &text, NULL);
331               break;
332             }
333         }
334
335       g_list_free (cells);
336
337       return text;
338     }
339
340   return NULL;
341 }
342
343 static void
344 ensure_menus_unposted (GtkMenuItemAccessible *menu_item)
345 {
346   AtkObject *parent;
347   GtkWidget *widget;
348
349   parent = atk_object_get_parent (ATK_OBJECT (menu_item));
350   while (parent)
351     {
352       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
353       if (GTK_IS_MENU (widget))
354         {
355           if (gtk_widget_get_mapped (widget))
356             gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
357
358           return;
359         }
360       parent = atk_object_get_parent (parent);
361     }
362 }
363
364 static gboolean
365 gtk_menu_item_accessible_do_action (AtkAction *action,
366                                     gint       i)
367 {
368   GtkWidget *item, *item_parent;
369   gboolean item_mapped;
370
371   item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
372   if (item == NULL)
373     return FALSE;
374
375   if (i != 0)
376     return FALSE;
377
378   if (!gtk_widget_get_sensitive (item) || !gtk_widget_get_visible (item))
379     return FALSE;
380
381   item_parent = gtk_widget_get_parent (item);
382   if (!GTK_IS_MENU_SHELL (item_parent))
383     return FALSE;
384
385   gtk_menu_shell_select_item (GTK_MENU_SHELL (item_parent), item);
386   item_mapped = gtk_widget_get_mapped (item);
387
388   /* This is what is called when <Return> is pressed for a menu item.
389    * The last argument means 'force hide'.
390    */
391   g_signal_emit_by_name (item_parent, "activate-current", 1);
392   if (!item_mapped)
393     ensure_menus_unposted (GTK_MENU_ITEM_ACCESSIBLE (action));
394
395   return TRUE;
396 }
397
398 static gint
399 gtk_menu_item_accessible_get_n_actions (AtkAction *action)
400 {
401   GtkWidget *item;
402
403   item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
404   if (item == NULL)
405     return 0;
406
407   if (!_gtk_menu_item_is_selectable (item))
408     return 0;
409
410   return 1;
411 }
412
413 static const gchar *
414 gtk_menu_item_accessible_action_get_name (AtkAction *action,
415                                           gint       i)
416 {
417   if (i != 0 || gtk_menu_item_accessible_get_n_actions (action) == 0)
418     return NULL;
419
420   return "click";
421 }
422
423 static gboolean
424 find_accel_by_widget (GtkAccelKey *key,
425                       GClosure    *closure,
426                       gpointer     data)
427 {
428   /* We assume that closure->data points to the widget
429    * pending gtk_widget_get_accel_closures being made public
430    */
431   return data == (gpointer) closure->data;
432 }
433
434 static gboolean
435 find_accel_by_closure (GtkAccelKey *key,
436                        GClosure    *closure,
437                        gpointer     data)
438 {
439   return data == (gpointer) closure;
440 }
441
442 /* This function returns a string of the form A;B;C where A is
443  * the keybinding for the widget; B is the keybinding to traverse
444  * from the menubar and C is the accelerator. The items in the
445  * keybinding to traverse from the menubar are separated by ":".
446  */
447 static const gchar *
448 gtk_menu_item_accessible_get_keybinding (AtkAction *action,
449                                          gint       i)
450 {
451   gchar *keybinding = NULL;
452   gchar *item_keybinding = NULL;
453   gchar *full_keybinding = NULL;
454   gchar *accelerator = NULL;
455   GtkWidget *item;
456   GtkWidget *temp_item;
457   GtkWidget *child;
458   GtkWidget *parent;
459
460   item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
461   if (item == NULL)
462     return NULL;
463
464   if (i != 0)
465     return NULL;
466
467   temp_item = item;
468   while (TRUE)
469     {
470       GdkModifierType mnemonic_modifier = 0;
471       guint key_val;
472       gchar *key, *temp_keybinding;
473
474       child = gtk_bin_get_child (GTK_BIN (temp_item));
475       if (child == NULL)
476         return NULL;
477
478       parent = gtk_widget_get_parent (temp_item);
479       if (!parent)
480         /* parent can be NULL when activating a window from the panel */
481         return NULL;
482
483       if (GTK_IS_MENU_BAR (parent))
484         {
485           GtkWidget *toplevel;
486
487           toplevel = gtk_widget_get_toplevel (parent);
488           if (toplevel && GTK_IS_WINDOW (toplevel))
489             mnemonic_modifier =
490               gtk_window_get_mnemonic_modifier (GTK_WINDOW (toplevel));
491         }
492
493       if (GTK_IS_LABEL (child))
494         {
495           key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (child));
496           if (key_val != GDK_KEY_VoidSymbol)
497             {
498               key = gtk_accelerator_name (key_val, mnemonic_modifier);
499               if (full_keybinding)
500                 temp_keybinding = g_strconcat (key, ":", full_keybinding, NULL);
501               else
502                 temp_keybinding = g_strdup (key);
503
504               if (temp_item == item)
505                 item_keybinding = g_strdup (key);
506
507               g_free (key);
508               g_free (full_keybinding);
509               full_keybinding = temp_keybinding;
510             }
511           else
512             {
513               /* No keybinding */
514               g_free (full_keybinding);
515               full_keybinding = NULL;
516               break;
517             }
518         }
519
520       /* We have reached the menu bar so we are finished */
521       if (GTK_IS_MENU_BAR (parent))
522         break;
523
524       g_return_val_if_fail (GTK_IS_MENU (parent), NULL);
525       temp_item = gtk_menu_get_attach_widget (GTK_MENU (parent));
526       if (!GTK_IS_MENU_ITEM (temp_item))
527         {
528           /* Menu is attached to something other than a menu item;
529            * probably an option menu
530            */
531           g_free (full_keybinding);
532           full_keybinding = NULL;
533           break;
534         }
535     }
536
537   parent = gtk_widget_get_parent (item);
538   if (GTK_IS_MENU (parent))
539     {
540       GtkAccelGroup *group;
541       GtkAccelKey *key;
542
543       group = gtk_menu_get_accel_group (GTK_MENU (parent));
544       if (group)
545         key = gtk_accel_group_find (group, find_accel_by_widget, item);
546       else
547         {
548           key = NULL;
549           child = gtk_bin_get_child (GTK_BIN (item));
550           if (GTK_IS_ACCEL_LABEL (child))
551             {
552               GtkAccelLabel *accel_label;
553               GClosure      *accel_closure;
554
555               accel_label = GTK_ACCEL_LABEL (child);
556               g_object_get (accel_label, "accel-closure", &accel_closure, NULL);
557               if (accel_closure)
558                 {
559                   key = gtk_accel_group_find (gtk_accel_group_from_accel_closure (accel_closure),
560                                               find_accel_by_closure,
561                                               accel_closure);
562                   g_closure_unref (accel_closure);
563                 }
564             }
565         }
566
567      if (key)
568        accelerator = gtk_accelerator_name (key->accel_key, key->accel_mods);
569    }
570
571   /* Concatenate the bindings */
572   if (item_keybinding || full_keybinding || accelerator)
573     {
574       gchar *temp;
575       if (item_keybinding)
576         {
577           keybinding = g_strconcat (item_keybinding, KEYBINDING_SEPARATOR, NULL);
578           g_free (item_keybinding);
579         }
580       else
581         keybinding = g_strdup (KEYBINDING_SEPARATOR);
582
583       if (full_keybinding)
584         {
585           temp = g_strconcat (keybinding, full_keybinding,
586                               KEYBINDING_SEPARATOR, NULL);
587           g_free (full_keybinding);
588         }
589       else
590         temp = g_strconcat (keybinding, KEYBINDING_SEPARATOR, NULL);
591
592       g_free (keybinding);
593       keybinding = temp;
594       if (accelerator)
595         {
596           temp = g_strconcat (keybinding, accelerator, NULL);
597           g_free (accelerator);
598           g_free (keybinding);
599           keybinding = temp;
600       }
601     }
602
603   return keybinding;
604 }
605
606 static void
607 atk_action_interface_init (AtkActionIface *iface)
608 {
609   iface->do_action = gtk_menu_item_accessible_do_action;
610   iface->get_n_actions = gtk_menu_item_accessible_get_n_actions;
611   iface->get_name = gtk_menu_item_accessible_action_get_name;
612   iface->get_keybinding = gtk_menu_item_accessible_get_keybinding;
613 }
614
615 static void
616 menu_item_selection (GtkMenuItem  *item,
617                      gboolean      selected)
618 {
619   AtkObject *obj, *parent;
620   gint i;
621
622   obj = gtk_widget_get_accessible (GTK_WIDGET (item));
623   atk_object_notify_state_change (obj, ATK_STATE_SELECTED, selected);
624
625   for (i = 0; i < atk_object_get_n_accessible_children (obj); i++)
626     {
627       AtkObject *child;
628       child = atk_object_ref_accessible_child (obj, i);
629       atk_object_notify_state_change (child, ATK_STATE_SHOWING, selected);
630       g_object_unref (child);
631     }
632   parent = atk_object_get_parent (obj);
633   g_signal_emit_by_name (parent, "selection-changed");
634 }
635
636 static gboolean
637 gtk_menu_item_accessible_add_selection (AtkSelection *selection,
638                                            gint          i)
639 {
640   GtkMenuShell *shell;
641   GList *kids;
642   guint length;
643   GtkWidget *widget;
644   GtkWidget *menu;
645   GtkWidget *child;
646
647   widget =  gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
648   if (widget == NULL)
649     return FALSE;
650
651   menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
652   if (menu == NULL)
653     return FALSE;
654
655   shell = GTK_MENU_SHELL (menu);
656   kids = gtk_container_get_children (GTK_CONTAINER (shell));
657   length = g_list_length (kids);
658   if (i < 0 || i > length)
659     {
660       g_list_free (kids);
661       return FALSE;
662     }
663
664   child = g_list_nth_data (kids, i);
665   g_list_free (kids);
666   g_return_val_if_fail (GTK_IS_MENU_ITEM (child), FALSE);
667   gtk_menu_shell_select_item (shell, GTK_WIDGET (child));
668   return TRUE;
669 }
670
671 static gboolean
672 gtk_menu_item_accessible_clear_selection (AtkSelection *selection)
673 {
674   GtkWidget *widget;
675   GtkWidget *menu;
676
677   widget =  gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
678   if (widget == NULL)
679     return FALSE;
680
681   menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
682   if (menu == NULL)
683     return FALSE;
684
685   gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
686
687   return TRUE;
688 }
689
690 static AtkObject *
691 gtk_menu_item_accessible_ref_selection (AtkSelection *selection,
692                                            gint          i)
693 {
694   GtkMenuShell *shell;
695   AtkObject *obj;
696   GtkWidget *widget;
697   GtkWidget *menu;
698   GtkWidget *item;
699
700   if (i != 0)
701     return NULL;
702
703   widget =  gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
704   if (widget == NULL)
705     return NULL;
706
707   menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
708   if (menu == NULL)
709     return NULL;
710
711   shell = GTK_MENU_SHELL (menu);
712
713   item = gtk_menu_shell_get_selected_item (shell);
714   if (item != NULL)
715     {
716       obj = gtk_widget_get_accessible (item);
717       g_object_ref (obj);
718       return obj;
719     }
720
721   return NULL;
722 }
723
724 static gint
725 gtk_menu_item_accessible_get_selection_count (AtkSelection *selection)
726 {
727   GtkMenuShell *shell;
728   GtkWidget *widget;
729   GtkWidget *menu;
730
731   widget =  gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
732   if (widget == NULL)
733     return 0;
734
735   menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
736   if (menu == NULL)
737     return 0;
738
739   shell = GTK_MENU_SHELL (menu);
740
741   if (gtk_menu_shell_get_selected_item (shell) != NULL)
742     return 1;
743
744   return 0;
745 }
746
747 static gboolean
748 gtk_menu_item_accessible_is_child_selected (AtkSelection *selection,
749                                                gint          i)
750 {
751   GtkMenuShell *shell;
752   gint j;
753   GtkWidget *widget;
754   GtkWidget *menu;
755   GtkWidget *item;
756   GList *kids;
757
758   widget =  gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
759   if (widget == NULL)
760     return FALSE;
761
762   menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
763   if (menu == NULL)
764     return FALSE;
765
766   shell = GTK_MENU_SHELL (menu);
767
768   item = gtk_menu_shell_get_selected_item (shell);
769   if (item == NULL)
770     return FALSE;
771
772   kids = gtk_container_get_children (GTK_CONTAINER (shell));
773   j = g_list_index (kids, item);
774   g_list_free (kids);
775
776   return j==i;
777 }
778
779 static gboolean
780 gtk_menu_item_accessible_remove_selection (AtkSelection *selection,
781                                               gint          i)
782 {
783   GtkMenuShell *shell;
784   GtkWidget *widget;
785   GtkWidget *menu;
786   GtkWidget *item;
787
788   if (i != 0)
789     return FALSE;
790
791   widget =  gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
792   if (widget == NULL)
793     return FALSE;
794
795   menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
796   if (menu == NULL)
797     return FALSE;
798
799   shell = GTK_MENU_SHELL (menu);
800
801   item = gtk_menu_shell_get_selected_item (shell);
802   if (item && gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)))
803     gtk_menu_shell_deselect (shell);
804
805   return TRUE;
806 }
807
808 static void
809 atk_selection_interface_init (AtkSelectionIface *iface)
810 {
811   iface->add_selection = gtk_menu_item_accessible_add_selection;
812   iface->clear_selection = gtk_menu_item_accessible_clear_selection;
813   iface->ref_selection = gtk_menu_item_accessible_ref_selection;
814   iface->get_selection_count = gtk_menu_item_accessible_get_selection_count;
815   iface->is_child_selected = gtk_menu_item_accessible_is_child_selected;
816   iface->remove_selection = gtk_menu_item_accessible_remove_selection;
817 }
818
819 static gint
820 menu_item_add_gtk (GtkContainer *container,
821                    GtkWidget    *widget)
822 {
823   GtkWidget *parent_widget;
824
825   g_return_val_if_fail (GTK_IS_MENU (container), 1);
826
827   parent_widget = gtk_menu_get_attach_widget (GTK_MENU (container));
828   if (GTK_IS_MENU_ITEM (parent_widget))
829     {
830       GTK_CONTAINER_ACCESSIBLE_CLASS (_gtk_menu_item_accessible_parent_class)->add_gtk (container, widget, gtk_widget_get_accessible (parent_widget));
831
832     }
833   return 1;
834 }
835
836 static gint
837 menu_item_remove_gtk (GtkContainer *container,
838                       GtkWidget    *widget)
839 {
840   GtkWidget *parent_widget;
841
842   g_return_val_if_fail (GTK_IS_MENU (container), 1);
843
844   parent_widget = gtk_menu_get_attach_widget (GTK_MENU (container));
845   if (GTK_IS_MENU_ITEM (parent_widget))
846     {
847       GTK_CONTAINER_ACCESSIBLE_CLASS (_gtk_menu_item_accessible_parent_class)->remove_gtk (container, widget, gtk_widget_get_accessible (parent_widget));
848     }
849   return 1;
850 }
851 static void
852 menu_item_select (GtkMenuItem *item)
853 {
854   menu_item_selection (item, TRUE);
855 }
856
857 static void
858 menu_item_deselect (GtkMenuItem *item)
859 {
860   menu_item_selection (item, FALSE);
861 }