]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailmenuitem.c
Do not disable deprecation guards
[~andy/gtk] / modules / other / gail / gailmenuitem.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 <gdk/gdkkeysyms.h>
23 #include "gailmenuitem.h"
24 #include "gailsubmenuitem.h"
25 #include <libgail-util/gailmisc.h>
26
27 #define KEYBINDING_SEPARATOR ";"
28
29 static void gail_menu_item_class_init  (GailMenuItemClass *klass);
30 static void gail_menu_item_init        (GailMenuItem      *menu_item);
31
32 static void                  gail_menu_item_real_initialize
33                                                           (AtkObject       *obj,
34                                                            gpointer        data);
35 static gint                  gail_menu_item_get_n_children (AtkObject      *obj);
36 static AtkObject*            gail_menu_item_ref_child      (AtkObject      *obj,
37                                                             gint           i);
38 static AtkStateSet*          gail_menu_item_ref_state_set  (AtkObject      *obj);
39 static void                  gail_menu_item_finalize       (GObject        *object);
40 static void                  gail_menu_item_label_map_gtk  (GtkWidget     *widget,
41                                                             gpointer       data);
42 static void                  gail_menu_item_init_textutil  (GailMenuItem  *item,
43                                                             GtkWidget     *label);
44 static void                  gail_menu_item_notify_label_gtk (GObject       *obj,
45                                                               GParamSpec    *pspec,
46                                                               gpointer      data);
47
48
49 static void                  atk_action_interface_init     (AtkActionIface *iface);
50 static gboolean              gail_menu_item_do_action      (AtkAction      *action,
51                                                             gint           i);
52 static gboolean              idle_do_action                (gpointer       data);
53 static gint                  gail_menu_item_get_n_actions  (AtkAction      *action);
54 static G_CONST_RETURN gchar* gail_menu_item_get_description(AtkAction      *action,
55                                                             gint           i);
56 static G_CONST_RETURN gchar* gail_menu_item_get_name       (AtkAction      *action,
57                                                             gint           i);
58 static G_CONST_RETURN gchar* gail_menu_item_get_keybinding (AtkAction      *action,
59                                                             gint           i);
60 static gboolean              gail_menu_item_set_description(AtkAction      *action,
61                                                             gint           i,
62                                                             const gchar    *desc);
63 static void                  menu_item_select              (GtkMenuItem        *item);
64 static void                  menu_item_deselect            (GtkMenuItem        *item);
65 static void                  menu_item_selection           (GtkMenuItem        *item,
66                                                             gboolean       selected);
67 static gboolean              find_accel                    (GtkAccelKey    *key,
68                                                             GClosure       *closure,
69                                                             gpointer       data);
70 static gboolean              find_accel_new                (GtkAccelKey    *key,
71                                                             GClosure       *closure,
72                                                             gpointer       data);
73 /* atktext.h */
74 static void       atk_text_interface_init          (AtkTextIface      *iface);
75
76 static gchar*     gail_menu_item_get_text          (AtkText           *text,
77                                                     gint               start_pos,
78                                                     gint               end_pos);
79 static gunichar   gail_menu_item_get_character_at_offset (AtkText     *text,
80                                                           gint         offset);
81 static gchar*     gail_menu_item_get_text_before_offset  (AtkText     *text,
82                                                           gint         offset,
83                                                           AtkTextBoundary  boundary_type,
84                                                           gint        *start_offset,
85                                                           gint        *end_offset);
86 static gchar*     gail_menu_item_get_text_at_offset      (AtkText     *text,
87                                                           gint         offset,
88                                                           AtkTextBoundary   boundary_type,
89                                                           gint        *start_offset,
90                                                           gint        *end_offset);
91 static gchar*     gail_menu_item_get_text_after_offset   (AtkText     *text,
92                                                           gint         offset,
93                                                           AtkTextBoundary   boundary_type,
94                                                           gint        *start_offset,
95                                                           gint        *end_offset);
96 static gint       gail_menu_item_get_character_count     (AtkText     *text);
97 static void       gail_menu_item_get_character_extents   (AtkText     *text,
98                                                           gint         offset,
99                                                           gint        *x,
100                                                           gint        *y,
101                                                           gint        *width,
102                                                           gint        *height,
103                                                           AtkCoordType  coords);
104 static gint      gail_menu_item_get_offset_at_point      (AtkText     *text,
105                                                           gint         x,
106                                                           gint         y,
107                                                           AtkCoordType  coords);
108 static AtkAttributeSet* gail_menu_item_get_run_attributes (AtkText    *text,
109                                                            gint        offset,
110                                                            gint       *start_offset,
111                                                            gint       *end_offset);
112 static AtkAttributeSet* gail_menu_item_get_default_attributes (AtkText *text);
113 static GtkWidget*       get_label_from_container   (GtkWidget    *container);
114
115
116 G_DEFINE_TYPE_WITH_CODE (GailMenuItem, gail_menu_item, GAIL_TYPE_CONTAINER,
117                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
118                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
119
120 static void
121 gail_menu_item_class_init (GailMenuItemClass *klass)
122 {
123   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
124   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
125
126   gobject_class->finalize = gail_menu_item_finalize;
127
128   class->get_n_children = gail_menu_item_get_n_children;
129   class->ref_child = gail_menu_item_ref_child;
130   class->ref_state_set = gail_menu_item_ref_state_set;
131   class->initialize = gail_menu_item_real_initialize;
132 }
133
134 static void
135 gail_menu_item_real_initialize (AtkObject *obj,
136                                 gpointer  data)
137 {
138   GtkWidget *widget;
139   GtkWidget *parent;
140   GailMenuItem *item = GAIL_MENU_ITEM (obj);
141   GtkWidget *label;
142
143   ATK_OBJECT_CLASS (gail_menu_item_parent_class)->initialize (obj, data);
144
145   item->textutil = NULL;
146   item->text = NULL;
147
148   label = get_label_from_container (GTK_WIDGET (data));
149   if (GTK_IS_LABEL (label))
150     {
151       if (gtk_widget_get_mapped (label))
152         gail_menu_item_init_textutil (item, label);
153       else
154         g_signal_connect (label,
155                           "map",
156                           G_CALLBACK (gail_menu_item_label_map_gtk),
157                           item);
158     }
159
160   g_signal_connect (data,
161                     "select",
162                     G_CALLBACK (menu_item_select),
163                     NULL);
164   g_signal_connect (data,
165                     "deselect",
166                     G_CALLBACK (menu_item_deselect),
167                     NULL);
168   widget = GTK_WIDGET (data);
169   parent = gtk_widget_get_parent (widget);
170   if (GTK_IS_MENU (parent))
171     {
172       GtkWidget *parent_widget;
173
174       parent_widget =  gtk_menu_get_attach_widget (GTK_MENU (parent));
175
176       if (!GTK_IS_MENU_ITEM (parent_widget))
177         parent_widget = gtk_widget_get_parent (widget);
178        if (parent_widget)
179         {
180           atk_object_set_parent (obj, gtk_widget_get_accessible (parent_widget));
181         }
182     }
183   g_object_set_data (G_OBJECT (obj), "atk-component-layer",
184                      GINT_TO_POINTER (ATK_LAYER_POPUP));
185
186   if (GTK_IS_TEAROFF_MENU_ITEM (data))
187     obj->role = ATK_ROLE_TEAR_OFF_MENU_ITEM;
188   else if (GTK_IS_SEPARATOR_MENU_ITEM (data))
189     obj->role = ATK_ROLE_SEPARATOR;
190   else
191     obj->role = ATK_ROLE_MENU_ITEM;
192 }
193
194 static void
195 gail_menu_item_init (GailMenuItem *menu_item)
196 {
197   menu_item->click_keybinding = NULL;
198   menu_item->click_description = NULL;
199 }
200
201 static void
202 gail_menu_item_label_map_gtk (GtkWidget *widget,
203                               gpointer data)
204 {
205   GailMenuItem *item;
206
207   item = GAIL_MENU_ITEM (data);
208   gail_menu_item_init_textutil (item, widget);
209 }
210
211 static void
212 gail_menu_item_notify_label_gtk (GObject           *obj,
213                                  GParamSpec        *pspec,
214                                  gpointer           data)
215 {
216   AtkObject* atk_obj = ATK_OBJECT (data);
217   GtkLabel *label;
218   GailMenuItem *menu_item;
219
220   if (strcmp (pspec->name, "label") == 0)
221     {
222       const gchar* label_text;
223
224       label = GTK_LABEL (obj);
225
226       label_text = gtk_label_get_text (label);
227
228       menu_item = GAIL_MENU_ITEM (atk_obj);
229       gail_text_util_text_setup (menu_item->textutil, label_text);
230
231       if (atk_obj->name == NULL)
232       {
233         /*
234          * The label has changed so notify a change in accessible-name
235          */
236         g_object_notify (G_OBJECT (atk_obj), "accessible-name");
237       }
238       /*
239        * The label is the only property which can be changed
240        */
241       g_signal_emit_by_name (atk_obj, "visible_data_changed");
242     }
243 }
244
245 static void
246 gail_menu_item_init_textutil (GailMenuItem  *item,
247                               GtkWidget *label)
248 {
249   const gchar *label_text;
250
251   if (item->textutil == NULL)
252     {
253       item->textutil = gail_text_util_new ();
254       g_signal_connect (label,
255                         "notify",
256                         (GCallback) gail_menu_item_notify_label_gtk,
257                         item);     
258     }
259   label_text = gtk_label_get_text (GTK_LABEL (label));
260   gail_text_util_text_setup (item->textutil, label_text);
261 }
262
263 /* atktext.h */
264
265 static void
266 atk_text_interface_init (AtkTextIface *iface)
267 {
268   iface->get_text = gail_menu_item_get_text;
269   iface->get_character_at_offset = gail_menu_item_get_character_at_offset;
270   iface->get_text_before_offset = gail_menu_item_get_text_before_offset;
271   iface->get_text_at_offset = gail_menu_item_get_text_at_offset;
272   iface->get_text_after_offset = gail_menu_item_get_text_after_offset;
273   iface->get_character_count = gail_menu_item_get_character_count;
274   iface->get_character_extents = gail_menu_item_get_character_extents;
275   iface->get_offset_at_point = gail_menu_item_get_offset_at_point;
276   iface->get_run_attributes = gail_menu_item_get_run_attributes;
277   iface->get_default_attributes = gail_menu_item_get_default_attributes;
278 }
279
280 static gchar*
281 gail_menu_item_get_text (AtkText *text,
282                          gint    start_pos,
283                          gint    end_pos)
284 {
285   GtkWidget *widget;
286   GtkWidget *label;
287   GailMenuItem *item;
288   const gchar *label_text;
289
290   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
291   if (widget == NULL)
292     /* State is defunct */
293     return NULL;
294
295   label = get_label_from_container (widget);
296
297   if (!GTK_IS_LABEL (label))
298     return NULL;
299
300   item = GAIL_MENU_ITEM (text);
301   if (!item->textutil) 
302     gail_menu_item_init_textutil (item, label);
303
304   label_text = gtk_label_get_text (GTK_LABEL (label));
305
306   if (label_text == NULL)
307     return NULL;
308   else
309   {
310     return gail_text_util_get_substring (item->textutil, 
311                                          start_pos, end_pos);
312   }
313 }
314
315 static gchar*
316 gail_menu_item_get_text_before_offset (AtkText         *text,
317                                         gint            offset,
318                                         AtkTextBoundary boundary_type,
319                                         gint            *start_offset,
320                                         gint            *end_offset)
321 {
322   GtkWidget *widget;
323   GtkWidget *label;
324   GailMenuItem *item;
325   
326   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
327   
328   if (widget == NULL)
329     /* State is defunct */
330     return NULL;
331   
332   /* Get label */
333   label = get_label_from_container (widget);
334
335   if (!GTK_IS_LABEL(label))
336     return NULL;
337
338   item = GAIL_MENU_ITEM (text);
339   if (!item->textutil)
340     gail_menu_item_init_textutil (item, label);
341
342   return gail_text_util_get_text (item->textutil,
343                            gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET, 
344                            boundary_type, offset, start_offset, end_offset); 
345 }
346
347 static gchar*
348 gail_menu_item_get_text_at_offset (AtkText         *text,
349                                    gint            offset,
350                                    AtkTextBoundary boundary_type,
351                                    gint            *start_offset,
352                                    gint            *end_offset)
353 {
354   GtkWidget *widget;
355   GtkWidget *label;
356   GailMenuItem *item;
357  
358   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
359   
360   if (widget == NULL)
361     /* State is defunct */
362     return NULL;
363   
364   /* Get label */
365   label = get_label_from_container (widget);
366
367   if (!GTK_IS_LABEL(label))
368     return NULL;
369
370   item = GAIL_MENU_ITEM (text);
371   if (!item->textutil)
372     gail_menu_item_init_textutil (item, label);
373
374   return gail_text_util_get_text (item->textutil,
375                               gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET, 
376                               boundary_type, offset, start_offset, end_offset);
377 }
378
379 static gchar*
380 gail_menu_item_get_text_after_offset (AtkText         *text,
381                                       gint            offset,
382                                       AtkTextBoundary boundary_type,
383                                       gint            *start_offset,
384                                       gint            *end_offset)
385 {
386   GtkWidget *widget;
387   GtkWidget *label;
388   GailMenuItem *item;
389
390   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
391   
392   if (widget == NULL)
393   {
394     /* State is defunct */
395     return NULL;
396   }
397   
398   /* Get label */
399   label = get_label_from_container (widget);
400
401   if (!GTK_IS_LABEL(label))
402     return NULL;
403
404   item = GAIL_MENU_ITEM (text);
405   if (!item->textutil)
406     gail_menu_item_init_textutil (item, label);
407
408   return gail_text_util_get_text (item->textutil,
409                            gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET, 
410                            boundary_type, offset, start_offset, end_offset);
411 }
412
413 static gint
414 gail_menu_item_get_character_count (AtkText *text)
415 {
416   GtkWidget *widget;
417   GtkWidget *label;
418
419   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
420   if (widget == NULL)
421     /* State is defunct */
422     return 0;
423
424   label = get_label_from_container (widget);
425
426   if (!GTK_IS_LABEL(label))
427     return 0;
428
429   return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
430 }
431
432 static void
433 gail_menu_item_get_character_extents (AtkText      *text,
434                                       gint         offset,
435                                       gint         *x,
436                                       gint         *y,
437                                       gint         *width,
438                                       gint         *height,
439                                       AtkCoordType coords)
440 {
441   GtkWidget *widget;
442   GtkWidget *label;
443   PangoRectangle char_rect;
444   gint index, x_layout, y_layout;
445   const gchar *label_text;
446  
447   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
448
449   if (widget == NULL)
450     /* State is defunct */
451     return;
452
453   label = get_label_from_container (widget);
454
455   if (!GTK_IS_LABEL(label))
456     return;
457   
458   gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
459   label_text = gtk_label_get_text (GTK_LABEL (label));
460   index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
461   pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
462   
463   gail_misc_get_extents_from_pango_rectangle (label, &char_rect, 
464                     x_layout, y_layout, x, y, width, height, coords);
465
466
467 static gint 
468 gail_menu_item_get_offset_at_point (AtkText      *text,
469                                gint         x,
470                                gint         y,
471                                AtkCoordType coords)
472
473   GtkWidget *widget;
474   GtkWidget *label;
475   gint index, x_layout, y_layout;
476   const gchar *label_text;
477
478   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
479   if (widget == NULL)
480     /* State is defunct */
481     return -1;
482
483   label = get_label_from_container (widget);
484
485   if (!GTK_IS_LABEL(label))
486     return -1;
487   
488   gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
489   
490   index = gail_misc_get_index_at_point_in_layout (label, 
491                                               gtk_label_get_layout (GTK_LABEL (label)), 
492                                               x_layout, y_layout, x, y, coords);
493   label_text = gtk_label_get_text (GTK_LABEL (label));
494   if (index == -1)
495     {
496       if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
497         return g_utf8_strlen (label_text, -1);
498
499       return index;  
500     }
501   else
502     return g_utf8_pointer_to_offset (label_text, label_text + index);  
503 }
504
505 static AtkAttributeSet*
506 gail_menu_item_get_run_attributes (AtkText *text,
507                               gint    offset,
508                               gint    *start_offset,
509                               gint    *end_offset)
510 {
511   GtkWidget *widget;
512   GtkWidget *label;
513   AtkAttributeSet *at_set = NULL;
514   GtkJustification justify;
515   GtkTextDirection dir;
516
517   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
518   if (widget == NULL)
519     /* State is defunct */
520     return NULL;
521
522   label = get_label_from_container (widget);
523
524   if (!GTK_IS_LABEL(label))
525     return NULL;
526   
527   /* Get values set for entire label, if any */
528   justify = gtk_label_get_justify (GTK_LABEL (label));
529   if (justify != GTK_JUSTIFY_CENTER)
530     {
531       at_set = gail_misc_add_attribute (at_set, 
532                                         ATK_TEXT_ATTR_JUSTIFICATION,
533      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
534     }
535   dir = gtk_widget_get_direction (label);
536   if (dir == GTK_TEXT_DIR_RTL)
537     {
538       at_set = gail_misc_add_attribute (at_set, 
539                                         ATK_TEXT_ATTR_DIRECTION,
540      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
541     }
542
543   at_set = gail_misc_layout_get_run_attributes (at_set,
544                                                 gtk_label_get_layout (GTK_LABEL (label)),
545                                                 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
546                                                 offset,
547                                                 start_offset,
548                                                 end_offset);
549   return at_set;
550 }
551
552 static AtkAttributeSet*
553 gail_menu_item_get_default_attributes (AtkText *text)
554 {
555   GtkWidget *widget;
556   GtkWidget *label;
557   AtkAttributeSet *at_set = NULL;
558
559   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
560   if (widget == NULL)
561     /* State is defunct */
562     return NULL;
563
564   label = get_label_from_container (widget);
565
566   if (!GTK_IS_LABEL(label))
567     return NULL;
568
569   at_set = gail_misc_get_default_attributes (at_set,
570                                              gtk_label_get_layout (GTK_LABEL (label)),
571                                              widget);
572   return at_set;
573 }
574
575 static gunichar 
576 gail_menu_item_get_character_at_offset (AtkText *text,
577                                    gint   offset)
578 {
579   GtkWidget *widget;
580   GtkWidget *label;
581   const gchar *string;
582   gchar *index;
583
584   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
585   if (widget == NULL)
586     /* State is defunct */
587     return '\0';
588
589   label = get_label_from_container (widget);
590
591   if (!GTK_IS_LABEL(label))
592     return '\0';
593   string = gtk_label_get_text (GTK_LABEL (label));
594   if (offset >= g_utf8_strlen (string, -1))
595     return '\0';
596   index = g_utf8_offset_to_pointer (string, offset);
597
598   return g_utf8_get_char (index);
599 }
600
601 static GtkWidget*
602 get_label_from_container (GtkWidget *container)
603 {
604   GtkWidget *label;
605   GList *children, *tmp_list;
606
607   if (!GTK_IS_CONTAINER (container))
608     return NULL;
609  
610   children = gtk_container_get_children (GTK_CONTAINER (container));
611   label = NULL;
612
613   for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next) 
614     {
615       if (GTK_IS_LABEL (tmp_list->data))
616         {
617            label = tmp_list->data;
618            break;
619         }
620       /*
621  *        * Get label from menu item in desktop background preferences
622  *               * option menu. See bug #144084.
623  *                      */
624       else if (GTK_IS_BOX (tmp_list->data))
625         {
626            label = get_label_from_container (GTK_WIDGET (tmp_list->data));
627            if (label)
628              break;
629         }
630     }
631   g_list_free (children);
632
633   return label;
634 }
635
636 AtkObject*
637 gail_menu_item_new (GtkWidget *widget)
638 {
639   GObject *object;
640   AtkObject *accessible;
641   
642   g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), NULL);
643
644   if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)))
645     return gail_sub_menu_item_new (widget);
646
647   object = g_object_new (GAIL_TYPE_MENU_ITEM, NULL);
648
649   accessible = ATK_OBJECT (object);
650   atk_object_initialize (accessible, widget);
651
652   return accessible;
653 }
654
655 GList *
656 get_children (GtkWidget *submenu)
657 {
658   GList *children;
659
660   children = gtk_container_get_children (GTK_CONTAINER (submenu));
661   if (g_list_length (children) == 0)
662     {
663       /*
664        * If menu is empty it may be because the menu items are created only 
665        * on demand. For example, in gnome-panel the menu items are created
666        * only when "show" signal is emitted on the menu.
667        *
668        * The following hack forces the menu items to be created.
669        */
670       if (!gtk_widget_get_visible (submenu))
671         {
672           /* FIXME GTK_WIDGET_SET_FLAGS (submenu, GTK_VISIBLE); */
673           g_signal_emit_by_name (submenu, "show");
674           /* FIXME GTK_WIDGET_UNSET_FLAGS (submenu, GTK_VISIBLE); */
675         }
676       g_list_free (children);
677       children = gtk_container_get_children (GTK_CONTAINER (submenu));
678     }
679   return children;
680 }
681
682 /*
683  * If a menu item has a submenu return the items of the submenu as the 
684  * accessible children; otherwise expose no accessible children.
685  */
686 static gint
687 gail_menu_item_get_n_children (AtkObject* obj)
688 {
689   GtkWidget *widget;
690   GtkWidget *submenu;
691   gint count = 0;
692
693   g_return_val_if_fail (GAIL_IS_MENU_ITEM (obj), count);
694
695   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
696   if (widget == NULL)
697     return count;
698
699   submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
700   if (submenu)
701     {
702       GList *children;
703
704       children = get_children (submenu);
705       count = g_list_length (children);
706       g_list_free (children);
707     }
708   return count;
709 }
710
711 static AtkObject*
712 gail_menu_item_ref_child (AtkObject *obj,
713                           gint       i)
714 {
715   AtkObject  *accessible;
716   GtkWidget *widget;
717   GtkWidget *submenu;
718
719   g_return_val_if_fail (GAIL_IS_MENU_ITEM (obj), NULL);
720   g_return_val_if_fail ((i >= 0), NULL);
721
722   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
723   if (widget == NULL)
724     return NULL;
725
726   submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
727   if (submenu)
728     {
729       GList *children;
730       GList *tmp_list;
731
732       children = get_children (submenu);
733       tmp_list = g_list_nth (children, i);
734       if (!tmp_list)
735         {
736           g_list_free (children);
737           return NULL;
738         }
739       accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data));
740       g_list_free (children);
741       g_object_ref (accessible);
742     }
743   else
744     accessible = NULL;
745
746   return accessible;
747 }
748
749 static AtkStateSet*
750 gail_menu_item_ref_state_set (AtkObject *obj)
751 {
752   AtkObject *menu_item;
753   AtkStateSet *state_set, *parent_state_set;
754
755   state_set = ATK_OBJECT_CLASS (gail_menu_item_parent_class)->ref_state_set (obj);
756
757   menu_item = atk_object_get_parent (obj);
758
759   if (menu_item)
760     {
761       if (!GTK_IS_MENU_ITEM (gtk_accessible_get_widget (GTK_ACCESSIBLE (menu_item))))
762         return state_set;
763
764       parent_state_set = atk_object_ref_state_set (menu_item);
765       if (!atk_state_set_contains_state (parent_state_set, ATK_STATE_SELECTED))
766         {
767           atk_state_set_remove_state (state_set, ATK_STATE_FOCUSED);
768           atk_state_set_remove_state (state_set, ATK_STATE_SHOWING);
769         }
770     }
771   return state_set;
772 }
773
774 static void
775 atk_action_interface_init (AtkActionIface *iface)
776 {
777   iface->do_action = gail_menu_item_do_action;
778   iface->get_n_actions = gail_menu_item_get_n_actions;
779   iface->get_description = gail_menu_item_get_description;
780   iface->get_name = gail_menu_item_get_name;
781   iface->get_keybinding = gail_menu_item_get_keybinding;
782   iface->set_description = gail_menu_item_set_description;
783 }
784
785 static gboolean
786 gail_menu_item_do_action (AtkAction *action,
787                           gint      i)
788 {
789   if (i == 0)
790     {
791       GtkWidget *item;
792       GailMenuItem *gail_menu_item;
793
794       item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
795       if (item == NULL)
796         /* State is defunct */
797         return FALSE;
798
799       if (!gtk_widget_get_sensitive (item) || !gtk_widget_get_visible (item))
800         return FALSE;
801
802       gail_menu_item = GAIL_MENU_ITEM (action);
803       if (gail_menu_item->action_idle_handler)
804         return FALSE;
805       else
806         {
807           gail_menu_item->action_idle_handler =
808             gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
809                                        idle_do_action,
810                                        g_object_ref (gail_menu_item),
811                                        (GDestroyNotify) g_object_unref);
812         }
813       return TRUE;
814     }
815   else
816     return FALSE;
817 }
818
819 static void
820 ensure_menus_unposted (GailMenuItem *menu_item)
821 {
822   AtkObject *parent;
823   GtkWidget *widget;
824
825   parent = atk_object_get_parent (ATK_OBJECT (menu_item));
826   while (parent)
827     {
828       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
829       if (GTK_IS_MENU (widget))
830         {
831           if (gtk_widget_get_mapped (widget))
832             gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
833
834           return;
835         }
836       parent = atk_object_get_parent (parent);
837     }
838 }
839
840 static gboolean
841 idle_do_action (gpointer data)
842 {
843   GtkWidget *item;
844   GtkWidget *item_parent;
845   GailMenuItem *menu_item;
846   gboolean item_mapped;
847
848   menu_item = GAIL_MENU_ITEM (data);
849   menu_item->action_idle_handler = 0;
850   item = gtk_accessible_get_widget (GTK_ACCESSIBLE (menu_item));
851   if (item == NULL /* State is defunct */ ||
852       !gtk_widget_get_sensitive (item) || !gtk_widget_get_visible (item))
853     return FALSE;
854
855   item_parent = gtk_widget_get_parent (item);
856   gtk_menu_shell_select_item (GTK_MENU_SHELL (item_parent), item);
857   item_mapped = gtk_widget_get_mapped (item);
858   /*
859    * This is what is called when <Return> is pressed for a menu item
860    */
861   g_signal_emit_by_name (item_parent, "activate_current",  
862                          /*force_hide*/ 1); 
863   if (!item_mapped)
864     ensure_menus_unposted (menu_item);
865
866   return FALSE;
867 }
868
869 static gint
870 gail_menu_item_get_n_actions (AtkAction *action)
871 {
872   /*
873    * Menu item has 1 action
874    */
875   return 1;
876 }
877
878 static G_CONST_RETURN gchar*
879 gail_menu_item_get_description (AtkAction *action,
880                                 gint      i)
881 {
882   if (i == 0)
883     {
884       GailMenuItem *item;
885
886       item = GAIL_MENU_ITEM (action);
887       return item->click_description;
888     }
889   else
890     return NULL;
891 }
892
893 static G_CONST_RETURN gchar*
894 gail_menu_item_get_name (AtkAction *action,
895                          gint      i)
896 {
897   if (i == 0)
898     return "click";
899   else
900     return NULL;
901 }
902
903 static G_CONST_RETURN gchar*
904 gail_menu_item_get_keybinding (AtkAction *action,
905                                gint      i)
906 {
907   /*
908    * This function returns a string of the form A;B;C where
909    * A is the keybinding for the widget; B is the keybinding to traverse
910    * from the menubar and C is the accelerator.
911    * The items in the keybinding to traverse from the menubar are separated
912    * by ":".
913    */
914   GailMenuItem  *gail_menu_item;
915   gchar *keybinding = NULL;
916   gchar *item_keybinding = NULL;
917   gchar *full_keybinding = NULL;
918   gchar *accelerator = NULL;
919
920   gail_menu_item = GAIL_MENU_ITEM (action);
921   if (i == 0)
922     {
923       GtkWidget *item;
924       GtkWidget *temp_item;
925       GtkWidget *child;
926       GtkWidget *parent;
927
928       item = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
929       if (item == NULL)
930         /* State is defunct */
931         return NULL;
932
933       temp_item = item;
934       while (TRUE)
935         {
936           GdkModifierType mnemonic_modifier = 0;
937           guint key_val;
938           gchar *key, *temp_keybinding;
939
940           child = gtk_bin_get_child (GTK_BIN (temp_item));
941           if (child == NULL)
942             {
943               /* Possibly a tear off menu item; it could also be a menu 
944                * separator generated by gtk_item_factory_create_items()
945                */
946               return NULL;
947             }
948           parent = gtk_widget_get_parent (temp_item);
949           if (!parent)
950             {
951               /*
952                * parent can be NULL when activating a window from the panel
953                */
954               return NULL;
955             }
956           g_return_val_if_fail (GTK_IS_MENU_SHELL (parent), NULL);
957           if (GTK_IS_MENU_BAR (parent))
958             {
959               GtkWidget *toplevel;
960
961               toplevel = gtk_widget_get_toplevel (parent);
962               if (toplevel && GTK_IS_WINDOW (toplevel))
963                 mnemonic_modifier = gtk_window_get_mnemonic_modifier (
964                                        GTK_WINDOW (toplevel));
965             }
966           if (GTK_IS_LABEL (child))
967             {
968               key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (child));
969               if (key_val != GDK_KEY_VoidSymbol)
970                 {
971                   key = gtk_accelerator_name (key_val, mnemonic_modifier);
972                   if (full_keybinding)
973                     temp_keybinding = g_strconcat (key, ":", full_keybinding, NULL);
974                   else 
975                     temp_keybinding = g_strconcat (key, NULL);
976                   if (temp_item == item)
977                     {
978                       item_keybinding = g_strdup (key); 
979                     }
980                   g_free (key);
981                   g_free (full_keybinding);
982                   full_keybinding = temp_keybinding;
983                 }
984               else
985                 {
986                   /* No keybinding */
987                   g_free (full_keybinding);
988                   full_keybinding = NULL;
989                   break;
990                 }        
991             }        
992           if (GTK_IS_MENU_BAR (parent))
993             /* We have reached the menu bar so we are finished */
994             break;
995           g_return_val_if_fail (GTK_IS_MENU (parent), NULL);
996           temp_item = gtk_menu_get_attach_widget (GTK_MENU (parent));
997           if (!GTK_IS_MENU_ITEM (temp_item))
998             {
999               /* 
1000                * Menu is attached to something other than a menu item;
1001                * probably an option menu
1002                */
1003               g_free (full_keybinding);
1004               full_keybinding = NULL;
1005               break;
1006             }
1007         }
1008
1009       parent = gtk_widget_get_parent (item);
1010       if (GTK_IS_MENU (parent))
1011         {
1012           GtkAccelGroup *group; 
1013           GtkAccelKey *key;
1014
1015           group = gtk_menu_get_accel_group (GTK_MENU (parent));
1016
1017           if (group)
1018             {
1019               key = gtk_accel_group_find (group, find_accel, item);
1020             }
1021           else
1022             {
1023               /*
1024                * If the menu item is created using GtkAction and GtkUIManager
1025                * we get here.
1026                */
1027               key = NULL;
1028               child = gtk_bin_get_child (GTK_BIN (item));
1029               if (GTK_IS_ACCEL_LABEL (child))
1030                 {
1031                   GtkAccelLabel *accel_label;
1032                   GClosure      *accel_closure;
1033
1034                   accel_label = GTK_ACCEL_LABEL (child);
1035                   g_object_get (accel_label,
1036                                 "accel-closure", &accel_closure,
1037                                 NULL);
1038                   if (accel_closure)
1039                     {
1040                       key = gtk_accel_group_find (gtk_accel_group_from_accel_closure (accel_closure),
1041                                                   find_accel_new,
1042                                                   accel_closure);
1043                     }
1044                
1045                 }
1046             }
1047
1048           if (key)
1049             {           
1050               accelerator = gtk_accelerator_name (key->accel_key,
1051                                                   key->accel_mods);
1052             }
1053         }
1054     }
1055   /*
1056    * Concatenate the bindings
1057    */
1058   if (item_keybinding || full_keybinding || accelerator)
1059     {
1060       gchar *temp;
1061       if (item_keybinding)
1062         {
1063           keybinding = g_strconcat (item_keybinding, KEYBINDING_SEPARATOR, NULL);
1064           g_free (item_keybinding);
1065         }
1066       else
1067         keybinding = g_strconcat (KEYBINDING_SEPARATOR, NULL);
1068
1069       if (full_keybinding)
1070         {
1071           temp = g_strconcat (keybinding, full_keybinding, 
1072                               KEYBINDING_SEPARATOR, NULL);
1073           g_free (full_keybinding);
1074         }
1075       else
1076         temp = g_strconcat (keybinding, KEYBINDING_SEPARATOR, NULL);
1077
1078       g_free (keybinding);
1079       keybinding = temp;
1080       if (accelerator)
1081         {
1082           temp = g_strconcat (keybinding, accelerator, NULL);
1083           g_free (accelerator);
1084           g_free (keybinding);
1085           keybinding = temp;
1086       }
1087     }
1088   g_free (gail_menu_item->click_keybinding);
1089   gail_menu_item->click_keybinding = keybinding;
1090   return keybinding;
1091 }
1092
1093 static gboolean
1094 gail_menu_item_set_description (AtkAction      *action,
1095                                 gint           i,
1096                                 const gchar    *desc)
1097 {
1098   if (i == 0)
1099     {
1100       GailMenuItem *item;
1101
1102       item = GAIL_MENU_ITEM (action);
1103       g_free (item->click_description);
1104       item->click_description = g_strdup (desc);
1105       return TRUE;
1106     }
1107   else
1108     return FALSE;
1109 }
1110
1111 static void
1112 gail_menu_item_finalize (GObject *object)
1113 {
1114   GailMenuItem *menu_item = GAIL_MENU_ITEM (object);
1115
1116   g_free (menu_item->click_keybinding);
1117   g_free (menu_item->click_description);
1118   if (menu_item->action_idle_handler)
1119     {
1120       g_source_remove (menu_item->action_idle_handler);
1121       menu_item->action_idle_handler = 0;
1122     }
1123
1124   if (menu_item->textutil)
1125     {
1126       g_object_unref (menu_item->textutil);
1127     }
1128   if (menu_item->text)
1129     {
1130       g_free (menu_item->text);
1131       menu_item->text = NULL;
1132     }
1133
1134   G_OBJECT_CLASS (gail_menu_item_parent_class)->finalize (object);
1135 }
1136
1137 static void
1138 menu_item_select (GtkMenuItem *item)
1139 {
1140   menu_item_selection (item, TRUE);
1141 }
1142
1143 static void
1144 menu_item_deselect (GtkMenuItem *item)
1145 {
1146   menu_item_selection (item, FALSE);
1147 }
1148
1149 static void
1150 menu_item_selection (GtkMenuItem  *item,
1151                      gboolean selected)
1152 {
1153   AtkObject *obj, *parent;
1154   gint i;
1155
1156   obj = gtk_widget_get_accessible (GTK_WIDGET (item));
1157   atk_object_notify_state_change (obj, ATK_STATE_SELECTED, selected);
1158
1159   for (i = 0; i < atk_object_get_n_accessible_children (obj); i++)
1160     {
1161       AtkObject *child;
1162       child = atk_object_ref_accessible_child (obj, i);
1163       atk_object_notify_state_change (child, ATK_STATE_SHOWING, selected);
1164       g_object_unref (child);
1165     }
1166   parent = atk_object_get_parent (obj);
1167   g_signal_emit_by_name (parent, "selection_changed"); 
1168 }
1169
1170 static gboolean
1171 find_accel (GtkAccelKey *key,
1172             GClosure    *closure,
1173             gpointer     data)
1174 {
1175   /*
1176    * We assume that closure->data points to the widget
1177    * pending gtk_widget_get_accel_closures being made public
1178    */
1179   return data == (gpointer) closure->data;
1180 }
1181
1182 static gboolean
1183 find_accel_new (GtkAccelKey *key,
1184                 GClosure    *closure,
1185                 gpointer     data)
1186 {
1187   return data == (gpointer) closure;
1188 }