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