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