]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
gdk/abicheck.sh filter out G_GNUC stuff when doing the compare.
[~andy/gtk] / gtk / gtkmenushell.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #define GTK_MENU_INTERNALS
28
29 #include <config.h>
30 #include "gdk/gdkkeysyms.h"
31 #include "gtkalias.h"
32 #include "gtkbindings.h"
33 #include "gtkmain.h"
34 #include "gtkmarshalers.h"
35 #include "gtkmenubar.h"
36 #include "gtkmenuitem.h"
37 #include "gtkmenushell.h"
38 #include "gtktearoffmenuitem.h"
39 #include "gtkwindow.h"
40
41 #define MENU_SHELL_TIMEOUT   500
42
43 enum {
44   DEACTIVATE,
45   SELECTION_DONE,
46   MOVE_CURRENT,
47   ACTIVATE_CURRENT,
48   CANCEL,
49   CYCLE_FOCUS,
50   LAST_SIGNAL
51 };
52
53 typedef void (*GtkMenuShellSignal1) (GtkObject           *object,
54                                      GtkMenuDirectionType arg1,
55                                      gpointer             data);
56 typedef void (*GtkMenuShellSignal2) (GtkObject *object,
57                                      gboolean   arg1,
58                                      gpointer   data);
59
60 /* Terminology:
61  * 
62  * A menu item can be "selected", this means that it is displayed
63  * in the prelight state, and if it has a submenu, that submenu
64  * will be popped up. 
65  * 
66  * A menu is "active" when it is visible onscreen and the user
67  * is selecting from it. A menubar is not active until the user
68  * clicks on one of its menuitems. When a menu is active,
69  * passing the mouse over a submenu will pop it up.
70  *
71  * menu_shell->active_menu_item, is however, not an "active"
72  * menu item (there is no such thing) but rather, the selected
73  * menu item in that MenuShell, if there is one.
74  *
75  * There is also is a concept of the current menu and a current
76  * menu item. The current menu item is the selected menu item
77  * that is furthest down in the heirarchy. (Every active menu_shell
78  * does not necessarily contain a selected menu item, but if
79  * it does, then menu_shell->parent_menu_shell must also contain
80  * a selected menu item. The current menu is the menu that 
81  * contains the current menu_item. It will always have a GTK
82  * grab and receive all key presses.
83  *
84  *
85  * Action signals:
86  *
87  *  ::move_current (GtkMenuDirection *dir)
88  *     Moves the current menu item in direction 'dir':
89  *
90  *       GTK_MENU_DIR_PARENT: To the parent menu shell
91  *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
92  *          a submenu.
93  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
94  *          in this menu.
95  * 
96  *     As a a bit of a hack to get movement between menus and
97  *     menubars working, if submenu_placement is different for
98  *     the menu and its MenuShell then the following apply:
99  * 
100  *       - For 'parent' the current menu is not just moved to
101  *         the parent, but moved to the previous entry in the parent
102  *       - For 'child', if there is no child, then current is
103  *         moved to the next item in the parent.
104  *
105  * 
106  *  ::activate_current (GBoolean *force_hide)
107  *     Activate the current item. If 'force_hide' is true, hide
108  *     the current menu item always. Otherwise, only hide
109  *     it if menu_item->klass->hide_on_activate is true.
110  *
111  *  ::cancel ()
112  *     Cancels the current selection
113  */
114
115 static void gtk_menu_shell_class_init        (GtkMenuShellClass *klass);
116 static void gtk_menu_shell_init              (GtkMenuShell      *menu_shell);
117 static void gtk_menu_shell_realize           (GtkWidget         *widget);
118 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
119                                               GdkEventButton    *event);
120 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
121                                               GdkEventButton    *event);
122 static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
123                                               GdkEventKey       *event);
124 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
125                                               GdkEventCrossing  *event);
126 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
127                                               GdkEventCrossing  *event);
128 static void gtk_menu_shell_add               (GtkContainer      *container,
129                                               GtkWidget         *widget);
130 static void gtk_menu_shell_remove            (GtkContainer      *container,
131                                               GtkWidget         *widget);
132 static void gtk_menu_shell_forall            (GtkContainer      *container,
133                                               gboolean           include_internals,
134                                               GtkCallback        callback,
135                                               gpointer           callback_data);
136 static void gtk_menu_shell_real_insert       (GtkMenuShell *menu_shell,
137                                               GtkWidget    *child,
138                                               gint          position);
139 static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
140 static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
141                                               GtkWidget         *child);
142 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
143                                               GdkEvent          *event);
144 static GType    gtk_menu_shell_child_type  (GtkContainer      *container);
145 static void gtk_menu_shell_real_select_item  (GtkMenuShell      *menu_shell,
146                                               GtkWidget         *menu_item);
147 static void gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell); 
148
149 static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
150                                               GtkMenuDirectionType direction);
151 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
152                                                   gboolean           force_hide);
153 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
154 static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
155                                                   GtkDirectionType   dir);
156
157 static GtkContainerClass *parent_class = NULL;
158 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
159
160
161 GType
162 gtk_menu_shell_get_type (void)
163 {
164   static GType menu_shell_type = 0;
165
166   if (!menu_shell_type)
167     {
168       static const GTypeInfo menu_shell_info =
169       {
170         sizeof (GtkMenuShellClass),
171         NULL,           /* base_init */
172         NULL,           /* base_finalize */
173         (GClassInitFunc) gtk_menu_shell_class_init,
174         NULL,           /* class_finalize */
175         NULL,           /* class_data */
176         sizeof (GtkMenuShell),
177         0,              /* n_preallocs */
178         (GInstanceInitFunc) gtk_menu_shell_init,
179         NULL,           /* value_table */
180       };
181
182       menu_shell_type =
183         g_type_register_static (GTK_TYPE_CONTAINER, "GtkMenuShell",
184                                 &menu_shell_info, G_TYPE_FLAG_ABSTRACT);
185     }
186
187   return menu_shell_type;
188 }
189
190 static void
191 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
192 {
193   GObjectClass *object_class;
194   GtkWidgetClass *widget_class;
195   GtkContainerClass *container_class;
196
197   GtkBindingSet *binding_set;
198
199   object_class = (GObjectClass*) klass;
200   widget_class = (GtkWidgetClass*) klass;
201   container_class = (GtkContainerClass*) klass;
202
203   parent_class = g_type_class_peek_parent (klass);
204
205   widget_class->realize = gtk_menu_shell_realize;
206   widget_class->button_press_event = gtk_menu_shell_button_press;
207   widget_class->button_release_event = gtk_menu_shell_button_release;
208   widget_class->key_press_event = gtk_menu_shell_key_press;
209   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
210   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
211
212   container_class->add = gtk_menu_shell_add;
213   container_class->remove = gtk_menu_shell_remove;
214   container_class->forall = gtk_menu_shell_forall;
215   container_class->child_type = gtk_menu_shell_child_type;
216
217   klass->submenu_placement = GTK_TOP_BOTTOM;
218   klass->deactivate = gtk_real_menu_shell_deactivate;
219   klass->selection_done = NULL;
220   klass->move_current = gtk_real_menu_shell_move_current;
221   klass->activate_current = gtk_real_menu_shell_activate_current;
222   klass->cancel = gtk_real_menu_shell_cancel;
223   klass->select_item = gtk_menu_shell_real_select_item;
224   klass->insert = gtk_menu_shell_real_insert;
225
226   menu_shell_signals[DEACTIVATE] =
227     g_signal_new ("deactivate",
228                   G_OBJECT_CLASS_TYPE (object_class),
229                   G_SIGNAL_RUN_FIRST,
230                   G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
231                   NULL, NULL,
232                   _gtk_marshal_VOID__VOID,
233                   G_TYPE_NONE, 0);
234   menu_shell_signals[SELECTION_DONE] =
235     g_signal_new ("selection-done",
236                   G_OBJECT_CLASS_TYPE (object_class),
237                   G_SIGNAL_RUN_FIRST,
238                   G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
239                   NULL, NULL,
240                   _gtk_marshal_VOID__VOID,
241                   G_TYPE_NONE, 0);
242   menu_shell_signals[MOVE_CURRENT] =
243     g_signal_new ("move_current",
244                   G_OBJECT_CLASS_TYPE (object_class),
245                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
246                   G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
247                   NULL, NULL,
248                   _gtk_marshal_VOID__ENUM,
249                   G_TYPE_NONE, 1, 
250                   GTK_TYPE_MENU_DIRECTION_TYPE);
251   menu_shell_signals[ACTIVATE_CURRENT] =
252     g_signal_new ("activate_current",
253                   G_OBJECT_CLASS_TYPE (object_class),
254                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
255                   G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
256                   NULL, NULL,
257                   _gtk_marshal_VOID__BOOLEAN,
258                   G_TYPE_NONE, 1, 
259                   G_TYPE_BOOLEAN);
260   menu_shell_signals[CANCEL] =
261     g_signal_new ("cancel",
262                   G_OBJECT_CLASS_TYPE (object_class),
263                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
264                   G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
265                   NULL, NULL,
266                   _gtk_marshal_VOID__VOID,
267                   G_TYPE_NONE, 0);
268   menu_shell_signals[CYCLE_FOCUS] =
269     _gtk_binding_signal_new ("cycle_focus",
270                              G_OBJECT_CLASS_TYPE (object_class),
271                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
272                              G_CALLBACK (gtk_real_menu_shell_cycle_focus),
273                              NULL, NULL,
274                              _gtk_marshal_VOID__ENUM,
275                              G_TYPE_NONE, 1,
276                              GTK_TYPE_DIRECTION_TYPE);
277
278
279   binding_set = gtk_binding_set_by_class (klass);
280   gtk_binding_entry_add_signal (binding_set,
281                                 GDK_Escape, 0,
282                                 "cancel", 0);
283   gtk_binding_entry_add_signal (binding_set,
284                                 GDK_Return, 0,
285                                 "activate_current", 1,
286                                 G_TYPE_BOOLEAN,
287                                 TRUE);
288   gtk_binding_entry_add_signal (binding_set,
289                                 GDK_KP_Enter, 0,
290                                 "activate_current", 1,
291                                 G_TYPE_BOOLEAN,
292                                 TRUE);
293   gtk_binding_entry_add_signal (binding_set,
294                                 GDK_space, 0,
295                                 "activate_current", 1,
296                                 G_TYPE_BOOLEAN,
297                                 FALSE);
298   gtk_binding_entry_add_signal (binding_set,
299                                 GDK_KP_Space, 0,
300                                 "activate_current", 1,
301                                 G_TYPE_BOOLEAN,
302                                 FALSE);
303   gtk_binding_entry_add_signal (binding_set,
304                                 GDK_F10, 0,
305                                 "cycle_focus", 1,
306                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
307   gtk_binding_entry_add_signal (binding_set,
308                                 GDK_F10, GDK_SHIFT_MASK,
309                                 "cycle_focus", 1,
310                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
311 }
312
313 static GType
314 gtk_menu_shell_child_type (GtkContainer     *container)
315 {
316   return GTK_TYPE_MENU_ITEM;
317 }
318
319 static void
320 gtk_menu_shell_init (GtkMenuShell *menu_shell)
321 {
322   menu_shell->children = NULL;
323   menu_shell->active_menu_item = NULL;
324   menu_shell->parent_menu_shell = NULL;
325   menu_shell->active = FALSE;
326   menu_shell->have_grab = FALSE;
327   menu_shell->have_xgrab = FALSE;
328   menu_shell->button = 0;
329   menu_shell->activate_time = 0;
330 }
331
332 void
333 gtk_menu_shell_append (GtkMenuShell *menu_shell,
334                        GtkWidget    *child)
335 {
336   gtk_menu_shell_insert (menu_shell, child, -1);
337 }
338
339 void
340 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
341                         GtkWidget    *child)
342 {
343   gtk_menu_shell_insert (menu_shell, child, 0);
344 }
345
346 void
347 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
348                        GtkWidget    *child,
349                        gint          position)
350 {
351   GtkMenuShellClass *class;
352
353   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
354   g_return_if_fail (GTK_IS_MENU_ITEM (child));
355
356   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
357
358   if (class->insert)
359     class->insert (menu_shell, child, position);
360 }
361
362 static void
363 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
364                             GtkWidget    *child,
365                             gint          position)
366 {
367   menu_shell->children = g_list_insert (menu_shell->children, child, position);
368
369   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
370 }
371
372 void
373 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
374 {
375   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
376
377   g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
378 }
379
380 static void
381 gtk_menu_shell_realize (GtkWidget *widget)
382 {
383   GdkWindowAttr attributes;
384   gint attributes_mask;
385
386   g_return_if_fail (GTK_IS_MENU_SHELL (widget));
387
388   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
389
390   attributes.x = widget->allocation.x;
391   attributes.y = widget->allocation.y;
392   attributes.width = widget->allocation.width;
393   attributes.height = widget->allocation.height;
394   attributes.window_type = GDK_WINDOW_CHILD;
395   attributes.wclass = GDK_INPUT_OUTPUT;
396   attributes.visual = gtk_widget_get_visual (widget);
397   attributes.colormap = gtk_widget_get_colormap (widget);
398   attributes.event_mask = gtk_widget_get_events (widget);
399   attributes.event_mask |= (GDK_EXPOSURE_MASK |
400                             GDK_BUTTON_PRESS_MASK |
401                             GDK_BUTTON_RELEASE_MASK |
402                             GDK_KEY_PRESS_MASK |
403                             GDK_ENTER_NOTIFY_MASK |
404                             GDK_LEAVE_NOTIFY_MASK);
405
406   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
407   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
408   gdk_window_set_user_data (widget->window, widget);
409
410   widget->style = gtk_style_attach (widget->style, widget->window);
411   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
412 }
413
414 void
415 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
416 {
417   if (!menu_shell->active)
418     {
419       gtk_grab_add (GTK_WIDGET (menu_shell));
420       menu_shell->have_grab = TRUE;
421       menu_shell->active = TRUE;
422     }
423 }
424
425 static gint
426 gtk_menu_shell_button_press (GtkWidget      *widget,
427                              GdkEventButton *event)
428 {
429   GtkMenuShell *menu_shell;
430   GtkWidget *menu_item;
431
432   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
433   g_return_val_if_fail (event != NULL, FALSE);
434
435   if (event->type != GDK_BUTTON_PRESS)
436     return FALSE;
437
438   menu_shell = GTK_MENU_SHELL (widget);
439
440   if (menu_shell->parent_menu_shell)
441     {
442       return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
443     }
444   else if (!menu_shell->active || !menu_shell->button)
445     {
446       _gtk_menu_shell_activate (menu_shell);
447       
448       menu_shell->button = event->button;
449
450       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
451
452       if (menu_item && _gtk_menu_item_is_selectable (menu_item))
453         {
454           if ((menu_item->parent == widget) &&
455               (menu_item != menu_shell->active_menu_item))
456             {
457               if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
458                 {
459                   menu_shell->activate_time = event->time;
460                 }
461       
462               gtk_menu_shell_select_item (menu_shell, menu_item);
463             }
464         }
465     }
466   else
467     {
468       widget = gtk_get_event_widget ((GdkEvent*) event);
469       if (widget == GTK_WIDGET (menu_shell))
470         {
471           gtk_menu_shell_deactivate (menu_shell);
472           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
473         }
474     }
475
476   return TRUE;
477 }
478
479 static gint
480 gtk_menu_shell_button_release (GtkWidget      *widget,
481                                GdkEventButton *event)
482 {
483   GtkMenuShell *menu_shell;
484   GtkWidget *menu_item;
485   gint deactivate;
486
487   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
488   g_return_val_if_fail (event != NULL, FALSE);
489
490   menu_shell = GTK_MENU_SHELL (widget);
491   if (menu_shell->active)
492     {
493       if (menu_shell->button && (event->button != menu_shell->button))
494         {
495           menu_shell->button = 0;
496           if (menu_shell->parent_menu_shell)
497             return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
498         }
499
500       menu_shell->button = 0;
501       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
502
503       deactivate = TRUE;
504
505       if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
506         {
507           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
508               _gtk_menu_item_is_selectable (menu_item))
509             {
510               if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
511                 gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
512               else
513                 gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
514               return TRUE;
515             }
516           else if (menu_item &&
517                    !_gtk_menu_item_is_selectable (menu_item) &&
518                    GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
519             {
520               deactivate = FALSE;
521             }
522           else if (menu_shell->parent_menu_shell)
523             {
524               menu_shell->active = TRUE;
525               gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
526               return TRUE;
527             }
528
529           /* If we ended up on an item with a submenu, leave the menu up.
530            */
531           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
532               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
533             {
534               deactivate = FALSE;
535             }
536         }
537       else /* a very fast press-release */
538         {
539           /* We only ever want to prevent deactivation on the first
540            * press/release. Setting the time to zero is a bit of a
541            * hack, since we could be being triggered in the first
542            * few fractions of a second after a server time wraparound.
543            * the chances of that happening are ~1/10^6, without
544            * serious harm if we lose.
545            */
546           menu_shell->activate_time = 0;
547           deactivate = FALSE;
548         }
549       
550       if (deactivate)
551         {
552           gtk_menu_shell_deactivate (menu_shell);
553           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
554         }
555     }
556
557   return TRUE;
558 }
559
560 static gint
561 gtk_menu_shell_key_press (GtkWidget     *widget,
562                           GdkEventKey *event)
563 {
564   GtkMenuShell *menu_shell;
565   GtkWidget *toplevel;
566   
567   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
568   g_return_val_if_fail (event != NULL, FALSE);
569       
570   menu_shell = GTK_MENU_SHELL (widget);
571
572   if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
573     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
574   
575   if (gtk_bindings_activate_event (GTK_OBJECT (widget), event))
576     return TRUE;
577
578   toplevel = gtk_widget_get_toplevel (widget);
579   if (GTK_IS_WINDOW (toplevel) &&
580       gtk_window_activate_key (GTK_WINDOW (toplevel), event))
581     return TRUE;
582
583   return FALSE;
584 }
585
586 static gint
587 gtk_menu_shell_enter_notify (GtkWidget        *widget,
588                              GdkEventCrossing *event)
589 {
590   GtkMenuShell *menu_shell;
591   GtkWidget *menu_item;
592
593   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
594   g_return_val_if_fail (event != NULL, FALSE);
595
596   menu_shell = GTK_MENU_SHELL (widget);
597
598   if (menu_shell->active)
599     {
600       menu_item = gtk_get_event_widget ((GdkEvent*) event);
601
602       if (!menu_item ||
603           (GTK_IS_MENU_ITEM (menu_item) && 
604            !_gtk_menu_item_is_selectable (menu_item)))
605         return TRUE;
606       
607       if ((menu_item->parent == widget) &&
608           (menu_shell->active_menu_item != menu_item) &&
609           GTK_IS_MENU_ITEM (menu_item))
610         {
611           if (menu_shell->ignore_enter)
612             return TRUE;
613           
614           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
615               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
616             {
617               gtk_menu_shell_select_item (menu_shell, menu_item);
618             }
619         }
620       else if (menu_shell->parent_menu_shell)
621         {
622           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
623         }
624     }
625
626   return TRUE;
627 }
628
629 static gint
630 gtk_menu_shell_leave_notify (GtkWidget        *widget,
631                              GdkEventCrossing *event)
632 {
633   GtkMenuShell *menu_shell;
634   GtkMenuItem *menu_item;
635   GtkWidget *event_widget;
636
637   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
638   g_return_val_if_fail (event != NULL, FALSE);
639
640   if (GTK_WIDGET_VISIBLE (widget))
641     {
642       menu_shell = GTK_MENU_SHELL (widget);
643       event_widget = gtk_get_event_widget ((GdkEvent*) event);
644
645       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
646         return TRUE;
647
648       menu_item = GTK_MENU_ITEM (event_widget);
649
650       if (!_gtk_menu_item_is_selectable (event_widget))
651         return TRUE;
652
653       if ((menu_shell->active_menu_item == event_widget) &&
654           (menu_item->submenu == NULL))
655         {
656           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
657               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
658             {
659               gtk_menu_shell_deselect (menu_shell);
660             }
661         }
662       else if (menu_shell->parent_menu_shell)
663         {
664           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
665         }
666     }
667
668   return TRUE;
669 }
670
671 static void
672 gtk_menu_shell_add (GtkContainer *container,
673                     GtkWidget    *widget)
674 {
675   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
676 }
677
678 static void
679 gtk_menu_shell_remove (GtkContainer *container,
680                        GtkWidget    *widget)
681 {
682   GtkMenuShell *menu_shell;
683   gint was_visible;
684   
685   g_return_if_fail (GTK_IS_MENU_SHELL (container));
686   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
687   
688   was_visible = GTK_WIDGET_VISIBLE (widget);
689   menu_shell = GTK_MENU_SHELL (container);
690   menu_shell->children = g_list_remove (menu_shell->children, widget);
691   
692   if (widget == menu_shell->active_menu_item)
693     {
694       gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
695       menu_shell->active_menu_item = NULL;
696     }
697
698   gtk_widget_unparent (widget);
699   
700   /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
701    * since that's what is needed by toplevels.
702    */
703   if (was_visible)
704     gtk_widget_queue_resize (GTK_WIDGET (container));
705 }
706
707 static void
708 gtk_menu_shell_forall (GtkContainer *container,
709                        gboolean      include_internals,
710                        GtkCallback   callback,
711                        gpointer      callback_data)
712 {
713   GtkMenuShell *menu_shell;
714   GtkWidget *child;
715   GList *children;
716
717   g_return_if_fail (GTK_IS_MENU_SHELL (container));
718   g_return_if_fail (callback != NULL);
719
720   menu_shell = GTK_MENU_SHELL (container);
721
722   children = menu_shell->children;
723   while (children)
724     {
725       child = children->data;
726       children = children->next;
727
728       (* callback) (child, callback_data);
729     }
730 }
731
732
733 static void
734 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
735 {
736   if (menu_shell->active)
737     {
738       menu_shell->button = 0;
739       menu_shell->active = FALSE;
740       menu_shell->activate_time = 0;
741
742       if (menu_shell->active_menu_item)
743         {
744           gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
745           menu_shell->active_menu_item = NULL;
746         }
747
748       if (menu_shell->have_grab)
749         {
750           menu_shell->have_grab = FALSE;
751           gtk_grab_remove (GTK_WIDGET (menu_shell));
752         }
753       if (menu_shell->have_xgrab)
754         {
755           GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
756           
757           menu_shell->have_xgrab = FALSE;
758           gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
759           gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
760         }
761     }
762 }
763
764 static gint
765 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
766                         GtkWidget    *child)
767 {
768   GtkWidget *parent;
769
770   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
771   g_return_val_if_fail (child != NULL, FALSE);
772
773   parent = child->parent;
774   while (parent && GTK_IS_MENU_SHELL (parent))
775     {
776       if (parent == (GtkWidget*) menu_shell)
777         return TRUE;
778       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
779     }
780
781   return FALSE;
782 }
783
784 static GtkWidget*
785 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
786                          GdkEvent     *event)
787 {
788   GtkWidget *menu_item;
789
790   menu_item = gtk_get_event_widget ((GdkEvent*) event);
791   
792   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
793     menu_item = menu_item->parent;
794
795   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
796     return menu_item;
797   else
798     return NULL;
799 }
800
801 /* Handlers for action signals */
802
803 void
804 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
805                             GtkWidget    *menu_item)
806 {
807   GtkMenuShellClass *class;
808
809   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
810   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
811
812   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
813
814   if (class->select_item &&
815       !(menu_shell->active &&
816         menu_shell->active_menu_item == menu_item))
817     class->select_item (menu_shell, menu_item);
818 }
819
820 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
821                                    GtkSubmenuPlacement  placement);
822
823 static void
824 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
825                                  GtkWidget    *menu_item)
826 {
827   gtk_menu_shell_deselect (menu_shell);
828
829   if (!_gtk_menu_item_is_selectable (menu_item))
830     return;
831
832   menu_shell->active_menu_item = menu_item;
833   _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
834                                GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
835   gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
836
837   /* This allows the bizarre radio buttons-with-submenus-display-history
838    * behavior
839    */
840   if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
841     gtk_widget_activate (menu_shell->active_menu_item);
842 }
843
844 void
845 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
846 {
847   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
848
849   if (menu_shell->active_menu_item)
850     {
851       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
852       menu_shell->active_menu_item = NULL;
853     }
854 }
855
856 void
857 gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
858                               GtkWidget         *menu_item,
859                               gboolean           force_deactivate)
860 {
861   GSList *slist, *shells = NULL;
862   gboolean deactivate = force_deactivate;
863
864   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
865   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
866
867   if (!deactivate)
868     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
869
870   g_object_ref (menu_shell);
871
872   if (deactivate)
873     {
874       GtkMenuShell *parent_menu_shell = menu_shell;
875
876       do
877         {
878           g_object_ref (parent_menu_shell);
879           shells = g_slist_prepend (shells, parent_menu_shell);
880           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
881         }
882       while (parent_menu_shell);
883       shells = g_slist_reverse (shells);
884
885       gtk_menu_shell_deactivate (menu_shell);
886   
887       /* flush the x-queue, so any grabs are removed and
888        * the menu is actually taken down
889        */
890       gdk_display_sync (gtk_widget_get_display (menu_item));
891     }
892
893   gtk_widget_activate (menu_item);
894
895   for (slist = shells; slist; slist = slist->next)
896     {
897       g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
898       g_object_unref (slist->data);
899     }
900   g_slist_free (shells);
901
902   g_object_unref (menu_shell);
903 }
904
905 /* Distance should be +/- 1 */
906 static void
907 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
908                               gint           distance)
909 {
910   if (menu_shell->active_menu_item)
911     {
912       GList *node = g_list_find (menu_shell->children,
913                                  menu_shell->active_menu_item);
914       GList *start_node = node;
915       
916       if (distance > 0)
917         {
918           node = node->next;
919           while (node != start_node && 
920                  (!node || !_gtk_menu_item_is_selectable (node->data)))
921             {
922               if (!node)
923                 node = menu_shell->children;
924               else
925                 node = node->next;
926             }
927         }
928       else
929         {
930           node = node->prev;
931           while (node != start_node &&
932                  (!node || !_gtk_menu_item_is_selectable (node->data)))
933             {
934               if (!node)
935                 node = g_list_last (menu_shell->children);
936               else
937                 node = node->prev;
938             }
939         }
940       
941       if (node)
942         gtk_menu_shell_select_item (menu_shell, node->data);
943     }
944 }
945
946 /**
947  * gtk_menu_shell_select_first:
948  * @menu_shell: a #GtkMenuShell
949  * @search_sensitive: if %TRUE, search for the first selectable
950  *                    menu item, otherwise select nothing if
951  *                    the first item isn't sensitive. This
952  *                    should be %FALSE if the menu is being
953  *                    popped up initially.
954  * 
955  * Select the first visible or selectable child of the menu shell;
956  * don't select tearoff items unless the only item is a tearoff
957  * item.
958  *
959  * Since: 2.2
960  **/
961 void
962 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
963                              gboolean      search_sensitive)
964 {
965   GtkWidget *to_select = NULL;
966   GList *tmp_list;
967
968   tmp_list = menu_shell->children;
969   while (tmp_list)
970     {
971       GtkWidget *child = tmp_list->data;
972       
973       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
974           _gtk_menu_item_is_selectable (child))
975         {
976           to_select = child;
977           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
978             break;
979         }
980       
981       tmp_list = tmp_list->next;
982     }
983
984   if (to_select)
985     gtk_menu_shell_select_item (menu_shell, to_select);
986 }
987
988 void
989 _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
990                              gboolean      search_sensitive)
991 {
992   GtkWidget *to_select = NULL;
993   GList *tmp_list;
994
995   tmp_list = g_list_last (menu_shell->children);
996   while (tmp_list)
997     {
998       GtkWidget *child = tmp_list->data;
999       
1000       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1001           _gtk_menu_item_is_selectable (child))
1002         {
1003           to_select = child;
1004           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1005             break;
1006         }
1007       
1008       tmp_list = tmp_list->prev;
1009     }
1010
1011   if (to_select)
1012     gtk_menu_shell_select_item (menu_shell, to_select);
1013 }
1014
1015 static void
1016 gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
1017 {
1018   GtkMenuItem *menu_item;
1019
1020   menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item); 
1021   
1022   if (menu_item->submenu)
1023     gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1024 }
1025
1026 static void
1027 gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
1028                                   GtkMenuDirectionType direction)
1029 {
1030   GtkMenuShell *parent_menu_shell = NULL;
1031   gboolean had_selection;
1032
1033   had_selection = menu_shell->active_menu_item != NULL;
1034
1035   if (menu_shell->parent_menu_shell)
1036     parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1037   
1038   if (gtk_widget_get_direction (GTK_WIDGET (menu_shell)) == GTK_TEXT_DIR_RTL)
1039     {
1040       switch (direction) 
1041         {
1042         case GTK_MENU_DIR_PARENT:
1043           direction = GTK_MENU_DIR_CHILD;
1044           break;
1045         case GTK_MENU_DIR_CHILD:
1046           direction = GTK_MENU_DIR_PARENT;
1047           break;
1048         case GTK_MENU_DIR_PREV:
1049           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
1050             direction = GTK_MENU_DIR_NEXT;
1051           break;
1052         case GTK_MENU_DIR_NEXT:
1053           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
1054             direction = GTK_MENU_DIR_PREV;
1055           break;
1056         default: ;
1057         }
1058     }
1059   
1060   switch (direction)
1061     {
1062     case GTK_MENU_DIR_PARENT:
1063       if (parent_menu_shell)
1064         {
1065           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement == 
1066                        GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1067             gtk_menu_shell_deselect (menu_shell);
1068           else 
1069             {
1070               gtk_menu_shell_move_selected (parent_menu_shell, -1);
1071               gtk_menu_shell_select_submenu_first (parent_menu_shell); 
1072             }
1073         }
1074       /* If there is no parent and the submenu is in the opposite direction
1075        * to the menu, then make the PARENT direction wrap around to
1076        * the bottom of the submenu.
1077        */
1078       else if (menu_shell->active_menu_item &&
1079                _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1080                GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1081         {
1082           GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
1083
1084           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement !=
1085               GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement)
1086             _gtk_menu_shell_select_last (submenu, TRUE);
1087         }
1088       break;
1089       
1090     case GTK_MENU_DIR_CHILD:
1091       if (menu_shell->active_menu_item &&
1092           _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1093           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1094         {
1095           gtk_menu_shell_select_submenu_first (menu_shell);
1096         }
1097       else
1098         {
1099           /* Try to find a menu running the opposite direction */
1100           while (parent_menu_shell && 
1101                  (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1102                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1103             {
1104               GtkWidget *tmp_widget = parent_menu_shell->parent_menu_shell;
1105
1106               if (tmp_widget)
1107                 parent_menu_shell = GTK_MENU_SHELL (tmp_widget);
1108               else
1109                 parent_menu_shell = NULL;
1110             }
1111
1112           if (parent_menu_shell)
1113             {
1114               gtk_menu_shell_move_selected (parent_menu_shell, 1);
1115               gtk_menu_shell_select_submenu_first (parent_menu_shell);
1116             }
1117         }
1118       break;
1119       
1120     case GTK_MENU_DIR_PREV:
1121       gtk_menu_shell_move_selected (menu_shell, -1);
1122       if (!had_selection &&
1123           !menu_shell->active_menu_item &&
1124           menu_shell->children)
1125         _gtk_menu_shell_select_last (menu_shell, TRUE);
1126       break;
1127     case GTK_MENU_DIR_NEXT:
1128       gtk_menu_shell_move_selected (menu_shell, 1);
1129       if (!had_selection &&
1130           !menu_shell->active_menu_item &&
1131           menu_shell->children)
1132         gtk_menu_shell_select_first (menu_shell, TRUE);
1133       break;
1134     }
1135   
1136 }
1137
1138 static void
1139 gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1140                                       gboolean           force_hide)
1141 {
1142   if (menu_shell->active_menu_item &&
1143       _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1144       GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1145     {
1146       gtk_menu_shell_activate_item (menu_shell,
1147                                     menu_shell->active_menu_item,
1148                                     force_hide);
1149     }
1150 }
1151
1152 static void
1153 gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1154 {
1155   /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1156    */
1157   gtk_menu_shell_deselect (menu_shell);
1158   
1159   gtk_menu_shell_deactivate (menu_shell);
1160   g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1161 }
1162
1163 static void
1164 gtk_real_menu_shell_cycle_focus (GtkMenuShell      *menu_shell,
1165                                  GtkDirectionType   dir)
1166 {
1167   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1168     {
1169       if (menu_shell->parent_menu_shell)
1170         menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1171       else
1172         menu_shell = NULL;
1173     }
1174
1175   if (menu_shell)
1176     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1177 }
1178
1179 gint
1180 _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell)
1181 {
1182   GtkMenuShellClass *klass = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1183   
1184   if (klass->get_popup_delay)
1185     {
1186       return klass->get_popup_delay (menu_shell);
1187     }
1188   else
1189     {
1190       gint popup_delay;
1191       GtkWidget *widget = GTK_WIDGET (menu_shell);
1192       
1193       g_object_get (gtk_widget_get_settings (widget),
1194                     "gtk-menu-popup-delay", &popup_delay,
1195                     NULL);
1196       
1197       return popup_delay;
1198     }
1199 }
1200
1201 /**
1202  * gtk_menu_shell_cancel:
1203  * @menu_shell: a #GtkMenuShell
1204  * 
1205  * Cancels the selection within the menu shell.  
1206  * 
1207  * Since: 2.4
1208  */
1209 void
1210 gtk_menu_shell_cancel (GtkMenuShell *menu_shell)
1211 {
1212   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1213
1214   g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0);
1215 }