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