]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
Deprecation cleanup
[~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 guint
189 binding_signal_new (const gchar        *signal_name,
190                     GType               itype,
191                     GSignalFlags        signal_flags,
192                     GCallback           handler,
193                     GSignalAccumulator  accumulator,
194                     gpointer            accu_data,
195                     GSignalCMarshaller  c_marshaller,
196                     GType               return_type,
197                     guint               n_params,
198                     ...)
199 {
200   va_list args;
201   guint signal_id;
202
203   g_return_val_if_fail (signal_name != NULL, 0);
204   
205   va_start (args, n_params);
206
207   signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
208                                    g_cclosure_new (handler, NULL, NULL),
209                                    accumulator, accu_data, c_marshaller,
210                                    return_type, n_params, args);
211
212   va_end (args);
213  
214   return signal_id;
215 }
216
217 static void
218 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
219 {
220   GObjectClass *object_class;
221   GtkWidgetClass *widget_class;
222   GtkContainerClass *container_class;
223
224   GtkBindingSet *binding_set;
225
226   object_class = (GObjectClass*) klass;
227   widget_class = (GtkWidgetClass*) klass;
228   container_class = (GtkContainerClass*) klass;
229
230   parent_class = g_type_class_peek_parent (klass);
231
232   widget_class->realize = gtk_menu_shell_realize;
233   widget_class->button_press_event = gtk_menu_shell_button_press;
234   widget_class->button_release_event = gtk_menu_shell_button_release;
235   widget_class->key_press_event = gtk_menu_shell_key_press;
236   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
237   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
238
239   container_class->add = gtk_menu_shell_add;
240   container_class->remove = gtk_menu_shell_remove;
241   container_class->forall = gtk_menu_shell_forall;
242   container_class->child_type = gtk_menu_shell_child_type;
243
244   klass->submenu_placement = GTK_TOP_BOTTOM;
245   klass->deactivate = gtk_real_menu_shell_deactivate;
246   klass->selection_done = NULL;
247   klass->move_current = gtk_real_menu_shell_move_current;
248   klass->activate_current = gtk_real_menu_shell_activate_current;
249   klass->cancel = gtk_real_menu_shell_cancel;
250   klass->select_item = gtk_menu_shell_real_select_item;
251   klass->insert = gtk_menu_shell_real_insert;
252
253   menu_shell_signals[DEACTIVATE] =
254     g_signal_new ("deactivate",
255                   G_OBJECT_CLASS_TYPE (object_class),
256                   G_SIGNAL_RUN_FIRST,
257                   G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
258                   NULL, NULL,
259                   _gtk_marshal_VOID__VOID,
260                   G_TYPE_NONE, 0);
261   menu_shell_signals[SELECTION_DONE] =
262     g_signal_new ("selection-done",
263                   G_OBJECT_CLASS_TYPE (object_class),
264                   G_SIGNAL_RUN_FIRST,
265                   G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
266                   NULL, NULL,
267                   _gtk_marshal_VOID__VOID,
268                   G_TYPE_NONE, 0);
269   menu_shell_signals[MOVE_CURRENT] =
270     g_signal_new ("move_current",
271                   G_OBJECT_CLASS_TYPE (object_class),
272                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
273                   G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
274                   NULL, NULL,
275                   _gtk_marshal_VOID__ENUM,
276                   G_TYPE_NONE, 1, 
277                   GTK_TYPE_MENU_DIRECTION_TYPE);
278   menu_shell_signals[ACTIVATE_CURRENT] =
279     g_signal_new ("activate_current",
280                   G_OBJECT_CLASS_TYPE (object_class),
281                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
282                   G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
283                   NULL, NULL,
284                   _gtk_marshal_VOID__BOOLEAN,
285                   G_TYPE_NONE, 1, 
286                   G_TYPE_BOOLEAN);
287   menu_shell_signals[CANCEL] =
288     g_signal_new ("cancel",
289                   G_OBJECT_CLASS_TYPE (object_class),
290                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
291                   G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
292                   NULL, NULL,
293                   _gtk_marshal_VOID__VOID,
294                   G_TYPE_NONE, 0);
295   menu_shell_signals[CYCLE_FOCUS] =
296     binding_signal_new ("cycle_focus",
297                         G_OBJECT_CLASS_TYPE (object_class),
298                         G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
299                         G_CALLBACK (gtk_real_menu_shell_cycle_focus),
300                         NULL, NULL,
301                         _gtk_marshal_VOID__ENUM,
302                         G_TYPE_NONE, 1,
303                         GTK_TYPE_DIRECTION_TYPE);
304
305
306   binding_set = gtk_binding_set_by_class (klass);
307   gtk_binding_entry_add_signal (binding_set,
308                                 GDK_Escape, 0,
309                                 "cancel", 0);
310   gtk_binding_entry_add_signal (binding_set,
311                                 GDK_Return, 0,
312                                 "activate_current", 1,
313                                 G_TYPE_BOOLEAN,
314                                 TRUE);
315   gtk_binding_entry_add_signal (binding_set,
316                                 GDK_KP_Enter, 0,
317                                 "activate_current", 1,
318                                 G_TYPE_BOOLEAN,
319                                 TRUE);
320   gtk_binding_entry_add_signal (binding_set,
321                                 GDK_space, 0,
322                                 "activate_current", 1,
323                                 G_TYPE_BOOLEAN,
324                                 FALSE);
325   gtk_binding_entry_add_signal (binding_set,
326                                 GDK_KP_Space, 0,
327                                 "activate_current", 1,
328                                 G_TYPE_BOOLEAN,
329                                 FALSE);
330   gtk_binding_entry_add_signal (binding_set,
331                                 GDK_F10, 0,
332                                 "cycle_focus", 1,
333                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
334   gtk_binding_entry_add_signal (binding_set,
335                                 GDK_F10, GDK_SHIFT_MASK,
336                                 "cycle_focus", 1,
337                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
338 }
339
340 static GType
341 gtk_menu_shell_child_type (GtkContainer     *container)
342 {
343   return GTK_TYPE_MENU_ITEM;
344 }
345
346 static void
347 gtk_menu_shell_init (GtkMenuShell *menu_shell)
348 {
349   menu_shell->children = NULL;
350   menu_shell->active_menu_item = NULL;
351   menu_shell->parent_menu_shell = NULL;
352   menu_shell->active = FALSE;
353   menu_shell->have_grab = FALSE;
354   menu_shell->have_xgrab = FALSE;
355   menu_shell->ignore_leave = FALSE;
356   menu_shell->button = 0;
357   menu_shell->menu_flag = 0;
358   menu_shell->activate_time = 0;
359 }
360
361 void
362 gtk_menu_shell_append (GtkMenuShell *menu_shell,
363                        GtkWidget    *child)
364 {
365   gtk_menu_shell_insert (menu_shell, child, -1);
366 }
367
368 void
369 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
370                         GtkWidget    *child)
371 {
372   gtk_menu_shell_insert (menu_shell, child, 0);
373 }
374
375 void
376 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
377                        GtkWidget    *child,
378                        gint          position)
379 {
380   GtkMenuShellClass *class;
381
382   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
383   g_return_if_fail (GTK_IS_MENU_ITEM (child));
384
385   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
386
387   if (class->insert)
388     class->insert (menu_shell, child, position);
389 }
390
391 static void
392 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
393                             GtkWidget    *child,
394                             gint          position)
395 {
396   menu_shell->children = g_list_insert (menu_shell->children, child, position);
397
398   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
399 }
400
401 void
402 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
403 {
404   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
405
406   g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
407 }
408
409 static void
410 gtk_menu_shell_realize (GtkWidget *widget)
411 {
412   GdkWindowAttr attributes;
413   gint attributes_mask;
414
415   g_return_if_fail (GTK_IS_MENU_SHELL (widget));
416
417   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
418
419   attributes.x = widget->allocation.x;
420   attributes.y = widget->allocation.y;
421   attributes.width = widget->allocation.width;
422   attributes.height = widget->allocation.height;
423   attributes.window_type = GDK_WINDOW_CHILD;
424   attributes.wclass = GDK_INPUT_OUTPUT;
425   attributes.visual = gtk_widget_get_visual (widget);
426   attributes.colormap = gtk_widget_get_colormap (widget);
427   attributes.event_mask = gtk_widget_get_events (widget);
428   attributes.event_mask |= (GDK_EXPOSURE_MASK |
429                             GDK_BUTTON_PRESS_MASK |
430                             GDK_BUTTON_RELEASE_MASK |
431                             GDK_KEY_PRESS_MASK |
432                             GDK_ENTER_NOTIFY_MASK |
433                             GDK_LEAVE_NOTIFY_MASK);
434
435   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
436   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
437   gdk_window_set_user_data (widget->window, widget);
438
439   widget->style = gtk_style_attach (widget->style, widget->window);
440   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
441 }
442
443 void
444 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
445 {
446   if (!menu_shell->active)
447     {
448       gtk_grab_add (GTK_WIDGET (menu_shell));
449       menu_shell->have_grab = TRUE;
450       menu_shell->active = TRUE;
451     }
452 }
453
454 static gint
455 gtk_menu_shell_button_press (GtkWidget      *widget,
456                              GdkEventButton *event)
457 {
458   GtkMenuShell *menu_shell;
459   GtkWidget *menu_item;
460
461   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
462   g_return_val_if_fail (event != NULL, FALSE);
463
464   if (event->type != GDK_BUTTON_PRESS)
465     return FALSE;
466
467   menu_shell = GTK_MENU_SHELL (widget);
468
469   if (menu_shell->parent_menu_shell)
470     {
471       return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
472     }
473   else if (!menu_shell->active || !menu_shell->button)
474     {
475       _gtk_menu_shell_activate (menu_shell);
476       
477       menu_shell->button = event->button;
478
479       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
480
481       if (menu_item && _gtk_menu_item_is_selectable (menu_item))
482         {
483           if ((menu_item->parent == widget) &&
484               (menu_item != menu_shell->active_menu_item))
485             {
486               if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
487                 g_object_set_data (G_OBJECT (menu_shell),
488                                    "gtk-menushell-just-activated",
489                                    GUINT_TO_POINTER (1));
490               gtk_menu_shell_select_item (menu_shell, menu_item);
491             }
492         }
493     }
494   else
495     {
496       widget = gtk_get_event_widget ((GdkEvent*) event);
497       if (widget == GTK_WIDGET (menu_shell))
498         {
499           gtk_menu_shell_deactivate (menu_shell);
500           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
501         }
502     }
503
504   return TRUE;
505 }
506
507 static gint
508 gtk_menu_shell_button_release (GtkWidget      *widget,
509                                GdkEventButton *event)
510 {
511   GtkMenuShell *menu_shell;
512   GtkWidget *menu_item;
513   gint deactivate;
514
515   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
516   g_return_val_if_fail (event != NULL, FALSE);
517
518   menu_shell = GTK_MENU_SHELL (widget);
519   if (menu_shell->active)
520     {
521       gboolean deactivate_immediately = FALSE;
522
523       if (menu_shell->button && (event->button != menu_shell->button))
524         {
525           menu_shell->button = 0;
526           if (menu_shell->parent_menu_shell)
527             return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
528         }
529
530       menu_shell->button = 0;
531       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
532
533       deactivate = TRUE;
534
535       if (menu_item
536           && GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
537         {
538           if (g_object_get_data (G_OBJECT (menu_shell), "gtk-menushell-just-activated"))
539             g_object_set_data (G_OBJECT (menu_shell), "gtk-menushell-just-activated", NULL);
540           else
541             deactivate_immediately = TRUE;
542         }
543
544       if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
545         {
546           if (deactivate_immediately)
547             {
548               gtk_menu_shell_deactivate (menu_shell);
549               return TRUE;
550             }
551             
552           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
553               _gtk_menu_item_is_selectable (menu_item))
554             {
555               if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
556                 {
557                   gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
558                   return TRUE;
559                 }
560             }
561           else if (menu_item && !_gtk_menu_item_is_selectable (menu_item))
562             deactivate = FALSE;
563           else if (menu_shell->parent_menu_shell)
564             {
565               menu_shell->active = TRUE;
566               gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
567               return TRUE;
568             }
569         }
570       else
571         {
572           /* We only ever want to prevent deactivation on the first
573            * press/release. Setting the time to zero is a bit of a
574            * hack, since we could be being triggered in the first
575            * few fractions of a second after a server time wraparound.
576            * the chances of that happening are ~1/10^6, without
577            * serious harm if we lose.
578            */
579           menu_shell->activate_time = 0;
580           deactivate = FALSE;
581         }
582       
583       /* If the button click was very fast, or we ended up on a submenu,
584        * leave the menu up
585        */
586       if (!deactivate || 
587           (menu_item && (menu_shell->active_menu_item == menu_item)))
588         {
589           deactivate = FALSE;
590           menu_shell->ignore_leave = TRUE;
591         }
592       else
593         deactivate = TRUE;
594
595       if (deactivate)
596         {
597           gtk_menu_shell_deactivate (menu_shell);
598           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
599         }
600     }
601
602   return TRUE;
603 }
604
605 static gint
606 gtk_menu_shell_key_press (GtkWidget     *widget,
607                           GdkEventKey *event)
608 {
609   GtkMenuShell *menu_shell;
610   GtkWidget *toplevel;
611   
612   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
613   g_return_val_if_fail (event != NULL, FALSE);
614       
615   menu_shell = GTK_MENU_SHELL (widget);
616
617   if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
618     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
619   
620   if (_gtk_bindings_activate_event (GTK_OBJECT (widget), event))
621     return TRUE;
622
623   toplevel = gtk_widget_get_toplevel (widget);
624   if (GTK_IS_WINDOW (toplevel) &&
625       _gtk_window_activate_key (GTK_WINDOW (toplevel), event))
626     return TRUE;
627
628   return FALSE;
629 }
630
631 static gint
632 gtk_menu_shell_enter_notify (GtkWidget        *widget,
633                              GdkEventCrossing *event)
634 {
635   GtkMenuShell *menu_shell;
636   GtkWidget *menu_item;
637
638   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
639   g_return_val_if_fail (event != NULL, FALSE);
640
641   menu_shell = GTK_MENU_SHELL (widget);
642
643   if (menu_shell->active)
644     {
645       menu_item = gtk_get_event_widget ((GdkEvent*) event);
646
647       if (!menu_item ||
648           (GTK_IS_MENU_ITEM (menu_item) && 
649            !_gtk_menu_item_is_selectable (menu_item)))
650         return TRUE;
651       
652       if ((menu_item->parent == widget) &&
653           (menu_shell->active_menu_item != menu_item) &&
654           GTK_IS_MENU_ITEM (menu_item))
655         {
656           if (menu_shell->ignore_enter)
657             return TRUE;
658           
659           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
660               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
661             {
662               gtk_menu_shell_select_item (menu_shell, menu_item);
663             }
664         }
665       else if (menu_shell->parent_menu_shell)
666         {
667           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
668         }
669     }
670
671   return TRUE;
672 }
673
674 static gint
675 gtk_menu_shell_leave_notify (GtkWidget        *widget,
676                              GdkEventCrossing *event)
677 {
678   GtkMenuShell *menu_shell;
679   GtkMenuItem *menu_item;
680   GtkWidget *event_widget;
681
682   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
683   g_return_val_if_fail (event != NULL, FALSE);
684
685   if (GTK_WIDGET_VISIBLE (widget))
686     {
687       menu_shell = GTK_MENU_SHELL (widget);
688       event_widget = gtk_get_event_widget ((GdkEvent*) event);
689
690       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
691         return TRUE;
692
693       menu_item = GTK_MENU_ITEM (event_widget);
694
695       if (menu_shell->ignore_leave)
696         {
697           menu_shell->ignore_leave = FALSE;
698           return TRUE;
699         }
700
701       if (!_gtk_menu_item_is_selectable (event_widget))
702         return TRUE;
703
704       if ((menu_shell->active_menu_item == event_widget) &&
705           (menu_item->submenu == NULL))
706         {
707           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
708               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
709             {
710               gtk_menu_shell_deselect (menu_shell);
711             }
712         }
713       else if (menu_shell->parent_menu_shell)
714         {
715           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
716         }
717     }
718
719   return TRUE;
720 }
721
722 static void
723 gtk_menu_shell_add (GtkContainer *container,
724                     GtkWidget    *widget)
725 {
726   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
727 }
728
729 static void
730 gtk_menu_shell_remove (GtkContainer *container,
731                        GtkWidget    *widget)
732 {
733   GtkMenuShell *menu_shell;
734   gint was_visible;
735   
736   g_return_if_fail (GTK_IS_MENU_SHELL (container));
737   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
738   
739   was_visible = GTK_WIDGET_VISIBLE (widget);
740   menu_shell = GTK_MENU_SHELL (container);
741   menu_shell->children = g_list_remove (menu_shell->children, widget);
742   
743   if (widget == menu_shell->active_menu_item)
744     {
745       gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
746       menu_shell->active_menu_item = NULL;
747     }
748
749   gtk_widget_unparent (widget);
750   
751   /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
752    * since that's what is needed by toplevels.
753    */
754   if (was_visible)
755     gtk_widget_queue_resize (GTK_WIDGET (container));
756 }
757
758 static void
759 gtk_menu_shell_forall (GtkContainer *container,
760                        gboolean      include_internals,
761                        GtkCallback   callback,
762                        gpointer      callback_data)
763 {
764   GtkMenuShell *menu_shell;
765   GtkWidget *child;
766   GList *children;
767
768   g_return_if_fail (GTK_IS_MENU_SHELL (container));
769   g_return_if_fail (callback != NULL);
770
771   menu_shell = GTK_MENU_SHELL (container);
772
773   children = menu_shell->children;
774   while (children)
775     {
776       child = children->data;
777       children = children->next;
778
779       (* callback) (child, callback_data);
780     }
781 }
782
783
784 static void
785 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
786 {
787   if (menu_shell->active)
788     {
789       menu_shell->button = 0;
790       menu_shell->active = FALSE;
791
792       if (menu_shell->active_menu_item)
793         {
794           gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
795           menu_shell->active_menu_item = NULL;
796         }
797
798       if (menu_shell->have_grab)
799         {
800           menu_shell->have_grab = FALSE;
801           gtk_grab_remove (GTK_WIDGET (menu_shell));
802         }
803       if (menu_shell->have_xgrab)
804         {
805           GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
806           
807           menu_shell->have_xgrab = FALSE;
808           gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
809           gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
810         }
811     }
812 }
813
814 static gint
815 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
816                         GtkWidget    *child)
817 {
818   GtkWidget *parent;
819
820   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
821   g_return_val_if_fail (child != NULL, FALSE);
822
823   parent = child->parent;
824   while (parent && GTK_IS_MENU_SHELL (parent))
825     {
826       if (parent == (GtkWidget*) menu_shell)
827         return TRUE;
828       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
829     }
830
831   return FALSE;
832 }
833
834 static GtkWidget*
835 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
836                          GdkEvent     *event)
837 {
838   GtkWidget *menu_item;
839
840   menu_item = gtk_get_event_widget ((GdkEvent*) event);
841   
842   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
843     menu_item = menu_item->parent;
844
845   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
846     return menu_item;
847   else
848     return NULL;
849 }
850
851 /* Handlers for action signals */
852
853 void
854 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
855                             GtkWidget    *menu_item)
856 {
857   GtkMenuShellClass *class;
858
859   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
860   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
861
862   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
863
864   if (class->select_item)
865     class->select_item (menu_shell, menu_item);
866 }
867
868 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
869                                    GtkSubmenuPlacement  placement);
870
871 static void
872 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
873                                  GtkWidget    *menu_item)
874 {
875   gtk_menu_shell_deselect (menu_shell);
876
877   if (!_gtk_menu_item_is_selectable (menu_item))
878     return;
879
880   menu_shell->active_menu_item = menu_item;
881   _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
882                                GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
883   gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
884
885   /* This allows the bizarre radio buttons-with-submenus-display-history
886    * behavior
887    */
888   if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
889     gtk_widget_activate (menu_shell->active_menu_item);
890 }
891
892 void
893 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
894 {
895   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
896
897   if (menu_shell->active_menu_item)
898     {
899       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
900       menu_shell->active_menu_item = NULL;
901     }
902 }
903
904 void
905 gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
906                               GtkWidget         *menu_item,
907                               gboolean           force_deactivate)
908 {
909   GSList *slist, *shells = NULL;
910   gboolean deactivate = force_deactivate;
911
912   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
913   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
914
915   if (!deactivate)
916     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
917
918   g_object_ref (menu_shell);
919
920   if (deactivate)
921     {
922       GtkMenuShell *parent_menu_shell = menu_shell;
923
924       do
925         {
926           g_object_ref (parent_menu_shell);
927           shells = g_slist_prepend (shells, parent_menu_shell);
928           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
929         }
930       while (parent_menu_shell);
931       shells = g_slist_reverse (shells);
932
933       gtk_menu_shell_deactivate (menu_shell);
934   
935       /* flush the x-queue, so any grabs are removed and
936        * the menu is actually taken down
937        */
938       gdk_display_sync (gtk_widget_get_display (menu_item));
939     }
940
941   gtk_widget_activate (menu_item);
942
943   for (slist = shells; slist; slist = slist->next)
944     {
945       g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
946       g_object_unref (slist->data);
947     }
948   g_slist_free (shells);
949
950   g_object_unref (menu_shell);
951 }
952
953 /* Distance should be +/- 1 */
954 static void
955 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
956                               gint           distance)
957 {
958   if (menu_shell->active_menu_item)
959     {
960       GList *node = g_list_find (menu_shell->children,
961                                  menu_shell->active_menu_item);
962       GList *start_node = node;
963       
964       if (distance > 0)
965         {
966           node = node->next;
967           while (node != start_node && 
968                  (!node || !_gtk_menu_item_is_selectable (node->data)))
969             {
970               if (!node)
971                 node = menu_shell->children;
972               else
973                 node = node->next;
974             }
975         }
976       else
977         {
978           node = node->prev;
979           while (node != start_node &&
980                  (!node || !_gtk_menu_item_is_selectable (node->data)))
981             {
982               if (!node)
983                 node = g_list_last (menu_shell->children);
984               else
985                 node = node->prev;
986             }
987         }
988       
989       if (node)
990         gtk_menu_shell_select_item (menu_shell, node->data);
991     }
992 }
993
994 /**
995  * gtk_menu_shell_select_first:
996  * @menu_shell: a #GtkMenuShell
997  * @search_sensitive: if %TRUE, search for the first selectable
998  *                    menu item, otherwise select nothing if
999  *                    the first item isn't sensitive. This
1000  *                    should be %FALSE if the menu is being
1001  *                    popped up initially.
1002  * 
1003  * Select the first visible or selectable child of the menu shell;
1004  * don't select tearoff items unless the only item is a tearoff
1005  * item.
1006  **/
1007 void
1008 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1009                              gboolean      search_sensitive)
1010 {
1011   GtkWidget *to_select = NULL;
1012   GList *tmp_list;
1013
1014   tmp_list = menu_shell->children;
1015   while (tmp_list)
1016     {
1017       GtkWidget *child = tmp_list->data;
1018       
1019       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1020           _gtk_menu_item_is_selectable (child))
1021         {
1022           to_select = child;
1023           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1024             break;
1025         }
1026       
1027       tmp_list = tmp_list->next;
1028     }
1029
1030   if (to_select)
1031     gtk_menu_shell_select_item (menu_shell, to_select);
1032 }
1033
1034 static void
1035 gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1036                             gboolean      search_sensitive)
1037 {
1038   GtkWidget *to_select = NULL;
1039   GList *tmp_list;
1040
1041   tmp_list = g_list_last (menu_shell->children);
1042   while (tmp_list)
1043     {
1044       GtkWidget *child = tmp_list->data;
1045       
1046       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1047           _gtk_menu_item_is_selectable (child))
1048         {
1049           to_select = child;
1050           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1051             break;
1052         }
1053       
1054       tmp_list = tmp_list->prev;
1055     }
1056
1057   if (to_select)
1058     gtk_menu_shell_select_item (menu_shell, to_select);
1059 }
1060
1061 static void
1062 gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
1063 {
1064   GtkMenuItem *menu_item;
1065
1066   menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item); 
1067   
1068   if (menu_item->submenu)
1069     gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1070 }
1071
1072 static void
1073 gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
1074                                   GtkMenuDirectionType direction)
1075 {
1076   GtkMenuShell *parent_menu_shell = NULL;
1077   gboolean had_selection;
1078
1079   had_selection = menu_shell->active_menu_item != NULL;
1080
1081   if (menu_shell->parent_menu_shell)
1082     parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1083   
1084   switch (direction)
1085     {
1086     case GTK_MENU_DIR_PARENT:
1087       if (parent_menu_shell)
1088         {
1089           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement == 
1090                        GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1091             gtk_menu_shell_deselect (menu_shell);
1092           else 
1093             {
1094               gtk_menu_shell_move_selected (parent_menu_shell, -1);
1095               gtk_menu_shell_select_submenu_first (parent_menu_shell); 
1096             }
1097         }
1098       break;
1099       
1100     case GTK_MENU_DIR_CHILD:
1101       if (menu_shell->active_menu_item &&
1102           _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1103           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1104         {
1105           gtk_menu_shell_select_submenu_first (menu_shell);
1106         }
1107       else
1108         {
1109           /* Try to find a menu running the opposite direction */
1110           while (parent_menu_shell && 
1111                  (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1112                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1113             {
1114               GtkWidget *tmp_widget = parent_menu_shell->parent_menu_shell;
1115
1116               if (tmp_widget)
1117                 parent_menu_shell = GTK_MENU_SHELL (tmp_widget);
1118               else
1119                 parent_menu_shell = NULL;
1120             }
1121
1122           if (parent_menu_shell)
1123             {
1124               gtk_menu_shell_move_selected (parent_menu_shell, 1);
1125               gtk_menu_shell_select_submenu_first (parent_menu_shell);
1126             }
1127         }
1128       break;
1129       
1130     case GTK_MENU_DIR_PREV:
1131       gtk_menu_shell_move_selected (menu_shell, -1);
1132       if (!had_selection &&
1133           !menu_shell->active_menu_item &&
1134           menu_shell->children)
1135         gtk_menu_shell_select_last (menu_shell, TRUE);
1136       break;
1137     case GTK_MENU_DIR_NEXT:
1138       gtk_menu_shell_move_selected (menu_shell, 1);
1139       if (!had_selection &&
1140           !menu_shell->active_menu_item &&
1141           menu_shell->children)
1142         gtk_menu_shell_select_first (menu_shell, TRUE);
1143       break;
1144     }
1145   
1146 }
1147
1148 static void
1149 gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1150                                       gboolean           force_hide)
1151 {
1152   if (menu_shell->active_menu_item &&
1153       _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1154       GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1155     {
1156       gtk_menu_shell_activate_item (menu_shell,
1157                                     menu_shell->active_menu_item,
1158                                     force_hide);
1159     }
1160 }
1161
1162 static void
1163 gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1164 {
1165   /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1166    */
1167   gtk_menu_shell_deselect (menu_shell);
1168   
1169   gtk_menu_shell_deactivate (menu_shell);
1170   g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1171 }
1172
1173 static void
1174 gtk_real_menu_shell_cycle_focus (GtkMenuShell      *menu_shell,
1175                                  GtkDirectionType   dir)
1176 {
1177   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1178     {
1179       if (menu_shell->parent_menu_shell)
1180         menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1181       else
1182         menu_shell = NULL;
1183     }
1184
1185   if (menu_shell)
1186     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1187 }