]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
Practically everything changed.
[~andy/gtk] / gtk / gtkmenushell.c
1 /* GTK - The GTK+ 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 "gtkbindings.h"
32 #include "gtkkeyhash.h"
33 #include "gtkmain.h"
34 #include "gtkmarshalers.h"
35 #include "gtkmenu.h"
36 #include "gtkmenubar.h"
37 #include "gtkmenuitem.h"
38 #include "gtkmenushell.h"
39 #include "gtkmnemonichash.h"
40 #include "gtktearoffmenuitem.h"
41 #include "gtkwindow.h"
42 #include "gtkprivate.h"
43 #include "gtkintl.h"
44 #include "gtkalias.h"
45
46 #define MENU_SHELL_TIMEOUT   500
47
48 #define PACK_DIRECTION(m)                                 \
49    (GTK_IS_MENU_BAR (m)                                   \
50      ? gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (m)) \
51      : GTK_PACK_DIRECTION_LTR)
52
53 enum {
54   DEACTIVATE,
55   SELECTION_DONE,
56   MOVE_CURRENT,
57   ACTIVATE_CURRENT,
58   CANCEL,
59   CYCLE_FOCUS,
60   MOVE_SELECTED,
61   LAST_SIGNAL
62 };
63
64 enum {
65   PROP_0,
66   PROP_TAKE_FOCUS
67 };
68
69 /* Terminology:
70  * 
71  * A menu item can be "selected", this means that it is displayed
72  * in the prelight state, and if it has a submenu, that submenu
73  * will be popped up. 
74  * 
75  * A menu is "active" when it is visible onscreen and the user
76  * is selecting from it. A menubar is not active until the user
77  * clicks on one of its menuitems. When a menu is active,
78  * passing the mouse over a submenu will pop it up.
79  *
80  * menu_shell->active_menu_item, is however, not an "active"
81  * menu item (there is no such thing) but rather, the selected
82  * menu item in that MenuShell, if there is one.
83  *
84  * There is also is a concept of the current menu and a current
85  * menu item. The current menu item is the selected menu item
86  * that is furthest down in the heirarchy. (Every active menu_shell
87  * does not necessarily contain a selected menu item, but if
88  * it does, then menu_shell->parent_menu_shell must also contain
89  * a selected menu item. The current menu is the menu that 
90  * contains the current menu_item. It will always have a GTK
91  * grab and receive all key presses.
92  *
93  *
94  * Action signals:
95  *
96  *  ::move_current (GtkMenuDirection *dir)
97  *     Moves the current menu item in direction 'dir':
98  *
99  *       GTK_MENU_DIR_PARENT: To the parent menu shell
100  *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
101  *          a submenu.
102  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
103  *          in this menu.
104  * 
105  *     As a a bit of a hack to get movement between menus and
106  *     menubars working, if submenu_placement is different for
107  *     the menu and its MenuShell then the following apply:
108  * 
109  *       - For 'parent' the current menu is not just moved to
110  *         the parent, but moved to the previous entry in the parent
111  *       - For 'child', if there is no child, then current is
112  *         moved to the next item in the parent.
113  *
114  *    Note that the above explanation of ::move_current was written
115  *    before menus and menubars had support for RTL flipping and
116  *    different packing directions, and therefore only applies for
117  *    when text direction and packing direction are both left-to-right.
118  * 
119  *  ::activate_current (GBoolean *force_hide)
120  *     Activate the current item. If 'force_hide' is true, hide
121  *     the current menu item always. Otherwise, only hide
122  *     it if menu_item->klass->hide_on_activate is true.
123  *
124  *  ::cancel ()
125  *     Cancels the current selection
126  */
127
128 #define GTK_MENU_SHELL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_MENU_SHELL, GtkMenuShellPrivate))
129
130 typedef struct _GtkMenuShellPrivate GtkMenuShellPrivate;
131
132 struct _GtkMenuShellPrivate
133 {
134   GtkMnemonicHash *mnemonic_hash;
135   GtkKeyHash *key_hash;
136
137   guint take_focus : 1;
138   guint activated_submenu : 1;
139 };
140
141 static void gtk_menu_shell_set_property      (GObject           *object,
142                                               guint              prop_id,
143                                               const GValue      *value,
144                                               GParamSpec        *pspec);
145 static void gtk_menu_shell_get_property      (GObject           *object,
146                                               guint              prop_id,
147                                               GValue            *value,
148                                               GParamSpec        *pspec);
149 static void gtk_menu_shell_realize           (GtkWidget         *widget);
150 static void gtk_menu_shell_finalize          (GObject           *object);
151 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
152                                               GdkEventButton    *event);
153 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
154                                               GdkEventButton    *event);
155 static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
156                                               GdkEventKey       *event);
157 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
158                                               GdkEventCrossing  *event);
159 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
160                                               GdkEventCrossing  *event);
161 static void gtk_menu_shell_screen_changed    (GtkWidget         *widget,
162                                               GdkScreen         *previous_screen);
163 static gboolean gtk_menu_shell_grab_broken       (GtkWidget         *widget,
164                                               GdkEventGrabBroken *event);
165 static void gtk_menu_shell_add               (GtkContainer      *container,
166                                               GtkWidget         *widget);
167 static void gtk_menu_shell_remove            (GtkContainer      *container,
168                                               GtkWidget         *widget);
169 static void gtk_menu_shell_forall            (GtkContainer      *container,
170                                               gboolean           include_internals,
171                                               GtkCallback        callback,
172                                               gpointer           callback_data);
173 static void gtk_menu_shell_real_insert       (GtkMenuShell *menu_shell,
174                                               GtkWidget    *child,
175                                               gint          position);
176 static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
177 static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
178                                               GtkWidget         *child);
179 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
180                                               GdkEvent          *event);
181 static GType    gtk_menu_shell_child_type  (GtkContainer      *container);
182 static void gtk_menu_shell_real_select_item  (GtkMenuShell      *menu_shell,
183                                               GtkWidget         *menu_item);
184 static gboolean gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell); 
185
186 static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
187                                               GtkMenuDirectionType direction);
188 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
189                                                   gboolean           force_hide);
190 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
191 static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
192                                                   GtkDirectionType   dir);
193
194 static void     gtk_menu_shell_reset_key_hash    (GtkMenuShell *menu_shell);
195 static gboolean gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
196                                                   GdkEventKey  *event);
197 static gboolean gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
198                                                    gint           distance);
199
200 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
201
202 G_DEFINE_ABSTRACT_TYPE (GtkMenuShell, gtk_menu_shell, GTK_TYPE_CONTAINER)
203
204 static void
205 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
206 {
207   GObjectClass *object_class;
208   GtkWidgetClass *widget_class;
209   GtkContainerClass *container_class;
210
211   GtkBindingSet *binding_set;
212
213   object_class = (GObjectClass*) klass;
214   widget_class = (GtkWidgetClass*) klass;
215   container_class = (GtkContainerClass*) klass;
216
217   object_class->set_property = gtk_menu_shell_set_property;
218   object_class->get_property = gtk_menu_shell_get_property;
219   object_class->finalize = gtk_menu_shell_finalize;
220
221   widget_class->realize = gtk_menu_shell_realize;
222   widget_class->button_press_event = gtk_menu_shell_button_press;
223   widget_class->button_release_event = gtk_menu_shell_button_release;
224   widget_class->grab_broken_event = gtk_menu_shell_grab_broken;
225   widget_class->key_press_event = gtk_menu_shell_key_press;
226   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
227   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
228   widget_class->screen_changed = gtk_menu_shell_screen_changed;
229
230   container_class->add = gtk_menu_shell_add;
231   container_class->remove = gtk_menu_shell_remove;
232   container_class->forall = gtk_menu_shell_forall;
233   container_class->child_type = gtk_menu_shell_child_type;
234
235   klass->submenu_placement = GTK_TOP_BOTTOM;
236   klass->deactivate = gtk_real_menu_shell_deactivate;
237   klass->selection_done = NULL;
238   klass->move_current = gtk_real_menu_shell_move_current;
239   klass->activate_current = gtk_real_menu_shell_activate_current;
240   klass->cancel = gtk_real_menu_shell_cancel;
241   klass->select_item = gtk_menu_shell_real_select_item;
242   klass->insert = gtk_menu_shell_real_insert;
243   klass->move_selected = gtk_menu_shell_real_move_selected;
244
245   menu_shell_signals[DEACTIVATE] =
246     g_signal_new (I_("deactivate"),
247                   G_OBJECT_CLASS_TYPE (object_class),
248                   G_SIGNAL_RUN_FIRST,
249                   G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
250                   NULL, NULL,
251                   _gtk_marshal_VOID__VOID,
252                   G_TYPE_NONE, 0);
253   menu_shell_signals[SELECTION_DONE] =
254     g_signal_new (I_("selection-done"),
255                   G_OBJECT_CLASS_TYPE (object_class),
256                   G_SIGNAL_RUN_FIRST,
257                   G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
258                   NULL, NULL,
259                   _gtk_marshal_VOID__VOID,
260                   G_TYPE_NONE, 0);
261   menu_shell_signals[MOVE_CURRENT] =
262     g_signal_new (I_("move_current"),
263                   G_OBJECT_CLASS_TYPE (object_class),
264                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
265                   G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
266                   NULL, NULL,
267                   _gtk_marshal_VOID__ENUM,
268                   G_TYPE_NONE, 1, 
269                   GTK_TYPE_MENU_DIRECTION_TYPE);
270   menu_shell_signals[ACTIVATE_CURRENT] =
271     g_signal_new (I_("activate_current"),
272                   G_OBJECT_CLASS_TYPE (object_class),
273                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
274                   G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
275                   NULL, NULL,
276                   _gtk_marshal_VOID__BOOLEAN,
277                   G_TYPE_NONE, 1, 
278                   G_TYPE_BOOLEAN);
279   menu_shell_signals[CANCEL] =
280     g_signal_new (I_("cancel"),
281                   G_OBJECT_CLASS_TYPE (object_class),
282                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
283                   G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
284                   NULL, NULL,
285                   _gtk_marshal_VOID__VOID,
286                   G_TYPE_NONE, 0);
287   menu_shell_signals[CYCLE_FOCUS] =
288     _gtk_binding_signal_new (I_("cycle_focus"),
289                              G_OBJECT_CLASS_TYPE (object_class),
290                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
291                              G_CALLBACK (gtk_real_menu_shell_cycle_focus),
292                              NULL, NULL,
293                              _gtk_marshal_VOID__ENUM,
294                              G_TYPE_NONE, 1,
295                              GTK_TYPE_DIRECTION_TYPE);
296   /**
297    * GtkMenuShell::move-selected:
298    * @menu_shell: the object on which the signal is emitted
299    * @distance: +1 to move to the next item, -1 to move to the previous
300    *
301    * The ::move-selected signal is emitted to move the selection to
302    * another item. 
303    * 
304    * Returns: %TRUE to stop the signal emission, %FALSE to continue
305    *
306    * Since: 2.12
307    */ 
308   menu_shell_signals[MOVE_SELECTED] =
309     g_signal_new (I_("move_selected"),
310                   G_OBJECT_CLASS_TYPE (object_class),
311                   G_SIGNAL_RUN_LAST,
312                   G_STRUCT_OFFSET (GtkMenuShellClass, move_selected),
313                   _gtk_boolean_handled_accumulator, NULL,
314                   _gtk_marshal_BOOLEAN__INT,
315                   G_TYPE_BOOLEAN, 1,
316                   G_TYPE_INT);
317
318   binding_set = gtk_binding_set_by_class (klass);
319   gtk_binding_entry_add_signal (binding_set,
320                                 GDK_Escape, 0,
321                                 "cancel", 0);
322   gtk_binding_entry_add_signal (binding_set,
323                                 GDK_Return, 0,
324                                 "activate_current", 1,
325                                 G_TYPE_BOOLEAN,
326                                 TRUE);
327   gtk_binding_entry_add_signal (binding_set,
328                                 GDK_ISO_Enter, 0,
329                                 "activate_current", 1,
330                                 G_TYPE_BOOLEAN,
331                                 TRUE);
332   gtk_binding_entry_add_signal (binding_set,
333                                 GDK_KP_Enter, 0,
334                                 "activate_current", 1,
335                                 G_TYPE_BOOLEAN,
336                                 TRUE);
337   gtk_binding_entry_add_signal (binding_set,
338                                 GDK_space, 0,
339                                 "activate_current", 1,
340                                 G_TYPE_BOOLEAN,
341                                 FALSE);
342   gtk_binding_entry_add_signal (binding_set,
343                                 GDK_KP_Space, 0,
344                                 "activate_current", 1,
345                                 G_TYPE_BOOLEAN,
346                                 FALSE);
347   gtk_binding_entry_add_signal (binding_set,
348                                 GDK_F10, 0,
349                                 "cycle_focus", 1,
350                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
351   gtk_binding_entry_add_signal (binding_set,
352                                 GDK_F10, GDK_SHIFT_MASK,
353                                 "cycle_focus", 1,
354                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
355
356   /**
357    * GtkMenuShell:take-focus:
358    *
359    * A boolean that determines whether the menu and its submenus grab the
360    * keyboard focus. See gtk_menu_shell_set_take_focus() and
361    * gtk_menu_shell_get_take_focus().
362    *
363    * Since: 2.8
364    **/
365   g_object_class_install_property (object_class,
366                                    PROP_TAKE_FOCUS,
367                                    g_param_spec_boolean ("take-focus",
368                                                          P_("Take Focus"),
369                                                          P_("A boolean that determines whether the menu grabs the keyboard focus"),
370                                                          TRUE,
371                                                          GTK_PARAM_READWRITE));
372
373   g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
374 }
375
376 static GType
377 gtk_menu_shell_child_type (GtkContainer     *container)
378 {
379   return GTK_TYPE_MENU_ITEM;
380 }
381
382 static void
383 gtk_menu_shell_init (GtkMenuShell *menu_shell)
384 {
385   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
386
387   menu_shell->children = NULL;
388   menu_shell->active_menu_item = NULL;
389   menu_shell->parent_menu_shell = NULL;
390   menu_shell->active = FALSE;
391   menu_shell->have_grab = FALSE;
392   menu_shell->have_xgrab = FALSE;
393   menu_shell->button = 0;
394   menu_shell->activate_time = 0;
395
396   priv->mnemonic_hash = NULL;
397   priv->key_hash = NULL;
398   priv->take_focus = TRUE;
399   priv->activated_submenu = FALSE;
400 }
401
402 static void
403 gtk_menu_shell_set_property (GObject      *object,
404                              guint         prop_id,
405                              const GValue *value,
406                              GParamSpec   *pspec)
407 {
408   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
409
410   switch (prop_id)
411     {
412     case PROP_TAKE_FOCUS:
413       gtk_menu_shell_set_take_focus (menu_shell, g_value_get_boolean (value));
414       break;
415     default:
416       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417       break;
418     }
419 }
420
421 static void
422 gtk_menu_shell_get_property (GObject     *object,
423                              guint        prop_id,
424                              GValue      *value,
425                              GParamSpec  *pspec)
426 {
427   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
428
429   switch (prop_id)
430     {
431     case PROP_TAKE_FOCUS:
432       g_value_set_boolean (value, gtk_menu_shell_get_take_focus (menu_shell));
433       break;
434     default:
435       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
436       break;
437     }
438 }
439
440 static void
441 gtk_menu_shell_finalize (GObject *object)
442 {
443   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
444   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
445
446   if (priv->mnemonic_hash)
447     _gtk_mnemonic_hash_free (priv->mnemonic_hash);
448   if (priv->key_hash)
449     _gtk_key_hash_free (priv->key_hash);
450
451   G_OBJECT_CLASS (gtk_menu_shell_parent_class)->finalize (object);
452 }
453
454
455 void
456 gtk_menu_shell_append (GtkMenuShell *menu_shell,
457                        GtkWidget    *child)
458 {
459   gtk_menu_shell_insert (menu_shell, child, -1);
460 }
461
462 void
463 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
464                         GtkWidget    *child)
465 {
466   gtk_menu_shell_insert (menu_shell, child, 0);
467 }
468
469 void
470 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
471                        GtkWidget    *child,
472                        gint          position)
473 {
474   GtkMenuShellClass *class;
475
476   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
477   g_return_if_fail (GTK_IS_MENU_ITEM (child));
478
479   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
480
481   if (class->insert)
482     class->insert (menu_shell, child, position);
483 }
484
485 static void
486 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
487                             GtkWidget    *child,
488                             gint          position)
489 {
490   menu_shell->children = g_list_insert (menu_shell->children, child, position);
491
492   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
493 }
494
495 void
496 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
497 {
498   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
499
500   g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
501 }
502
503 static void
504 gtk_menu_shell_realize (GtkWidget *widget)
505 {
506   GdkWindowAttr attributes;
507   gint attributes_mask;
508
509   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
510
511   attributes.x = widget->allocation.x;
512   attributes.y = widget->allocation.y;
513   attributes.width = widget->allocation.width;
514   attributes.height = widget->allocation.height;
515   attributes.window_type = GDK_WINDOW_CHILD;
516   attributes.wclass = GDK_INPUT_OUTPUT;
517   attributes.visual = gtk_widget_get_visual (widget);
518   attributes.colormap = gtk_widget_get_colormap (widget);
519   attributes.event_mask = gtk_widget_get_events (widget);
520   attributes.event_mask |= (GDK_EXPOSURE_MASK |
521                             GDK_BUTTON_PRESS_MASK |
522                             GDK_BUTTON_RELEASE_MASK |
523                             GDK_KEY_PRESS_MASK |
524                             GDK_ENTER_NOTIFY_MASK |
525                             GDK_LEAVE_NOTIFY_MASK);
526
527   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
528   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
529   gdk_window_set_user_data (widget->window, widget);
530
531   widget->style = gtk_style_attach (widget->style, widget->window);
532   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
533 }
534
535 void
536 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
537 {
538   if (!menu_shell->active)
539     {
540       gtk_grab_add (GTK_WIDGET (menu_shell));
541       menu_shell->have_grab = TRUE;
542       menu_shell->active = TRUE;
543     }
544 }
545
546 static gint
547 gtk_menu_shell_button_press (GtkWidget      *widget,
548                              GdkEventButton *event)
549 {
550   GtkMenuShell *menu_shell;
551   GtkWidget *menu_item;
552
553   if (event->type != GDK_BUTTON_PRESS)
554     return FALSE;
555
556   menu_shell = GTK_MENU_SHELL (widget);
557
558   if (menu_shell->parent_menu_shell)
559     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
560
561   menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
562
563   if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
564       menu_item != GTK_MENU_SHELL (menu_item->parent)->active_menu_item)
565     {
566       /*  select the menu item *before* activating the shell, so submenus
567        *  which might be open are closed the friendly way. If we activate
568        *  (and thus grab) this menu shell first, we might get grab_broken
569        *  events which will close the entire menu hierarchy. Selecting the
570        *  menu item also fixes up the state as if enter_notify() would
571        *  have run before (which normally selects the item).
572        */
573       if (GTK_MENU_SHELL_GET_CLASS (menu_item->parent)->submenu_placement != GTK_TOP_BOTTOM)
574         {
575           gtk_menu_shell_select_item (GTK_MENU_SHELL (menu_item->parent), menu_item);
576         }
577     }
578
579   if (!menu_shell->active || !menu_shell->button)
580     {
581       _gtk_menu_shell_activate (menu_shell);
582
583       menu_shell->button = event->button;
584
585       if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
586           menu_item->parent == widget &&
587           menu_item != menu_shell->active_menu_item)
588         {
589           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
590             {
591               menu_shell->activate_time = event->time;
592               gtk_menu_shell_select_item (menu_shell, menu_item);
593             }
594         }
595     }
596   else
597     {
598       widget = gtk_get_event_widget ((GdkEvent*) event);
599       if (widget == GTK_WIDGET (menu_shell))
600         {
601           gtk_menu_shell_deactivate (menu_shell);
602           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
603         }
604     }
605
606   if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
607       GTK_MENU_ITEM (menu_item)->submenu != NULL &&
608       !GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_item)->submenu))
609     {
610       GtkMenuShellPrivate *priv;
611
612       _gtk_menu_item_popup_submenu (menu_item, FALSE);
613
614       priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent);
615       priv->activated_submenu = TRUE;
616     }
617
618   return TRUE;
619 }
620
621 static gboolean
622 gtk_menu_shell_grab_broken (GtkWidget          *widget,
623                             GdkEventGrabBroken *event)
624 {
625   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
626
627   if (menu_shell->have_xgrab && event->grab_window == NULL)
628     {
629       /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
630        */
631       
632       gtk_menu_shell_deselect (menu_shell);
633       
634       gtk_menu_shell_deactivate (menu_shell);
635       g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
636     }
637
638   return TRUE;
639 }
640
641 static gint
642 gtk_menu_shell_button_release (GtkWidget      *widget,
643                                GdkEventButton *event)
644 {
645   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
646   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (widget);
647
648   if (menu_shell->active)
649     {
650       GtkWidget *menu_item;
651       gboolean   deactivate = TRUE;
652
653       if (menu_shell->button && (event->button != menu_shell->button))
654         {
655           menu_shell->button = 0;
656           if (menu_shell->parent_menu_shell)
657             return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
658         }
659
660       menu_shell->button = 0;
661       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
662
663       if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
664         {
665           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
666               _gtk_menu_item_is_selectable (menu_item))
667             {
668               GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->submenu;
669
670               if (submenu == NULL)
671                 {
672                   gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
673
674                   deactivate = FALSE;
675                 }
676               else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM ||
677                        priv->activated_submenu)
678                 {
679                   gint popdown_delay;
680                   GTimeVal *popup_time;
681                   gint64 usec_since_popup = 0;
682
683                   g_object_get (gtk_widget_get_settings (widget),
684                                 "gtk-menu-popdown-delay", &popdown_delay,
685                                 NULL);
686
687                   popup_time = g_object_get_data (G_OBJECT (submenu),
688                                                   "gtk-menu-exact-popup-time");
689
690                   if (popup_time)
691                     {
692                       GTimeVal current_time;
693
694                       g_get_current_time (&current_time);
695
696                       usec_since_popup = ((gint64) current_time.tv_sec * 1000 * 1000 +
697                                           (gint64) current_time.tv_usec -
698                                           (gint64) popup_time->tv_sec * 1000 * 1000 -
699                                           (gint64) popup_time->tv_usec);
700
701                       g_object_set_data (G_OBJECT (submenu),
702                                          "gtk-menu-exact-popup-time", NULL);
703                     }
704
705                   /*  only close the submenu on click if we opened the
706                    *  menu explicitely (usec_since_popup == 0) or
707                    *  enough time has passed since it was opened by
708                    *  GtkMenuItem's timeout (usec_since_popup > delay).
709                    */
710                   if (!priv->activated_submenu &&
711                       (usec_since_popup == 0 ||
712                        usec_since_popup > popdown_delay * 1000))
713                     {
714                       _gtk_menu_item_popdown_submenu (menu_item);
715                     }
716                   else
717                     {
718                       gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
719                     }
720
721                   deactivate = FALSE;
722                 }
723             }
724           else if (menu_item &&
725                    !_gtk_menu_item_is_selectable (menu_item) &&
726                    GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
727             {
728               deactivate = FALSE;
729             }
730           else if (menu_shell->parent_menu_shell)
731             {
732               menu_shell->active = TRUE;
733               gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
734               deactivate = FALSE;
735             }
736
737           /* If we ended up on an item with a submenu, leave the menu up.
738            */
739           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
740               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
741             {
742               deactivate = FALSE;
743             }
744         }
745       else /* a very fast press-release */
746         {
747           /* We only ever want to prevent deactivation on the first
748            * press/release. Setting the time to zero is a bit of a
749            * hack, since we could be being triggered in the first
750            * few fractions of a second after a server time wraparound.
751            * the chances of that happening are ~1/10^6, without
752            * serious harm if we lose.
753            */
754           menu_shell->activate_time = 0;
755           deactivate = FALSE;
756         }
757
758       if (deactivate)
759         {
760           gtk_menu_shell_deactivate (menu_shell);
761           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
762         }
763
764       priv->activated_submenu = FALSE;
765     }
766
767   return TRUE;
768 }
769
770 static gint
771 gtk_menu_shell_key_press (GtkWidget   *widget,
772                           GdkEventKey *event)
773 {
774   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
775   gboolean enable_mnemonics;
776
777   if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
778     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
779   
780   if (gtk_bindings_activate_event (GTK_OBJECT (widget), event))
781     return TRUE;
782
783   g_object_get (gtk_widget_get_settings (widget),
784                 "gtk-enable-mnemonics", &enable_mnemonics,
785                 NULL);
786
787   if (enable_mnemonics)
788     return gtk_menu_shell_activate_mnemonic (menu_shell, event);
789
790   return FALSE;
791 }
792
793 static gint
794 gtk_menu_shell_enter_notify (GtkWidget        *widget,
795                              GdkEventCrossing *event)
796 {
797   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
798
799   if (menu_shell->active)
800     {
801       GtkWidget *menu_item;
802
803       menu_item = gtk_get_event_widget ((GdkEvent*) event);
804
805       if (!menu_item ||
806           (GTK_IS_MENU_ITEM (menu_item) && 
807            !_gtk_menu_item_is_selectable (menu_item)))
808         return TRUE;
809
810       if (menu_item->parent == widget &&
811           GTK_IS_MENU_ITEM (menu_item))
812         {
813           if (menu_shell->ignore_enter)
814             return TRUE;
815
816           if (event->detail != GDK_NOTIFY_INFERIOR)
817             {
818               if (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT)
819                 gtk_menu_shell_select_item (menu_shell, menu_item);
820
821               /* If any mouse button is down, and there is a submenu
822                * that is not yet visible, activate it. It's sufficient
823                * to check for any button's mask (not only the one
824                * matching menu_shell->button), because there is no
825                * situation a mouse button could be pressed while
826                * entering a menu item where we wouldn't want to show
827                * its submenu.
828                */
829               if ((event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) &&
830                   GTK_MENU_ITEM (menu_item)->submenu != NULL)
831                 {
832                   GtkMenuShellPrivate *priv;
833
834                   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent);
835                   priv->activated_submenu = TRUE;
836
837                   if (!GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_item)->submenu))
838                     {
839                       gboolean touchscreen_mode;
840
841                       g_object_get (gtk_widget_get_settings (widget),
842                                     "gtk-touchscreen-mode", &touchscreen_mode,
843                                     NULL);
844
845                       if (touchscreen_mode)
846                         _gtk_menu_item_popup_submenu (menu_item, TRUE);
847                     }
848                 }
849             }
850         }
851       else if (menu_shell->parent_menu_shell)
852         {
853           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
854         }
855     }
856
857   return TRUE;
858 }
859
860 static gint
861 gtk_menu_shell_leave_notify (GtkWidget        *widget,
862                              GdkEventCrossing *event)
863 {
864   if (GTK_WIDGET_VISIBLE (widget))
865     {
866       GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
867       GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent*) event);
868       GtkMenuItem *menu_item;
869
870       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
871         return TRUE;
872
873       menu_item = GTK_MENU_ITEM (event_widget);
874
875       if (!_gtk_menu_item_is_selectable (event_widget))
876         return TRUE;
877
878       if ((menu_shell->active_menu_item == event_widget) &&
879           (menu_item->submenu == NULL))
880         {
881           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
882               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
883             {
884               gtk_menu_shell_deselect (menu_shell);
885             }
886         }
887       else if (menu_shell->parent_menu_shell)
888         {
889           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
890         }
891     }
892
893   return TRUE;
894 }
895
896 static void
897 gtk_menu_shell_screen_changed (GtkWidget *widget,
898                                GdkScreen *previous_screen)
899 {
900   gtk_menu_shell_reset_key_hash (GTK_MENU_SHELL (widget));
901 }
902
903 static void
904 gtk_menu_shell_add (GtkContainer *container,
905                     GtkWidget    *widget)
906 {
907   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
908 }
909
910 static void
911 gtk_menu_shell_remove (GtkContainer *container,
912                        GtkWidget    *widget)
913 {
914   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
915   gint was_visible;
916
917   was_visible = GTK_WIDGET_VISIBLE (widget);
918   menu_shell->children = g_list_remove (menu_shell->children, widget);
919   
920   if (widget == menu_shell->active_menu_item)
921     {
922       gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
923       menu_shell->active_menu_item = NULL;
924     }
925
926   gtk_widget_unparent (widget);
927   
928   /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
929    * since that's what is needed by toplevels.
930    */
931   if (was_visible)
932     gtk_widget_queue_resize (GTK_WIDGET (container));
933 }
934
935 static void
936 gtk_menu_shell_forall (GtkContainer *container,
937                        gboolean      include_internals,
938                        GtkCallback   callback,
939                        gpointer      callback_data)
940 {
941   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
942   GtkWidget *child;
943   GList *children;
944
945   children = menu_shell->children;
946   while (children)
947     {
948       child = children->data;
949       children = children->next;
950
951       (* callback) (child, callback_data);
952     }
953 }
954
955
956 static void
957 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
958 {
959   if (menu_shell->active)
960     {
961       menu_shell->button = 0;
962       menu_shell->active = FALSE;
963       menu_shell->activate_time = 0;
964
965       if (menu_shell->active_menu_item)
966         {
967           gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
968           menu_shell->active_menu_item = NULL;
969         }
970
971       if (menu_shell->have_grab)
972         {
973           menu_shell->have_grab = FALSE;
974           gtk_grab_remove (GTK_WIDGET (menu_shell));
975         }
976       if (menu_shell->have_xgrab)
977         {
978           GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
979           
980           menu_shell->have_xgrab = FALSE;
981           gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
982           gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
983         }
984     }
985 }
986
987 static gint
988 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
989                         GtkWidget    *child)
990 {
991   GtkWidget *parent;
992
993   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
994   g_return_val_if_fail (child != NULL, FALSE);
995
996   parent = child->parent;
997   while (GTK_IS_MENU_SHELL (parent))
998     {
999       if (parent == (GtkWidget*) menu_shell)
1000         return TRUE;
1001       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
1002     }
1003
1004   return FALSE;
1005 }
1006
1007 static GtkWidget*
1008 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
1009                          GdkEvent     *event)
1010 {
1011   GtkWidget *menu_item;
1012
1013   menu_item = gtk_get_event_widget ((GdkEvent*) event);
1014   
1015   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
1016     menu_item = menu_item->parent;
1017
1018   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
1019     return menu_item;
1020   else
1021     return NULL;
1022 }
1023
1024 /* Handlers for action signals */
1025
1026 void
1027 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
1028                             GtkWidget    *menu_item)
1029 {
1030   GtkMenuShellClass *class;
1031
1032   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1033   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1034
1035   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1036
1037   if (class->select_item &&
1038       !(menu_shell->active &&
1039         menu_shell->active_menu_item == menu_item))
1040     class->select_item (menu_shell, menu_item);
1041 }
1042
1043 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1044                                    GtkSubmenuPlacement  placement);
1045
1046 static void
1047 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
1048                                  GtkWidget    *menu_item)
1049 {
1050   GtkPackDirection pack_dir = PACK_DIRECTION (menu_shell);
1051
1052   gtk_menu_shell_deselect (menu_shell);
1053
1054   if (!_gtk_menu_item_is_selectable (menu_item))
1055     return;
1056
1057   menu_shell->active_menu_item = menu_item;
1058   if (pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT)
1059     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
1060                                   GTK_LEFT_RIGHT);
1061   else
1062     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
1063                                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
1064   gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
1065
1066   /* This allows the bizarre radio buttons-with-submenus-display-history
1067    * behavior
1068    */
1069   if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1070     gtk_widget_activate (menu_shell->active_menu_item);
1071 }
1072
1073 void
1074 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
1075 {
1076   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1077
1078   if (menu_shell->active_menu_item)
1079     {
1080       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
1081       menu_shell->active_menu_item = NULL;
1082     }
1083 }
1084
1085 void
1086 gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
1087                               GtkWidget         *menu_item,
1088                               gboolean           force_deactivate)
1089 {
1090   GSList *slist, *shells = NULL;
1091   gboolean deactivate = force_deactivate;
1092
1093   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1094   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1095
1096   if (!deactivate)
1097     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
1098
1099   g_object_ref (menu_shell);
1100   g_object_ref (menu_item);
1101
1102   if (deactivate)
1103     {
1104       GtkMenuShell *parent_menu_shell = menu_shell;
1105
1106       do
1107         {
1108           g_object_ref (parent_menu_shell);
1109           shells = g_slist_prepend (shells, parent_menu_shell);
1110           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
1111         }
1112       while (parent_menu_shell);
1113       shells = g_slist_reverse (shells);
1114
1115       gtk_menu_shell_deactivate (menu_shell);
1116   
1117       /* flush the x-queue, so any grabs are removed and
1118        * the menu is actually taken down
1119        */
1120       gdk_display_sync (gtk_widget_get_display (menu_item));
1121     }
1122
1123   gtk_widget_activate (menu_item);
1124
1125   for (slist = shells; slist; slist = slist->next)
1126     {
1127       g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
1128       g_object_unref (slist->data);
1129     }
1130   g_slist_free (shells);
1131
1132   g_object_unref (menu_shell);
1133   g_object_unref (menu_item);
1134 }
1135
1136 /* Distance should be +/- 1 */
1137 static gboolean
1138 gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
1139                                    gint           distance)
1140 {
1141   if (menu_shell->active_menu_item)
1142     {
1143       GList *node = g_list_find (menu_shell->children,
1144                                  menu_shell->active_menu_item);
1145       GList *start_node = node;
1146       gboolean wrap_around;
1147
1148       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1149                     "gtk-keynav-wrap-around", &wrap_around,
1150                     NULL);
1151
1152       if (distance > 0)
1153         {
1154           node = node->next;
1155           while (node != start_node && 
1156                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1157             {
1158               if (node)
1159                 node = node->next;
1160               else if (wrap_around)
1161                 node = menu_shell->children;
1162               else
1163                 {
1164                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1165                   break;
1166                 }
1167             }
1168         }
1169       else
1170         {
1171           node = node->prev;
1172           while (node != start_node &&
1173                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1174             {
1175               if (node)
1176                 node = node->prev;
1177               else if (wrap_around)
1178                 node = g_list_last (menu_shell->children);
1179               else
1180                 {
1181                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1182                   break;
1183                 }
1184             }
1185         }
1186       
1187       if (node)
1188         gtk_menu_shell_select_item (menu_shell, node->data);
1189     }
1190
1191   return TRUE;
1192 }
1193
1194 /* Distance should be +/- 1 */
1195 static void
1196 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
1197                               gint           distance)
1198 {
1199   gboolean handled = FALSE;
1200
1201   g_signal_emit (menu_shell, menu_shell_signals[MOVE_SELECTED], 0,
1202                  distance, &handled);
1203 }
1204
1205 /**
1206  * gtk_menu_shell_select_first:
1207  * @menu_shell: a #GtkMenuShell
1208  * @search_sensitive: if %TRUE, search for the first selectable
1209  *                    menu item, otherwise select nothing if
1210  *                    the first item isn't sensitive. This
1211  *                    should be %FALSE if the menu is being
1212  *                    popped up initially.
1213  * 
1214  * Select the first visible or selectable child of the menu shell;
1215  * don't select tearoff items unless the only item is a tearoff
1216  * item.
1217  *
1218  * Since: 2.2
1219  **/
1220 void
1221 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1222                              gboolean      search_sensitive)
1223 {
1224   GtkWidget *to_select = NULL;
1225   GList *tmp_list;
1226
1227   tmp_list = menu_shell->children;
1228   while (tmp_list)
1229     {
1230       GtkWidget *child = tmp_list->data;
1231       
1232       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1233           _gtk_menu_item_is_selectable (child))
1234         {
1235           to_select = child;
1236           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1237             break;
1238         }
1239       
1240       tmp_list = tmp_list->next;
1241     }
1242
1243   if (to_select)
1244     gtk_menu_shell_select_item (menu_shell, to_select);
1245 }
1246
1247 void
1248 _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1249                              gboolean      search_sensitive)
1250 {
1251   GtkWidget *to_select = NULL;
1252   GList *tmp_list;
1253
1254   tmp_list = g_list_last (menu_shell->children);
1255   while (tmp_list)
1256     {
1257       GtkWidget *child = tmp_list->data;
1258       
1259       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1260           _gtk_menu_item_is_selectable (child))
1261         {
1262           to_select = child;
1263           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1264             break;
1265         }
1266       
1267       tmp_list = tmp_list->prev;
1268     }
1269
1270   if (to_select)
1271     gtk_menu_shell_select_item (menu_shell, to_select);
1272 }
1273
1274 static gboolean
1275 gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
1276 {
1277   GtkMenuItem *menu_item;
1278
1279   if (menu_shell->active_menu_item == NULL)
1280     return FALSE;
1281
1282   menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item); 
1283   
1284   if (menu_item->submenu)
1285     {
1286       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), FALSE);
1287       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1288       if (GTK_MENU_SHELL (menu_item->submenu)->active_menu_item)
1289         return TRUE;
1290     }
1291
1292   return FALSE;
1293 }
1294
1295 static void
1296 gtk_real_menu_shell_move_current (GtkMenuShell         *menu_shell,
1297                                   GtkMenuDirectionType  direction)
1298 {
1299   GtkMenuShell *parent_menu_shell = NULL;
1300   gboolean had_selection;
1301   gboolean touchscreen_mode;
1302
1303   had_selection = menu_shell->active_menu_item != NULL;
1304
1305   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1306                 "gtk-touchscreen-mode", &touchscreen_mode,
1307                 NULL);
1308
1309   if (menu_shell->parent_menu_shell)
1310     parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1311
1312   switch (direction)
1313     {
1314     case GTK_MENU_DIR_PARENT:
1315       if (touchscreen_mode &&
1316           menu_shell->active_menu_item &&
1317           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu &&
1318           GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu))
1319         {
1320           /* if we are on a menu item that has an open submenu but the
1321            * focus is not in that submenu (e.g. because it's empty or
1322            * has only insensitive items), close that submenu instead
1323            * of running into the code below which would close *this*
1324            * menu.
1325            */
1326           _gtk_menu_item_popdown_submenu (menu_shell->active_menu_item);
1327         }
1328       else if (parent_menu_shell)
1329         {
1330           if (touchscreen_mode)
1331             {
1332               /* close menu when returning from submenu. */
1333               _gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->parent_menu_item);
1334               break;
1335             }
1336
1337           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1338               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1339             gtk_menu_shell_deselect (menu_shell);
1340           else
1341             {
1342               if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1343                 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1344               else
1345                 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1346               gtk_menu_shell_select_submenu_first (parent_menu_shell);
1347             }
1348         }
1349       /* If there is no parent and the submenu is in the opposite direction
1350        * to the menu, then make the PARENT direction wrap around to
1351        * the bottom of the submenu.
1352        */
1353       else if (menu_shell->active_menu_item &&
1354                _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1355                GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1356         {
1357           GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
1358
1359           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement !=
1360               GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement)
1361             _gtk_menu_shell_select_last (submenu, TRUE);
1362         }
1363       break;
1364
1365     case GTK_MENU_DIR_CHILD:
1366       if (menu_shell->active_menu_item &&
1367           _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1368           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1369         {
1370           if (gtk_menu_shell_select_submenu_first (menu_shell))
1371             break;
1372         }
1373
1374       /* Try to find a menu running the opposite direction */
1375       while (parent_menu_shell &&
1376              (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1377               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1378         {
1379           parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->parent_menu_shell);
1380         }
1381
1382       if (parent_menu_shell)
1383         {
1384           if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1385             gtk_menu_shell_move_selected (parent_menu_shell, 1);
1386           else
1387             gtk_menu_shell_move_selected (parent_menu_shell, -1);
1388
1389           gtk_menu_shell_select_submenu_first (parent_menu_shell);
1390         }
1391       break;
1392
1393     case GTK_MENU_DIR_PREV:
1394       gtk_menu_shell_move_selected (menu_shell, -1);
1395       if (!had_selection &&
1396           !menu_shell->active_menu_item &&
1397           menu_shell->children)
1398         _gtk_menu_shell_select_last (menu_shell, TRUE);
1399       break;
1400
1401     case GTK_MENU_DIR_NEXT:
1402       gtk_menu_shell_move_selected (menu_shell, 1);
1403       if (!had_selection &&
1404           !menu_shell->active_menu_item &&
1405           menu_shell->children)
1406         gtk_menu_shell_select_first (menu_shell, TRUE);
1407       break;
1408     }
1409 }
1410
1411 static void
1412 gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1413                                       gboolean           force_hide)
1414 {
1415   if (menu_shell->active_menu_item &&
1416       _gtk_menu_item_is_selectable (menu_shell->active_menu_item))
1417   {
1418     if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1419       gtk_menu_shell_activate_item (menu_shell,
1420                                     menu_shell->active_menu_item,
1421                                     force_hide);
1422     else
1423       _gtk_menu_item_popup_submenu (menu_shell->active_menu_item, FALSE);
1424   }
1425 }
1426
1427 static void
1428 gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1429 {
1430   /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1431    */
1432   gtk_menu_shell_deselect (menu_shell);
1433   
1434   gtk_menu_shell_deactivate (menu_shell);
1435   g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1436 }
1437
1438 static void
1439 gtk_real_menu_shell_cycle_focus (GtkMenuShell      *menu_shell,
1440                                  GtkDirectionType   dir)
1441 {
1442   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1443     {
1444       if (menu_shell->parent_menu_shell)
1445         menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1446       else
1447         menu_shell = NULL;
1448     }
1449
1450   if (menu_shell)
1451     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1452 }
1453
1454 gint
1455 _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell)
1456 {
1457   GtkMenuShellClass *klass = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1458   
1459   if (klass->get_popup_delay)
1460     {
1461       return klass->get_popup_delay (menu_shell);
1462     }
1463   else
1464     {
1465       gint popup_delay;
1466       GtkWidget *widget = GTK_WIDGET (menu_shell);
1467       
1468       g_object_get (gtk_widget_get_settings (widget),
1469                     "gtk-menu-popup-delay", &popup_delay,
1470                     NULL);
1471       
1472       return popup_delay;
1473     }
1474 }
1475
1476 /**
1477  * gtk_menu_shell_cancel:
1478  * @menu_shell: a #GtkMenuShell
1479  * 
1480  * Cancels the selection within the menu shell.  
1481  * 
1482  * Since: 2.4
1483  */
1484 void
1485 gtk_menu_shell_cancel (GtkMenuShell *menu_shell)
1486 {
1487   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1488
1489   g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0);
1490 }
1491
1492 static GtkMnemonicHash *
1493 gtk_menu_shell_get_mnemonic_hash (GtkMenuShell *menu_shell,
1494                                   gboolean      create)
1495 {
1496   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1497
1498   if (!private->mnemonic_hash && create)
1499     private->mnemonic_hash = _gtk_mnemonic_hash_new ();
1500   
1501   return private->mnemonic_hash;
1502 }
1503
1504 static void
1505 menu_shell_add_mnemonic_foreach (guint    keyval,
1506                                  GSList  *targets,
1507                                  gpointer data)
1508 {
1509   GtkKeyHash *key_hash = data;
1510
1511   _gtk_key_hash_add_entry (key_hash, keyval, 0, GUINT_TO_POINTER (keyval));
1512 }
1513
1514 static GtkKeyHash *
1515 gtk_menu_shell_get_key_hash (GtkMenuShell *menu_shell,
1516                              gboolean      create)
1517 {
1518   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1519   GtkWidget *widget = GTK_WIDGET (menu_shell);
1520
1521   if (!private->key_hash && create && gtk_widget_has_screen (widget))
1522     {
1523       GtkMnemonicHash *mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1524       GdkScreen *screen = gtk_widget_get_screen (widget);
1525       GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
1526
1527       if (!mnemonic_hash)
1528         return NULL;
1529       
1530       private->key_hash = _gtk_key_hash_new (keymap, NULL);
1531
1532       _gtk_mnemonic_hash_foreach (mnemonic_hash,
1533                                   menu_shell_add_mnemonic_foreach,
1534                                   private->key_hash);
1535     }
1536   
1537   return private->key_hash;
1538 }
1539
1540 static void
1541 gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell)
1542 {
1543   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1544
1545   if (private->key_hash)
1546     {
1547       _gtk_key_hash_free (private->key_hash);
1548       private->key_hash = NULL;
1549     }
1550 }
1551
1552 static gboolean
1553 gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
1554                                   GdkEventKey  *event)
1555 {
1556   GtkMnemonicHash *mnemonic_hash;
1557   GtkKeyHash *key_hash;
1558   GSList *entries;
1559   gboolean result = FALSE;
1560
1561   mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1562   if (!mnemonic_hash)
1563     return FALSE;
1564
1565   key_hash = gtk_menu_shell_get_key_hash (menu_shell, TRUE);
1566   if (!key_hash)
1567     return FALSE;
1568   
1569   entries = _gtk_key_hash_lookup (key_hash,
1570                                   event->hardware_keycode,
1571                                   event->state,
1572                                   gtk_accelerator_get_default_mod_mask (),
1573                                   event->group);
1574
1575   if (entries)
1576     result = _gtk_mnemonic_hash_activate (mnemonic_hash,
1577                                           GPOINTER_TO_UINT (entries->data));
1578
1579   return result;
1580 }
1581
1582 void
1583 _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell,
1584                               guint      keyval,
1585                               GtkWidget *target)
1586 {
1587   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1588   g_return_if_fail (GTK_IS_WIDGET (target));
1589
1590   _gtk_mnemonic_hash_add (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1591                           keyval, target);
1592   gtk_menu_shell_reset_key_hash (menu_shell);
1593 }
1594
1595 void
1596 _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
1597                                  guint      keyval,
1598                                  GtkWidget *target)
1599 {
1600   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1601   g_return_if_fail (GTK_IS_WIDGET (target));
1602   
1603   _gtk_mnemonic_hash_remove (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1604                              keyval, target);
1605   gtk_menu_shell_reset_key_hash (menu_shell);
1606 }
1607
1608 /**
1609  * gtk_menu_shell_get_take_focus:
1610  * @menu_shell: a #GtkMenuShell
1611  *
1612  * Returns %TRUE if the menu shell will take the keyboard focus on popup.
1613  *
1614  * Returns: %TRUE if the menu shell will take the keyboard focus on popup.
1615  *
1616  * Since: 2.8
1617  **/
1618 gboolean
1619 gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell)
1620 {
1621   GtkMenuShellPrivate *priv;
1622
1623   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1624
1625   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1626
1627   return priv->take_focus;
1628 }
1629
1630 /**
1631  * gtk_menu_shell_set_take_focus:
1632  * @menu_shell: a #GtkMenuShell
1633  * @take_focus: %TRUE if the menu shell should take the keyboard focus on popup.
1634  *
1635  * If @take_focus is %TRUE (the default) the menu shell will take the keyboard 
1636  * focus so that it will receive all keyboard events which is needed to enable
1637  * keyboard navigation in menus.
1638  *
1639  * Setting @take_focus to %FALSE is useful only for special applications
1640  * like virtual keyboard implementations which should not take keyboard
1641  * focus.
1642  *
1643  * The @take_focus state of a menu or menu bar is automatically propagated
1644  * to submenus whenever a submenu is popped up, so you don't have to worry
1645  * about recursively setting it for your entire menu hierarchy. Only when
1646  * programmatically picking a submenu and popping it up manually, the
1647  * @take_focus property of the submenu needs to be set explicitely.
1648  *
1649  * Note that setting it to %FALSE has side-effects:
1650  *
1651  * If the focus is in some other app, it keeps the focus and keynav in
1652  * the menu doesn't work. Consequently, keynav on the menu will only
1653  * work if the focus is on some toplevel owned by the onscreen keyboard.
1654  *
1655  * To avoid confusing the user, menus with @take_focus set to %FALSE
1656  * should not display mnemonics or accelerators, since it cannot be
1657  * guaranteed that they will work.
1658  *
1659  * See also gdk_keyboard_grab()
1660  *
1661  * Since: 2.8
1662  **/
1663 void
1664 gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
1665                                gboolean      take_focus)
1666 {
1667   GtkMenuShellPrivate *priv;
1668
1669   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1670
1671   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1672
1673   if (priv->take_focus != take_focus)
1674     {
1675       priv->take_focus = take_focus;
1676       g_object_notify (G_OBJECT (menu_shell), "take-focus");
1677     }
1678 }
1679
1680 #define __GTK_MENU_SHELL_C__
1681 #include "gtkaliasdef.c"