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