]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenu.c
GtkMenuShell: take a single device for grabs.
[~andy/gtk] / gtk / gtkmenu.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 #include "config.h"
29 #include <string.h>
30 #include "gdk/gdkkeysyms.h"
31 #include "gtkaccellabel.h"
32 #include "gtkaccelmap.h"
33 #include "gtkbindings.h"
34 #include "gtkcheckmenuitem.h"
35 #include  <gobject/gvaluecollector.h>
36 #include "gtkmain.h"
37 #include "gtkmarshalers.h"
38 #include "gtkmenu.h"
39 #include "gtktearoffmenuitem.h"
40 #include "gtkwindow.h"
41 #include "gtkhbox.h"
42 #include "gtkvscrollbar.h"
43 #include "gtksettings.h"
44 #include "gtkprivate.h"
45 #include "gtkintl.h"
46 #include "gtkalias.h"
47
48
49 #define DEFAULT_POPUP_DELAY     225
50 #define DEFAULT_POPDOWN_DELAY  1000
51
52 #define NAVIGATION_REGION_OVERSHOOT 50  /* How much the navigation region
53                                          * extends below the submenu
54                                          */
55
56 #define MENU_SCROLL_STEP1      8
57 #define MENU_SCROLL_STEP2     15
58 #define MENU_SCROLL_FAST_ZONE  8
59 #define MENU_SCROLL_TIMEOUT1  50
60 #define MENU_SCROLL_TIMEOUT2  20
61
62 #define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
63 #define ATTACHED_MENUS "gtk-attached-menus"
64
65 typedef struct _GtkMenuAttachData       GtkMenuAttachData;
66 typedef struct _GtkMenuPrivate          GtkMenuPrivate;
67 typedef struct _GtkMenuPopdownData      GtkMenuPopdownData;
68
69 struct _GtkMenuAttachData
70 {
71   GtkWidget *attach_widget;
72   GtkMenuDetachFunc detacher;
73 };
74
75 struct _GtkMenuPrivate 
76 {
77   gint x;
78   gint y;
79   gboolean initially_pushed_in;
80
81   /* info used for the table */
82   guint *heights;
83   gint heights_length;
84
85   gint monitor_num;
86
87   /* Cached layout information */
88   gint n_rows;
89   gint n_columns;
90
91   gchar *title;
92
93   /* Arrow states */
94   GtkStateType lower_arrow_state;
95   GtkStateType upper_arrow_state;
96
97   guint have_layout           : 1;
98   guint seen_item_enter       : 1;
99   guint have_position         : 1;
100   guint ignore_button_release : 1;
101   guint no_toggle_size        : 1;
102 };
103
104 struct _GtkMenuPopdownData
105 {
106   GtkMenu *menu;
107   GdkDevice *device;
108 };
109
110 typedef struct
111 {
112   gint left_attach;
113   gint right_attach;
114   gint top_attach;
115   gint bottom_attach;
116   gint effective_left_attach;
117   gint effective_right_attach;
118   gint effective_top_attach;
119   gint effective_bottom_attach;
120 } AttachInfo;
121
122 enum {
123   MOVE_SCROLL,
124   LAST_SIGNAL
125 };
126
127 enum {
128   PROP_0,
129   PROP_ACTIVE,
130   PROP_ACCEL_GROUP,
131   PROP_ACCEL_PATH,
132   PROP_ATTACH_WIDGET,
133   PROP_TEAROFF_STATE,
134   PROP_TEAROFF_TITLE,
135   PROP_MONITOR,
136   PROP_RESERVE_TOGGLE_SIZE
137 };
138
139 enum {
140   CHILD_PROP_0,
141   CHILD_PROP_LEFT_ATTACH,
142   CHILD_PROP_RIGHT_ATTACH,
143   CHILD_PROP_TOP_ATTACH,
144   CHILD_PROP_BOTTOM_ATTACH
145 };
146
147 static void     gtk_menu_set_property      (GObject          *object,
148                                             guint             prop_id,
149                                             const GValue     *value,
150                                             GParamSpec       *pspec);
151 static void     gtk_menu_get_property      (GObject          *object,
152                                             guint             prop_id,
153                                             GValue           *value,
154                                             GParamSpec       *pspec);
155 static void     gtk_menu_set_child_property(GtkContainer     *container,
156                                             GtkWidget        *child,
157                                             guint             property_id,
158                                             const GValue     *value,
159                                             GParamSpec       *pspec);
160 static void     gtk_menu_get_child_property(GtkContainer     *container,
161                                             GtkWidget        *child,
162                                             guint             property_id,
163                                             GValue           *value,
164                                             GParamSpec       *pspec);
165 static void     gtk_menu_destroy           (GtkObject        *object);
166 static void     gtk_menu_realize           (GtkWidget        *widget);
167 static void     gtk_menu_unrealize         (GtkWidget        *widget);
168 static void     gtk_menu_size_request      (GtkWidget        *widget,
169                                             GtkRequisition   *requisition);
170 static void     gtk_menu_size_allocate     (GtkWidget        *widget,
171                                             GtkAllocation    *allocation);
172 static void     gtk_menu_paint             (GtkWidget        *widget,
173                                             GdkEventExpose   *expose);
174 static void     gtk_menu_show              (GtkWidget        *widget);
175 static gboolean gtk_menu_expose            (GtkWidget        *widget,
176                                             GdkEventExpose   *event);
177 static gboolean gtk_menu_key_press         (GtkWidget        *widget,
178                                             GdkEventKey      *event);
179 static gboolean gtk_menu_scroll            (GtkWidget        *widget,
180                                             GdkEventScroll   *event);
181 static gboolean gtk_menu_button_press      (GtkWidget        *widget,
182                                             GdkEventButton   *event);
183 static gboolean gtk_menu_button_release    (GtkWidget        *widget,
184                                             GdkEventButton   *event);
185 static gboolean gtk_menu_motion_notify     (GtkWidget        *widget,
186                                             GdkEventMotion   *event);
187 static gboolean gtk_menu_enter_notify      (GtkWidget        *widget,
188                                             GdkEventCrossing *event);
189 static gboolean gtk_menu_leave_notify      (GtkWidget        *widget,
190                                             GdkEventCrossing *event);
191 static void     gtk_menu_scroll_to         (GtkMenu          *menu,
192                                             gint              offset);
193 static void     gtk_menu_grab_notify       (GtkWidget        *widget,
194                                             gboolean          was_grabbed);
195
196 static void     gtk_menu_stop_scrolling         (GtkMenu  *menu);
197 static void     gtk_menu_remove_scroll_timeout  (GtkMenu  *menu);
198 static gboolean gtk_menu_scroll_timeout         (gpointer  data);
199 static gboolean gtk_menu_scroll_timeout_initial (gpointer  data);
200 static void     gtk_menu_start_scrolling        (GtkMenu  *menu);
201
202 static void     gtk_menu_scroll_item_visible (GtkMenuShell    *menu_shell,
203                                               GtkWidget       *menu_item);
204 static void     gtk_menu_select_item       (GtkMenuShell     *menu_shell,
205                                             GtkWidget        *menu_item);
206 static void     gtk_menu_real_insert       (GtkMenuShell     *menu_shell,
207                                             GtkWidget        *child,
208                                             gint              position);
209 static void     gtk_menu_scrollbar_changed (GtkAdjustment    *adjustment,
210                                             GtkMenu          *menu);
211 static void     gtk_menu_handle_scrolling  (GtkMenu          *menu,
212                                             gint              event_x,
213                                             gint              event_y,
214                                             gboolean          enter,
215                                             gboolean          motion);
216 static void     gtk_menu_set_tearoff_hints (GtkMenu          *menu,
217                                             gint             width);
218 static void     gtk_menu_style_set         (GtkWidget        *widget,
219                                             GtkStyle         *previous_style);
220 static gboolean gtk_menu_focus             (GtkWidget        *widget,
221                                             GtkDirectionType direction);
222 static gint     gtk_menu_get_popup_delay   (GtkMenuShell     *menu_shell);
223 static void     gtk_menu_move_current      (GtkMenuShell     *menu_shell,
224                                             GtkMenuDirectionType direction);
225 static void     gtk_menu_real_move_scroll  (GtkMenu          *menu,
226                                             GtkScrollType     type);
227
228 static void     gtk_menu_stop_navigating_submenu       (GtkMenu          *menu);
229 static gboolean gtk_menu_stop_navigating_submenu_cb    (gpointer          user_data);
230 static gboolean gtk_menu_navigating_submenu            (GtkMenu          *menu,
231                                                         gint              event_x,
232                                                         gint              event_y);
233 static void     gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
234                                                         GtkMenuItem      *menu_item,
235                                                         GdkEventCrossing *event);
236  
237 static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
238 static void gtk_menu_show_all       (GtkWidget         *widget);
239 static void gtk_menu_hide_all       (GtkWidget         *widget);
240 static void gtk_menu_position       (GtkMenu           *menu);
241 static void gtk_menu_reparent       (GtkMenu           *menu, 
242                                      GtkWidget         *new_parent, 
243                                      gboolean           unrealize);
244 static void gtk_menu_remove         (GtkContainer      *menu,
245                                      GtkWidget         *widget);
246
247 static void gtk_menu_update_title   (GtkMenu           *menu);
248
249 static void       menu_grab_transfer_window_destroy (GtkMenu *menu);
250 static GdkWindow *menu_grab_transfer_window_get     (GtkMenu *menu);
251
252 static gboolean gtk_menu_real_can_activate_accel (GtkWidget *widget,
253                                                   guint      signal_id);
254 static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
255                                            gboolean group_changed);
256
257 static const gchar attach_data_key[] = "gtk-menu-attach-data";
258
259 static guint menu_signals[LAST_SIGNAL] = { 0 };
260
261 static GtkMenuPrivate *
262 gtk_menu_get_private (GtkMenu *menu)
263 {
264   return G_TYPE_INSTANCE_GET_PRIVATE (menu, GTK_TYPE_MENU, GtkMenuPrivate);
265 }
266
267 G_DEFINE_TYPE (GtkMenu, gtk_menu, GTK_TYPE_MENU_SHELL)
268
269 static void
270 menu_queue_resize (GtkMenu *menu)
271 {
272   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
273
274   priv->have_layout = FALSE;
275   gtk_widget_queue_resize (GTK_WIDGET (menu));
276 }
277
278 static void
279 attach_info_free (AttachInfo *info)
280 {
281   g_slice_free (AttachInfo, info);
282 }
283
284 static AttachInfo *
285 get_attach_info (GtkWidget *child)
286 {
287   GObject *object = G_OBJECT (child);
288   AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY);
289
290   if (!ai)
291     {
292       ai = g_slice_new0 (AttachInfo);
293       g_object_set_data_full (object, I_(ATTACH_INFO_KEY), ai,
294                               (GDestroyNotify) attach_info_free);
295     }
296
297   return ai;
298 }
299
300 static gboolean
301 is_grid_attached (AttachInfo *ai)
302 {
303   return (ai->left_attach >= 0 &&
304           ai->right_attach >= 0 &&
305           ai->top_attach >= 0 &&
306           ai->bottom_attach >= 0);
307 }
308
309 static void
310 menu_ensure_layout (GtkMenu *menu)
311 {
312   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
313
314   if (!priv->have_layout)
315     {
316       GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
317       GList *l;
318       gchar *row_occupied;
319       gint current_row;
320       gint max_right_attach;      
321       gint max_bottom_attach;
322
323       /* Find extents of gridded portion
324        */
325       max_right_attach = 1;
326       max_bottom_attach = 0;
327
328       for (l = menu_shell->children; l; l = l->next)
329         {
330           GtkWidget *child = l->data;
331           AttachInfo *ai = get_attach_info (child);
332
333           if (is_grid_attached (ai))
334             {
335               max_bottom_attach = MAX (max_bottom_attach, ai->bottom_attach);
336               max_right_attach = MAX (max_right_attach, ai->right_attach);
337             }
338         }
339          
340       /* Find empty rows
341        */
342       row_occupied = g_malloc0 (max_bottom_attach);
343
344       for (l = menu_shell->children; l; l = l->next)
345         {
346           GtkWidget *child = l->data;
347           AttachInfo *ai = get_attach_info (child);
348
349           if (is_grid_attached (ai))
350             {
351               gint i;
352
353               for (i = ai->top_attach; i < ai->bottom_attach; i++)
354                 row_occupied[i] = TRUE;
355             }
356         }
357
358       /* Lay non-grid-items out in those rows
359        */
360       current_row = 0;
361       for (l = menu_shell->children; l; l = l->next)
362         {
363           GtkWidget *child = l->data;
364           AttachInfo *ai = get_attach_info (child);
365
366           if (!is_grid_attached (ai))
367             {
368               while (current_row < max_bottom_attach && row_occupied[current_row])
369                 current_row++;
370                 
371               ai->effective_left_attach = 0;
372               ai->effective_right_attach = max_right_attach;
373               ai->effective_top_attach = current_row;
374               ai->effective_bottom_attach = current_row + 1;
375
376               current_row++;
377             }
378           else
379             {
380               ai->effective_left_attach = ai->left_attach;
381               ai->effective_right_attach = ai->right_attach;
382               ai->effective_top_attach = ai->top_attach;
383               ai->effective_bottom_attach = ai->bottom_attach;
384             }
385         }
386
387       g_free (row_occupied);
388
389       priv->n_rows = MAX (current_row, max_bottom_attach);
390       priv->n_columns = max_right_attach;
391       priv->have_layout = TRUE;
392     }
393 }
394
395
396 static gint
397 gtk_menu_get_n_columns (GtkMenu *menu)
398 {
399   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
400
401   menu_ensure_layout (menu);
402
403   return priv->n_columns;
404 }
405
406 static gint
407 gtk_menu_get_n_rows (GtkMenu *menu)
408 {
409   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
410
411   menu_ensure_layout (menu);
412
413   return priv->n_rows;
414 }
415
416 static void
417 get_effective_child_attach (GtkWidget *child,
418                             int       *l,
419                             int       *r,
420                             int       *t,
421                             int       *b)
422 {
423   GtkMenu *menu = GTK_MENU (child->parent);
424   AttachInfo *ai;
425   
426   menu_ensure_layout (menu);
427
428   ai = get_attach_info (child);
429
430   if (l)
431     *l = ai->effective_left_attach;
432   if (r)
433     *r = ai->effective_right_attach;
434   if (t)
435     *t = ai->effective_top_attach;
436   if (b)
437     *b = ai->effective_bottom_attach;
438
439 }
440
441 static void
442 gtk_menu_class_init (GtkMenuClass *class)
443 {
444   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
445   GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
446   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
447   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
448   GtkMenuShellClass *menu_shell_class = GTK_MENU_SHELL_CLASS (class);
449   GtkBindingSet *binding_set;
450   
451   gobject_class->set_property = gtk_menu_set_property;
452   gobject_class->get_property = gtk_menu_get_property;
453
454   object_class->destroy = gtk_menu_destroy;
455   
456   widget_class->realize = gtk_menu_realize;
457   widget_class->unrealize = gtk_menu_unrealize;
458   widget_class->size_request = gtk_menu_size_request;
459   widget_class->size_allocate = gtk_menu_size_allocate;
460   widget_class->show = gtk_menu_show;
461   widget_class->expose_event = gtk_menu_expose;
462   widget_class->scroll_event = gtk_menu_scroll;
463   widget_class->key_press_event = gtk_menu_key_press;
464   widget_class->button_press_event = gtk_menu_button_press;
465   widget_class->button_release_event = gtk_menu_button_release;
466   widget_class->motion_notify_event = gtk_menu_motion_notify;
467   widget_class->show_all = gtk_menu_show_all;
468   widget_class->hide_all = gtk_menu_hide_all;
469   widget_class->enter_notify_event = gtk_menu_enter_notify;
470   widget_class->leave_notify_event = gtk_menu_leave_notify;
471   widget_class->style_set = gtk_menu_style_set;
472   widget_class->focus = gtk_menu_focus;
473   widget_class->can_activate_accel = gtk_menu_real_can_activate_accel;
474   widget_class->grab_notify = gtk_menu_grab_notify;
475
476   container_class->remove = gtk_menu_remove;
477   container_class->get_child_property = gtk_menu_get_child_property;
478   container_class->set_child_property = gtk_menu_set_child_property;
479   
480   menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
481   menu_shell_class->deactivate = gtk_menu_deactivate;
482   menu_shell_class->select_item = gtk_menu_select_item;
483   menu_shell_class->insert = gtk_menu_real_insert;
484   menu_shell_class->get_popup_delay = gtk_menu_get_popup_delay;
485   menu_shell_class->move_current = gtk_menu_move_current;
486
487   menu_signals[MOVE_SCROLL] =
488     g_signal_new_class_handler (I_("move-scroll"),
489                                 G_OBJECT_CLASS_TYPE (object_class),
490                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
491                                 G_CALLBACK (gtk_menu_real_move_scroll),
492                                 NULL, NULL,
493                                 _gtk_marshal_VOID__ENUM,
494                                 G_TYPE_NONE, 1,
495                                 GTK_TYPE_SCROLL_TYPE);
496
497   /**
498    * GtkMenu:active:
499    *
500    * The index of the currently selected menu item, or -1 if no
501    * menu item is selected.
502    *
503    * Since: 2.14
504    **/
505   g_object_class_install_property (gobject_class,
506                                    PROP_ACTIVE,
507                                    g_param_spec_int ("active",
508                                                      P_("Active"),
509                                                      P_("The currently selected menu item"),
510                                                      -1, G_MAXINT, -1,
511                                                      GTK_PARAM_READWRITE));
512
513   /**
514    * GtkMenu:accel-group:
515    *
516    * The accel group holding accelerators for the menu.
517    *
518    * Since: 2.14
519    **/
520   g_object_class_install_property (gobject_class,
521                                    PROP_ACCEL_GROUP,
522                                    g_param_spec_object ("accel-group",
523                                                         P_("Accel Group"),
524                                                         P_("The accel group holding accelerators for the menu"),
525                                                         GTK_TYPE_ACCEL_GROUP,
526                                                         GTK_PARAM_READWRITE));
527
528   /**
529    * GtkMenu:accel-path:
530    *
531    * An accel path used to conveniently construct accel paths of child items.
532    *
533    * Since: 2.14
534    **/
535   g_object_class_install_property (gobject_class,
536                                    PROP_ACCEL_PATH,
537                                    g_param_spec_string ("accel-path",
538                                                         P_("Accel Path"),
539                                                         P_("An accel path used to conveniently construct accel paths of child items"),
540                                                         NULL,
541                                                         GTK_PARAM_READWRITE));
542
543   /**
544    * GtkMenu:attach-widget:
545    *
546    * The widget the menu is attached to. Setting this property attaches
547    * the menu without a #GtkMenuDetachFunc. If you need to use a detacher,
548    * use gtk_menu_attach_to_widget() directly.
549    *
550    * Since: 2.14
551    **/
552   g_object_class_install_property (gobject_class,
553                                    PROP_ATTACH_WIDGET,
554                                    g_param_spec_object ("attach-widget",
555                                                         P_("Attach Widget"),
556                                                         P_("The widget the menu is attached to"),
557                                                         GTK_TYPE_WIDGET,
558                                                         GTK_PARAM_READWRITE));
559
560   g_object_class_install_property (gobject_class,
561                                    PROP_TEAROFF_TITLE,
562                                    g_param_spec_string ("tearoff-title",
563                                                         P_("Tearoff Title"),
564                                                         P_("A title that may be displayed by the window manager when this menu is torn-off"),
565                                                         NULL,
566                                                         GTK_PARAM_READWRITE));
567
568   /**
569    * GtkMenu:tearoff-state:
570    *
571    * A boolean that indicates whether the menu is torn-off.
572    *
573    * Since: 2.6
574    **/
575   g_object_class_install_property (gobject_class,
576                                    PROP_TEAROFF_STATE,
577                                    g_param_spec_boolean ("tearoff-state",
578                                                          P_("Tearoff State"),
579                                                          P_("A boolean that indicates whether the menu is torn-off"),
580                                                          FALSE,
581                                                          GTK_PARAM_READWRITE));
582
583   /**
584    * GtkMenu:monitor:
585    *
586    * The monitor the menu will be popped up on.
587    *
588    * Since: 2.14
589    **/
590   g_object_class_install_property (gobject_class,
591                                    PROP_MONITOR,
592                                    g_param_spec_int ("monitor",
593                                                      P_("Monitor"),
594                                                      P_("The monitor the menu will be popped up on"),
595                                                      -1, G_MAXINT, -1,
596                                                      GTK_PARAM_READWRITE));
597
598   gtk_widget_class_install_style_property (widget_class,
599                                            g_param_spec_int ("vertical-padding",
600                                                              P_("Vertical Padding"),
601                                                              P_("Extra space at the top and bottom of the menu"),
602                                                              0,
603                                                              G_MAXINT,
604                                                              1,
605                                                              GTK_PARAM_READABLE));
606
607   /**
608    * GtkMenu:reserve-toggle-size:
609    *
610    * A boolean that indicates whether the menu reserves space for
611    * toggles and icons, regardless of their actual presence.
612    *
613    * This property should only be changed from its default value
614    * for special-purposes such as tabular menus. Regular menus that
615    * are connected to a menu bar or context menus should reserve
616    * toggle space for consistency.
617    *
618    * Since: 2.18
619    */
620   g_object_class_install_property (gobject_class,
621                                    PROP_RESERVE_TOGGLE_SIZE,
622                                    g_param_spec_boolean ("reserve-toggle-size",
623                                                          P_("Reserve Toggle Size"),
624                                                          P_("A boolean that indicates whether the menu reserves space for toggles and icons"),
625                                                          TRUE,
626                                                          GTK_PARAM_READWRITE));
627
628   gtk_widget_class_install_style_property (widget_class,
629                                            g_param_spec_int ("horizontal-padding",
630                                                              P_("Horizontal Padding"),
631                                                              P_("Extra space at the left and right edges of the menu"),
632                                                              0,
633                                                              G_MAXINT,
634                                                              0,
635                                                              GTK_PARAM_READABLE));
636
637   gtk_widget_class_install_style_property (widget_class,
638                                            g_param_spec_int ("vertical-offset",
639                                                              P_("Vertical Offset"),
640                                                              P_("When the menu is a submenu, position it this number of pixels offset vertically"),
641                                                              G_MININT,
642                                                              G_MAXINT,
643                                                              0,
644                                                              GTK_PARAM_READABLE));
645
646   gtk_widget_class_install_style_property (widget_class,
647                                            g_param_spec_int ("horizontal-offset",
648                                                              P_("Horizontal Offset"),
649                                                              P_("When the menu is a submenu, position it this number of pixels offset horizontally"),
650                                                              G_MININT,
651                                                              G_MAXINT,
652                                                              -2,
653                                                              GTK_PARAM_READABLE));
654
655   gtk_widget_class_install_style_property (widget_class,
656                                            g_param_spec_boolean ("double-arrows",
657                                                                  P_("Double Arrows"),
658                                                                  P_("When scrolling, always show both arrows."),
659                                                                  TRUE,
660                                                                  GTK_PARAM_READABLE));
661
662   /**
663    * GtkMenu:arrow-placement:
664    *
665    * Indicates where scroll arrows should be placed.
666    *
667    * Since: 2.16
668    **/
669   gtk_widget_class_install_style_property (widget_class,
670                                            g_param_spec_enum ("arrow-placement",
671                                                               P_("Arrow Placement"),
672                                                               P_("Indicates where scroll arrows should be placed"),
673                                                               GTK_TYPE_ARROW_PLACEMENT,
674                                                               GTK_ARROWS_BOTH,
675                                                               GTK_PARAM_READABLE));
676
677  gtk_container_class_install_child_property (container_class,
678                                              CHILD_PROP_LEFT_ATTACH,
679                                               g_param_spec_int ("left-attach",
680                                                                P_("Left Attach"),
681                                                                P_("The column number to attach the left side of the child to"),
682                                                                 -1, INT_MAX, -1,
683                                                                GTK_PARAM_READWRITE));
684
685  gtk_container_class_install_child_property (container_class,
686                                              CHILD_PROP_RIGHT_ATTACH,
687                                               g_param_spec_int ("right-attach",
688                                                                P_("Right Attach"),
689                                                                P_("The column number to attach the right side of the child to"),
690                                                                 -1, INT_MAX, -1,
691                                                                GTK_PARAM_READWRITE));
692
693  gtk_container_class_install_child_property (container_class,
694                                              CHILD_PROP_TOP_ATTACH,
695                                               g_param_spec_int ("top-attach",
696                                                                P_("Top Attach"),
697                                                                P_("The row number to attach the top of the child to"),
698                                                                 -1, INT_MAX, -1,
699                                                                GTK_PARAM_READWRITE));
700
701  gtk_container_class_install_child_property (container_class,
702                                              CHILD_PROP_BOTTOM_ATTACH,
703                                               g_param_spec_int ("bottom-attach",
704                                                                P_("Bottom Attach"),
705                                                                P_("The row number to attach the bottom of the child to"),
706                                                                 -1, INT_MAX, -1,
707                                                                GTK_PARAM_READWRITE));
708
709  /**
710   * GtkMenu::arrow-scaling
711   *
712   * Arbitrary constant to scale down the size of the scroll arrow.
713   *
714   * Since: 2.16
715   */
716   gtk_widget_class_install_style_property (widget_class,
717                                            g_param_spec_float ("arrow-scaling",
718                                                                P_("Arrow Scaling"),
719                                                                P_("Arbitrary constant to scale down the size of the scroll arrow"),
720                                                                0.0, 1.0, 0.7,
721                                                                GTK_PARAM_READABLE));
722
723   binding_set = gtk_binding_set_by_class (class);
724   gtk_binding_entry_add_signal (binding_set,
725                                 GDK_Up, 0,
726                                 I_("move-current"), 1,
727                                 GTK_TYPE_MENU_DIRECTION_TYPE,
728                                 GTK_MENU_DIR_PREV);
729   gtk_binding_entry_add_signal (binding_set,
730                                 GDK_KP_Up, 0,
731                                 "move-current", 1,
732                                 GTK_TYPE_MENU_DIRECTION_TYPE,
733                                 GTK_MENU_DIR_PREV);
734   gtk_binding_entry_add_signal (binding_set,
735                                 GDK_Down, 0,
736                                 "move-current", 1,
737                                 GTK_TYPE_MENU_DIRECTION_TYPE,
738                                 GTK_MENU_DIR_NEXT);
739   gtk_binding_entry_add_signal (binding_set,
740                                 GDK_KP_Down, 0,
741                                 "move-current", 1,
742                                 GTK_TYPE_MENU_DIRECTION_TYPE,
743                                 GTK_MENU_DIR_NEXT);
744   gtk_binding_entry_add_signal (binding_set,
745                                 GDK_Left, 0,
746                                 "move-current", 1,
747                                 GTK_TYPE_MENU_DIRECTION_TYPE,
748                                 GTK_MENU_DIR_PARENT);
749   gtk_binding_entry_add_signal (binding_set,
750                                 GDK_KP_Left, 0,
751                                 "move-current", 1,
752                                 GTK_TYPE_MENU_DIRECTION_TYPE,
753                                 GTK_MENU_DIR_PARENT);
754   gtk_binding_entry_add_signal (binding_set,
755                                 GDK_Right, 0,
756                                 "move-current", 1,
757                                 GTK_TYPE_MENU_DIRECTION_TYPE,
758                                 GTK_MENU_DIR_CHILD);
759   gtk_binding_entry_add_signal (binding_set,
760                                 GDK_KP_Right, 0,
761                                 "move-current", 1,
762                                 GTK_TYPE_MENU_DIRECTION_TYPE,
763                                 GTK_MENU_DIR_CHILD);
764   gtk_binding_entry_add_signal (binding_set,
765                                 GDK_Home, 0,
766                                 "move-scroll", 1,
767                                 GTK_TYPE_SCROLL_TYPE,
768                                 GTK_SCROLL_START);
769   gtk_binding_entry_add_signal (binding_set,
770                                 GDK_KP_Home, 0,
771                                 "move-scroll", 1,
772                                 GTK_TYPE_SCROLL_TYPE,
773                                 GTK_SCROLL_START);
774   gtk_binding_entry_add_signal (binding_set,
775                                 GDK_End, 0,
776                                 "move-scroll", 1,
777                                 GTK_TYPE_SCROLL_TYPE,
778                                 GTK_SCROLL_END);
779   gtk_binding_entry_add_signal (binding_set,
780                                 GDK_KP_End, 0,
781                                 "move-scroll", 1,
782                                 GTK_TYPE_SCROLL_TYPE,
783                                 GTK_SCROLL_END);
784   gtk_binding_entry_add_signal (binding_set,
785                                 GDK_Page_Up, 0,
786                                 "move-scroll", 1,
787                                 GTK_TYPE_SCROLL_TYPE,
788                                 GTK_SCROLL_PAGE_UP);
789   gtk_binding_entry_add_signal (binding_set,
790                                 GDK_KP_Page_Up, 0,
791                                 "move-scroll", 1,
792                                 GTK_TYPE_SCROLL_TYPE,
793                                 GTK_SCROLL_PAGE_UP);
794   gtk_binding_entry_add_signal (binding_set,
795                                 GDK_Page_Down, 0,
796                                 "move-scroll", 1,
797                                 GTK_TYPE_SCROLL_TYPE,
798                                 GTK_SCROLL_PAGE_DOWN);
799   gtk_binding_entry_add_signal (binding_set,
800                                 GDK_KP_Page_Down, 0,
801                                 "move-scroll", 1,
802                                 GTK_TYPE_SCROLL_TYPE,
803                                 GTK_SCROLL_PAGE_DOWN);
804
805   gtk_settings_install_property (g_param_spec_boolean ("gtk-can-change-accels",
806                                                        P_("Can change accelerators"),
807                                                        P_("Whether menu accelerators can be changed by pressing a key over the menu item"),
808                                                        FALSE,
809                                                        GTK_PARAM_READWRITE));
810
811   gtk_settings_install_property (g_param_spec_int ("gtk-menu-popup-delay",
812                                                    P_("Delay before submenus appear"),
813                                                    P_("Minimum time the pointer must stay over a menu item before the submenu appear"),
814                                                    0,
815                                                    G_MAXINT,
816                                                    DEFAULT_POPUP_DELAY,
817                                                    GTK_PARAM_READWRITE));
818
819   gtk_settings_install_property (g_param_spec_int ("gtk-menu-popdown-delay",
820                                                    P_("Delay before hiding a submenu"),
821                                                    P_("The time before hiding a submenu when the pointer is moving towards the submenu"),
822                                                    0,
823                                                    G_MAXINT,
824                                                    DEFAULT_POPDOWN_DELAY,
825                                                    GTK_PARAM_READWRITE));
826
827   g_type_class_add_private (gobject_class, sizeof (GtkMenuPrivate));
828 }
829
830
831 static void
832 gtk_menu_set_property (GObject      *object,
833                        guint         prop_id,
834                        const GValue *value,
835                        GParamSpec   *pspec)
836 {
837   GtkMenu *menu = GTK_MENU (object);
838
839   switch (prop_id)
840     {
841     case PROP_ACTIVE:
842       gtk_menu_set_active (menu, g_value_get_int (value));
843       break;
844     case PROP_ACCEL_GROUP:
845       gtk_menu_set_accel_group (menu, g_value_get_object (value));
846       break;
847     case PROP_ACCEL_PATH:
848       gtk_menu_set_accel_path (menu, g_value_get_string (value));
849       break;
850     case PROP_ATTACH_WIDGET:
851       {
852         GtkWidget *widget;
853
854         widget = gtk_menu_get_attach_widget (menu);
855         if (widget)
856           gtk_menu_detach (menu);
857
858         widget = (GtkWidget*) g_value_get_object (value); 
859         if (widget)
860           gtk_menu_attach_to_widget (menu, widget, NULL);
861       }
862       break;
863     case PROP_TEAROFF_STATE:
864       gtk_menu_set_tearoff_state (menu, g_value_get_boolean (value));
865       break;
866     case PROP_TEAROFF_TITLE:
867       gtk_menu_set_title (menu, g_value_get_string (value));
868       break;
869     case PROP_MONITOR:
870       gtk_menu_set_monitor (menu, g_value_get_int (value));
871       break;
872     case PROP_RESERVE_TOGGLE_SIZE:
873       gtk_menu_set_reserve_toggle_size (menu, g_value_get_boolean (value));
874       break;
875     default:
876       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
877       break;
878     }
879 }
880
881 static void
882 gtk_menu_get_property (GObject     *object,
883                        guint        prop_id,
884                        GValue      *value,
885                        GParamSpec  *pspec)
886 {
887   GtkMenu *menu = GTK_MENU (object);
888
889   switch (prop_id)
890     {
891     case PROP_ACTIVE:
892       g_value_set_int (value, g_list_index (GTK_MENU_SHELL (menu)->children, gtk_menu_get_active (menu)));
893       break;
894     case PROP_ACCEL_GROUP:
895       g_value_set_object (value, gtk_menu_get_accel_group (menu));
896       break;
897     case PROP_ACCEL_PATH:
898       g_value_set_string (value, gtk_menu_get_accel_path (menu));
899       break;
900     case PROP_ATTACH_WIDGET:
901       g_value_set_object (value, gtk_menu_get_attach_widget (menu));
902       break;
903     case PROP_TEAROFF_STATE:
904       g_value_set_boolean (value, gtk_menu_get_tearoff_state (menu));
905       break;
906     case PROP_TEAROFF_TITLE:
907       g_value_set_string (value, gtk_menu_get_title (menu));
908       break;
909     case PROP_MONITOR:
910       g_value_set_int (value, gtk_menu_get_monitor (menu));
911       break;
912     case PROP_RESERVE_TOGGLE_SIZE:
913       g_value_set_boolean (value, gtk_menu_get_reserve_toggle_size (menu));
914       break;
915     default:
916       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
917       break;
918     }
919 }
920
921 static void
922 gtk_menu_set_child_property (GtkContainer *container,
923                              GtkWidget    *child,
924                              guint         property_id,
925                              const GValue *value,
926                              GParamSpec   *pspec)
927 {
928   GtkMenu *menu = GTK_MENU (container);
929   AttachInfo *ai = get_attach_info (child);
930
931   switch (property_id)
932     {
933     case CHILD_PROP_LEFT_ATTACH:
934       ai->left_attach = g_value_get_int (value);
935       break;
936     case CHILD_PROP_RIGHT_ATTACH:
937       ai->right_attach = g_value_get_int (value);
938       break;
939     case CHILD_PROP_TOP_ATTACH:
940       ai->top_attach = g_value_get_int (value); 
941       break;
942     case CHILD_PROP_BOTTOM_ATTACH:
943       ai->bottom_attach = g_value_get_int (value);
944       break;
945
946     default:
947       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
948       return;
949     }
950
951   menu_queue_resize (menu);
952 }
953
954 static void
955 gtk_menu_get_child_property (GtkContainer *container,
956                              GtkWidget    *child,
957                              guint         property_id,
958                              GValue       *value,
959                              GParamSpec   *pspec)
960 {
961   AttachInfo *ai = get_attach_info (child);
962
963   switch (property_id)
964     {
965     case CHILD_PROP_LEFT_ATTACH:
966       g_value_set_int (value, ai->left_attach);
967       break;
968     case CHILD_PROP_RIGHT_ATTACH:
969       g_value_set_int (value, ai->right_attach);
970       break;
971     case CHILD_PROP_TOP_ATTACH:
972       g_value_set_int (value, ai->top_attach);
973       break;
974     case CHILD_PROP_BOTTOM_ATTACH:
975       g_value_set_int (value, ai->bottom_attach);
976       break;
977       
978     default:
979       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
980       return;
981     }
982 }
983
984 static gboolean
985 gtk_menu_window_event (GtkWidget *window,
986                        GdkEvent  *event,
987                        GtkWidget *menu)
988 {
989   gboolean handled = FALSE;
990
991   g_object_ref (window);
992   g_object_ref (menu);
993
994   switch (event->type)
995     {
996     case GDK_KEY_PRESS:
997     case GDK_KEY_RELEASE:
998       handled = gtk_widget_event (menu, event);
999       break;
1000     default:
1001       break;
1002     }
1003
1004   g_object_unref (window);
1005   g_object_unref (menu);
1006
1007   return handled;
1008 }
1009
1010 static void
1011 gtk_menu_window_size_request (GtkWidget      *window,
1012                               GtkRequisition *requisition,
1013                               GtkMenu        *menu)
1014 {
1015   GtkMenuPrivate *private = gtk_menu_get_private (menu);
1016
1017   if (private->have_position)
1018     {
1019       GdkScreen *screen = gtk_widget_get_screen (window);
1020       GdkRectangle monitor;
1021       
1022       gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor);
1023
1024       if (private->y + requisition->height > monitor.y + monitor.height)
1025         requisition->height = monitor.y + monitor.height - private->y;
1026
1027       if (private->y < monitor.y)
1028         requisition->height -= monitor.y - private->y;
1029     }
1030 }
1031
1032 static void
1033 gtk_menu_init (GtkMenu *menu)
1034 {
1035   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
1036
1037   menu->parent_menu_item = NULL;
1038   menu->old_active_menu_item = NULL;
1039   menu->accel_group = NULL;
1040   menu->position_func = NULL;
1041   menu->position_func_data = NULL;
1042   menu->toggle_size = 0;
1043
1044   menu->toplevel = g_object_connect (g_object_new (GTK_TYPE_WINDOW,
1045                                                    "type", GTK_WINDOW_POPUP,
1046                                                    "child", menu,
1047                                                    NULL),
1048                                      "signal::event", gtk_menu_window_event, menu,
1049                                      "signal::size-request", gtk_menu_window_size_request, menu,
1050                                      "signal::destroy", gtk_widget_destroyed, &menu->toplevel,
1051                                      NULL);
1052   gtk_window_set_resizable (GTK_WINDOW (menu->toplevel), FALSE);
1053   gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->toplevel), 0);
1054
1055   /* Refloat the menu, so that reference counting for the menu isn't
1056    * affected by it being a child of the toplevel
1057    */
1058   g_object_force_floating (G_OBJECT (menu));
1059   menu->needs_destruction_ref_count = TRUE;
1060
1061   menu->view_window = NULL;
1062   menu->bin_window = NULL;
1063
1064   menu->scroll_offset = 0;
1065   menu->scroll_step  = 0;
1066   menu->timeout_id = 0;
1067   menu->scroll_fast = FALSE;
1068   
1069   menu->tearoff_window = NULL;
1070   menu->tearoff_hbox = NULL;
1071   menu->torn_off = FALSE;
1072   menu->tearoff_active = FALSE;
1073   menu->tearoff_adjustment = NULL;
1074   menu->tearoff_scrollbar = NULL;
1075
1076   menu->upper_arrow_visible = FALSE;
1077   menu->lower_arrow_visible = FALSE;
1078   menu->upper_arrow_prelight = FALSE;
1079   menu->lower_arrow_prelight = FALSE;
1080
1081   priv->upper_arrow_state = GTK_STATE_NORMAL;
1082   priv->lower_arrow_state = GTK_STATE_NORMAL;
1083
1084   priv->have_layout = FALSE;
1085   priv->monitor_num = -1;
1086 }
1087
1088 static void
1089 gtk_menu_destroy (GtkObject *object)
1090 {
1091   GtkMenu *menu = GTK_MENU (object);
1092   GtkMenuAttachData *data;
1093   GtkMenuPrivate *priv; 
1094
1095   gtk_menu_remove_scroll_timeout (menu);
1096   
1097   data = g_object_get_data (G_OBJECT (object), attach_data_key);
1098   if (data)
1099     gtk_menu_detach (menu);
1100   
1101   gtk_menu_stop_navigating_submenu (menu);
1102
1103   if (menu->old_active_menu_item)
1104     {
1105       g_object_unref (menu->old_active_menu_item);
1106       menu->old_active_menu_item = NULL;
1107     }
1108
1109   /* Add back the reference count for being a child */
1110   if (menu->needs_destruction_ref_count)
1111     {
1112       menu->needs_destruction_ref_count = FALSE;
1113       g_object_ref (object);
1114     }
1115   
1116   if (menu->accel_group)
1117     {
1118       g_object_unref (menu->accel_group);
1119       menu->accel_group = NULL;
1120     }
1121
1122   if (menu->toplevel)
1123     gtk_widget_destroy (menu->toplevel);
1124
1125   if (menu->tearoff_window)
1126     gtk_widget_destroy (menu->tearoff_window);
1127
1128   priv = gtk_menu_get_private (menu);
1129
1130   if (priv->heights)
1131     {
1132       g_free (priv->heights);
1133       priv->heights = NULL;
1134     }
1135
1136   if (priv->title)
1137     {
1138       g_free (priv->title);
1139       priv->title = NULL;
1140     }
1141
1142   GTK_OBJECT_CLASS (gtk_menu_parent_class)->destroy (object);
1143 }
1144
1145 static void
1146 menu_change_screen (GtkMenu   *menu,
1147                     GdkScreen *new_screen)
1148 {
1149   GtkMenuPrivate *private = gtk_menu_get_private (menu);
1150
1151   if (gtk_widget_has_screen (GTK_WIDGET (menu)))
1152     {
1153       if (new_screen == gtk_widget_get_screen (GTK_WIDGET (menu)))
1154         return;
1155     }
1156
1157   if (menu->torn_off)
1158     {
1159       gtk_window_set_screen (GTK_WINDOW (menu->tearoff_window), new_screen);
1160       gtk_menu_position (menu);
1161     }
1162
1163   gtk_window_set_screen (GTK_WINDOW (menu->toplevel), new_screen);
1164   private->monitor_num = -1;
1165 }
1166
1167 static void
1168 attach_widget_screen_changed (GtkWidget *attach_widget,
1169                               GdkScreen *previous_screen,
1170                               GtkMenu   *menu)
1171 {
1172   if (gtk_widget_has_screen (attach_widget) &&
1173       !g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
1174     {
1175       menu_change_screen (menu, gtk_widget_get_screen (attach_widget));
1176     }
1177 }
1178
1179 void
1180 gtk_menu_attach_to_widget (GtkMenu             *menu,
1181                            GtkWidget           *attach_widget,
1182                            GtkMenuDetachFunc    detacher)
1183 {
1184   GtkMenuAttachData *data;
1185   GList *list;
1186   
1187   g_return_if_fail (GTK_IS_MENU (menu));
1188   g_return_if_fail (GTK_IS_WIDGET (attach_widget));
1189   
1190   /* keep this function in sync with gtk_widget_set_parent()
1191    */
1192   
1193   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1194   if (data)
1195     {
1196       g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
1197                  g_type_name (G_TYPE_FROM_INSTANCE (data->attach_widget)));
1198      return;
1199     }
1200   
1201   g_object_ref_sink (menu);
1202   
1203   data = g_slice_new (GtkMenuAttachData);
1204   data->attach_widget = attach_widget;
1205   
1206   g_signal_connect (attach_widget, "screen-changed",
1207                     G_CALLBACK (attach_widget_screen_changed), menu);
1208   attach_widget_screen_changed (attach_widget, NULL, menu);
1209   
1210   data->detacher = detacher;
1211   g_object_set_data (G_OBJECT (menu), I_(attach_data_key), data);
1212   list = g_object_steal_data (G_OBJECT (attach_widget), ATTACHED_MENUS);
1213   if (!g_list_find (list, menu))
1214     {
1215       list = g_list_prepend (list, menu);
1216     }
1217   g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list,
1218                           (GDestroyNotify) g_list_free);
1219
1220   if (gtk_widget_get_state (GTK_WIDGET (menu)) != GTK_STATE_NORMAL)
1221     gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
1222   
1223   /* we don't need to set the style here, since
1224    * we are a toplevel widget.
1225    */
1226
1227   /* Fallback title for menu comes from attach widget */
1228   gtk_menu_update_title (menu);
1229 }
1230
1231 GtkWidget*
1232 gtk_menu_get_attach_widget (GtkMenu *menu)
1233 {
1234   GtkMenuAttachData *data;
1235   
1236   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1237   
1238   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1239   if (data)
1240     return data->attach_widget;
1241   return NULL;
1242 }
1243
1244 void
1245 gtk_menu_detach (GtkMenu *menu)
1246 {
1247   GtkMenuAttachData *data;
1248   GList *list;
1249   
1250   g_return_if_fail (GTK_IS_MENU (menu));
1251   
1252   /* keep this function in sync with gtk_widget_unparent()
1253    */
1254   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1255   if (!data)
1256     {
1257       g_warning ("gtk_menu_detach(): menu is not attached");
1258       return;
1259     }
1260   g_object_set_data (G_OBJECT (menu), I_(attach_data_key), NULL);
1261   
1262   g_signal_handlers_disconnect_by_func (data->attach_widget,
1263                                         (gpointer) attach_widget_screen_changed,
1264                                         menu);
1265
1266   if (data->detacher)
1267     data->detacher (data->attach_widget, menu);
1268   list = g_object_steal_data (G_OBJECT (data->attach_widget), ATTACHED_MENUS);
1269   list = g_list_remove (list, menu);
1270   if (list)
1271     g_object_set_data_full (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), list,
1272                             (GDestroyNotify) g_list_free);
1273   else
1274     g_object_set_data (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), NULL);
1275   
1276   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
1277     gtk_widget_unrealize (GTK_WIDGET (menu));
1278   
1279   g_slice_free (GtkMenuAttachData, data);
1280   
1281   /* Fallback title for menu comes from attach widget */
1282   gtk_menu_update_title (menu);
1283
1284   g_object_unref (menu);
1285 }
1286
1287 static void
1288 gtk_menu_remove (GtkContainer *container,
1289                  GtkWidget    *widget)
1290 {
1291   GtkMenu *menu = GTK_MENU (container);
1292
1293   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1294
1295   /* Clear out old_active_menu_item if it matches the item we are removing
1296    */
1297   if (menu->old_active_menu_item == widget)
1298     {
1299       g_object_unref (menu->old_active_menu_item);
1300       menu->old_active_menu_item = NULL;
1301     }
1302
1303   GTK_CONTAINER_CLASS (gtk_menu_parent_class)->remove (container, widget);
1304   g_object_set_data (G_OBJECT (widget), I_(ATTACH_INFO_KEY), NULL);
1305
1306   menu_queue_resize (menu);
1307 }
1308
1309 GtkWidget*
1310 gtk_menu_new (void)
1311 {
1312   return g_object_new (GTK_TYPE_MENU, NULL);
1313 }
1314
1315 static void
1316 gtk_menu_real_insert (GtkMenuShell *menu_shell,
1317                       GtkWidget    *child,
1318                       gint          position)
1319 {
1320   GtkMenu *menu = GTK_MENU (menu_shell);
1321   AttachInfo *ai = get_attach_info (child);
1322
1323   ai->left_attach = -1;
1324   ai->right_attach = -1;
1325   ai->top_attach = -1;
1326   ai->bottom_attach = -1;
1327
1328   if (gtk_widget_get_realized (GTK_WIDGET (menu_shell)))
1329     gtk_widget_set_parent_window (child, menu->bin_window);
1330
1331   GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->insert (menu_shell, child, position);
1332
1333   menu_queue_resize (menu);
1334 }
1335
1336 static void
1337 gtk_menu_tearoff_bg_copy (GtkMenu *menu)
1338 {
1339   GtkWidget *widget;
1340   gint width, height;
1341
1342   widget = GTK_WIDGET (menu);
1343
1344   if (menu->torn_off)
1345     {
1346       GdkPixmap *pixmap;
1347       GdkGC *gc;
1348       GdkGCValues gc_values;
1349
1350       menu->tearoff_active = FALSE;
1351       menu->saved_scroll_offset = menu->scroll_offset;
1352       
1353       gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1354       gc = gdk_gc_new_with_values (widget->window,
1355                                    &gc_values, GDK_GC_SUBWINDOW);
1356       
1357       gdk_drawable_get_size (menu->tearoff_window->window, &width, &height);
1358       
1359       pixmap = gdk_pixmap_new (menu->tearoff_window->window,
1360                                width,
1361                                height,
1362                                -1);
1363
1364       gdk_draw_drawable (pixmap, gc,
1365                          menu->tearoff_window->window,
1366                          0, 0, 0, 0, -1, -1);
1367       g_object_unref (gc);
1368
1369       gtk_widget_set_size_request (menu->tearoff_window,
1370                                    width,
1371                                    height);
1372
1373       gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
1374       g_object_unref (pixmap);
1375     }
1376 }
1377
1378 static gboolean
1379 popup_grab_on_window (GdkWindow *window,
1380                       GdkDevice *keyboard,
1381                       GdkDevice *pointer,
1382                       guint32    activate_time)
1383 {
1384   if (keyboard &&
1385       gdk_device_grab (keyboard, window,
1386                        GDK_OWNERSHIP_WINDOW, TRUE,
1387                        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1388                        NULL, activate_time) != GDK_GRAB_SUCCESS)
1389     return FALSE;
1390
1391   if (pointer &&
1392       gdk_device_grab (pointer, window,
1393                        GDK_OWNERSHIP_WINDOW, TRUE,
1394                        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1395                        GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
1396                        GDK_POINTER_MOTION_MASK,
1397                        NULL, activate_time) != GDK_GRAB_SUCCESS)
1398     {
1399       if (keyboard)
1400         gdk_device_ungrab (keyboard, activate_time);
1401
1402       return FALSE;
1403     }
1404
1405   return TRUE;
1406 }
1407
1408 /**
1409  * gtk_menu_popup_for_device:
1410  * @menu: a #GtkMenu.
1411  * @device: (allow-none): a #GdkDevice
1412  * @parent_menu_shell: (allow-none): the menu shell containing the triggering menu item, or %NULL
1413  * @parent_menu_item: (allow-none): the menu item whose activation triggered the popup, or %NULL
1414  * @func: (allow-none): a user supplied function used to position the menu, or %NULL
1415  * @data: (allow-none): user supplied data to be passed to @func.
1416  * @button: the mouse button which was pressed to initiate the event.
1417  * @activate_time: the time at which the activation event occurred.
1418  *
1419  * Displays a menu and makes it available for selection.  Applications can use
1420  * this function to display context-sensitive menus, and will typically supply
1421  * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data
1422  * parameters. The default menu positioning function will position the menu
1423  * at the current position of @device (or its corresponding pointer).
1424  *
1425  * The @button parameter should be the mouse button pressed to initiate
1426  * the menu popup. If the menu popup was initiated by something other than
1427  * a mouse button press, such as a mouse button release or a keypress,
1428  * @button should be 0.
1429  *
1430  * The @activate_time parameter is used to conflict-resolve initiation of
1431  * concurrent requests for mouse/keyboard grab requests. To function
1432  * properly, this needs to be the time stamp of the user event (such as
1433  * a mouse click or key press) that caused the initiation of the popup.
1434  * Only if no such event is available, gtk_get_current_event_time() can
1435  * be used instead.
1436  *
1437  * Since: 3.0
1438  */
1439 void
1440 gtk_menu_popup_for_device (GtkMenu             *menu,
1441                            GdkDevice           *device,
1442                            GtkWidget           *parent_menu_shell,
1443                            GtkWidget           *parent_menu_item,
1444                            GtkMenuPositionFunc  func,
1445                            gpointer             data,
1446                            guint                button,
1447                            guint32              activate_time)
1448 {
1449   GtkWidget *widget;
1450   GtkWidget *xgrab_shell;
1451   GtkWidget *parent;
1452   GdkEvent *current_event;
1453   GtkMenuShell *menu_shell;
1454   gboolean grab_keyboard;
1455   GtkMenuPrivate *priv;
1456   GtkWidget *parent_toplevel;
1457   GdkDevice *keyboard, *pointer;
1458
1459   g_return_if_fail (GTK_IS_MENU (menu));
1460   g_return_if_fail (GDK_IS_DEVICE (device));
1461
1462   widget = GTK_WIDGET (menu);
1463   menu_shell = GTK_MENU_SHELL (menu);
1464   priv = gtk_menu_get_private (menu);
1465
1466   if (device->source == GDK_SOURCE_KEYBOARD)
1467     {
1468       keyboard = device;
1469       pointer = gdk_device_get_associated_device (device);
1470     }
1471   else
1472     {
1473       pointer = device;
1474       keyboard = gdk_device_get_associated_device (device);
1475     }
1476
1477   menu_shell->parent_menu_shell = parent_menu_shell;
1478
1479   priv->seen_item_enter = FALSE;
1480   
1481   /* Find the last viewable ancestor, and make an X grab on it
1482    */
1483   parent = GTK_WIDGET (menu);
1484   xgrab_shell = NULL;
1485   while (parent)
1486     {
1487       gboolean viewable = TRUE;
1488       GtkWidget *tmp = parent;
1489       
1490       while (tmp)
1491         {
1492           if (!gtk_widget_get_mapped (tmp))
1493             {
1494               viewable = FALSE;
1495               break;
1496             }
1497           tmp = tmp->parent;
1498         }
1499       
1500       if (viewable)
1501         xgrab_shell = parent;
1502       
1503       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
1504     }
1505
1506   /* We want to receive events generated when we map the menu; unfortunately,
1507    * since there is probably already an implicit grab in place from the
1508    * button that the user used to pop up the menu, we won't receive then --
1509    * in particular, the EnterNotify when the menu pops up under the pointer.
1510    *
1511    * If we are grabbing on a parent menu shell, no problem; just grab on
1512    * that menu shell first before popping up the window with owner_events = TRUE.
1513    *
1514    * When grabbing on the menu itself, things get more convuluted - we
1515    * we do an explicit grab on a specially created window with
1516    * owner_events = TRUE, which we override further down with a grab
1517    * on the menu. (We can't grab on the menu until it is mapped; we
1518    * probably could just leave the grab on the other window, with a
1519    * little reorganization of the code in gtkmenu*).
1520    */
1521   grab_keyboard = gtk_menu_shell_get_take_focus (menu_shell);
1522   gtk_window_set_accept_focus (GTK_WINDOW (menu->toplevel), grab_keyboard);
1523
1524   if (!grab_keyboard)
1525     keyboard = NULL;
1526
1527   if (xgrab_shell && xgrab_shell != widget)
1528     {
1529       if (popup_grab_on_window (xgrab_shell->window, keyboard, pointer, activate_time))
1530         {
1531           _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (xgrab_shell), pointer);
1532           GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
1533         }
1534     }
1535   else
1536     {
1537       GdkWindow *transfer_window;
1538
1539       xgrab_shell = widget;
1540       transfer_window = menu_grab_transfer_window_get (menu);
1541       if (popup_grab_on_window (transfer_window, keyboard, pointer, activate_time))
1542         {
1543           _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (xgrab_shell), pointer);
1544           GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
1545         }
1546     }
1547
1548   if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab)
1549     {
1550       /* We failed to make our pointer/keyboard grab. Rather than leaving the user
1551        * with a stuck up window, we just abort here. Presumably the user will
1552        * try again.
1553        */
1554       menu_shell->parent_menu_shell = NULL;
1555       menu_grab_transfer_window_destroy (menu);
1556       return;
1557     }
1558
1559   _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (menu), pointer);
1560   menu_shell->active = TRUE;
1561   menu_shell->button = button;
1562
1563   /* If we are popping up the menu from something other than, a button
1564    * press then, as a heuristic, we ignore enter events for the menu
1565    * until we get a MOTION_NOTIFY.  
1566    */
1567
1568   current_event = gtk_get_current_event ();
1569   if (current_event)
1570     {
1571       if ((current_event->type != GDK_BUTTON_PRESS) &&
1572           (current_event->type != GDK_ENTER_NOTIFY))
1573         menu_shell->ignore_enter = TRUE;
1574
1575       gdk_event_free (current_event);
1576     }
1577   else
1578     menu_shell->ignore_enter = TRUE;
1579
1580   if (menu->torn_off)
1581     {
1582       gtk_menu_tearoff_bg_copy (menu);
1583
1584       gtk_menu_reparent (menu, menu->toplevel, FALSE);
1585     }
1586
1587   parent_toplevel = NULL;
1588   if (parent_menu_shell) 
1589     parent_toplevel = gtk_widget_get_toplevel (parent_menu_shell);
1590   else if (!g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
1591     {
1592       GtkWidget *attach_widget = gtk_menu_get_attach_widget (menu);
1593       if (attach_widget)
1594         parent_toplevel = gtk_widget_get_toplevel (attach_widget);
1595     }
1596
1597   /* Set transient for to get the right window group and parent relationship */
1598   if (GTK_IS_WINDOW (parent_toplevel))
1599     gtk_window_set_transient_for (GTK_WINDOW (menu->toplevel),
1600                                   GTK_WINDOW (parent_toplevel));
1601   
1602   menu->parent_menu_item = parent_menu_item;
1603   menu->position_func = func;
1604   menu->position_func_data = data;
1605   menu_shell->activate_time = activate_time;
1606
1607   /* We need to show the menu here rather in the init function because
1608    * code expects to be able to tell if the menu is onscreen by
1609    * looking at the gtk_widget_get_visible (menu)
1610    */
1611   gtk_widget_show (GTK_WIDGET (menu));
1612
1613   /* Position the menu, possibly changing the size request
1614    */
1615   gtk_menu_position (menu);
1616
1617   /* Compute the size of the toplevel and realize it so we
1618    * can scroll correctly.
1619    */
1620   {
1621     GtkRequisition tmp_request;
1622     GtkAllocation tmp_allocation = { 0, };
1623
1624     gtk_widget_size_request (menu->toplevel, &tmp_request);
1625     
1626     tmp_allocation.width = tmp_request.width;
1627     tmp_allocation.height = tmp_request.height;
1628
1629     gtk_widget_size_allocate (menu->toplevel, &tmp_allocation);
1630     
1631     gtk_widget_realize (GTK_WIDGET (menu));
1632   }
1633
1634   gtk_menu_scroll_to (menu, menu->scroll_offset);
1635
1636   /* if no item is selected, select the first one */
1637   if (!menu_shell->active_menu_item)
1638     {
1639       gboolean touchscreen_mode;
1640
1641       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
1642                     "gtk-touchscreen-mode", &touchscreen_mode,
1643                     NULL);
1644
1645       if (touchscreen_mode)
1646         gtk_menu_shell_select_first (menu_shell, TRUE);
1647     }
1648
1649   /* Once everything is set up correctly, map the toplevel window on
1650      the screen.
1651    */
1652   gtk_widget_show (menu->toplevel);
1653
1654   if (xgrab_shell == widget)
1655     popup_grab_on_window (widget->window, keyboard, pointer, activate_time); /* Should always succeed */
1656
1657   gtk_device_grab_add (GTK_WIDGET (menu), pointer, TRUE);
1658
1659   if (parent_menu_shell)
1660     {
1661       gboolean keyboard_mode;
1662
1663       keyboard_mode = _gtk_menu_shell_get_keyboard_mode (GTK_MENU_SHELL (parent_menu_shell));
1664       _gtk_menu_shell_set_keyboard_mode (menu_shell, keyboard_mode);
1665     }
1666   else if (menu_shell->button == 0) /* a keynav-activated context menu */
1667     _gtk_menu_shell_set_keyboard_mode (menu_shell, TRUE);
1668
1669   _gtk_menu_shell_update_mnemonics (menu_shell);
1670 }
1671
1672 /**
1673  * gtk_menu_popup:
1674  * @menu: a #GtkMenu.
1675  * @parent_menu_shell: (allow-none): the menu shell containing the triggering menu item, or %NULL
1676  * @parent_menu_item: (allow-none): the menu item whose activation triggered the popup, or %NULL
1677  * @func: (allow-none): a user supplied function used to position the menu, or %NULL
1678  * @data: (allow-none): user supplied data to be passed to @func.
1679  * @button: the mouse button which was pressed to initiate the event.
1680  * @activate_time: the time at which the activation event occurred.
1681  *
1682  * Displays a menu and makes it available for selection.  Applications can use
1683  * this function to display context-sensitive menus, and will typically supply
1684  * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data 
1685  * parameters. The default menu positioning function will position the menu
1686  * at the current mouse cursor position.
1687  *
1688  * The @button parameter should be the mouse button pressed to initiate
1689  * the menu popup. If the menu popup was initiated by something other than
1690  * a mouse button press, such as a mouse button release or a keypress,
1691  * @button should be 0.
1692  *
1693  * The @activate_time parameter is used to conflict-resolve initiation of
1694  * concurrent requests for mouse/keyboard grab requests. To function
1695  * properly, this needs to be the time stamp of the user event (such as
1696  * a mouse click or key press) that caused the initiation of the popup.
1697  * Only if no such event is available, gtk_get_current_event_time() can
1698  * be used instead.
1699  */
1700 void
1701 gtk_menu_popup (GtkMenu             *menu,
1702                 GtkWidget           *parent_menu_shell,
1703                 GtkWidget           *parent_menu_item,
1704                 GtkMenuPositionFunc  func,
1705                 gpointer             data,
1706                 guint                button,
1707                 guint32              activate_time)
1708 {
1709   GdkDevice *device;
1710
1711   g_return_if_fail (GTK_IS_MENU (menu));
1712
1713   device = gtk_get_current_event_device ();
1714
1715   if (!device)
1716     {
1717       GdkDisplay *display;
1718       GdkDeviceManager *device_manager;
1719       GList *devices;
1720
1721       display = gtk_widget_get_display (GTK_WIDGET (menu));
1722       device_manager = gdk_display_get_device_manager (display);
1723       devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
1724
1725       device = devices->data;
1726
1727       g_list_free (devices);
1728     }
1729
1730   gtk_menu_popup_for_device (menu, device,
1731                              parent_menu_shell,
1732                              parent_menu_item,
1733                              func, data,
1734                              button, activate_time);
1735 }
1736
1737 void
1738 gtk_menu_popdown (GtkMenu *menu)
1739 {
1740   GtkMenuPrivate *private;
1741   GtkMenuShell *menu_shell;
1742   GdkDevice *pointer;
1743
1744   g_return_if_fail (GTK_IS_MENU (menu));
1745   
1746   menu_shell = GTK_MENU_SHELL (menu);
1747   private = gtk_menu_get_private (menu);
1748
1749   menu_shell->parent_menu_shell = NULL;
1750   menu_shell->active = FALSE;
1751   menu_shell->ignore_enter = FALSE;
1752
1753   private->have_position = FALSE;
1754
1755   gtk_menu_stop_scrolling (menu);
1756   
1757   gtk_menu_stop_navigating_submenu (menu);
1758   
1759   if (menu_shell->active_menu_item)
1760     {
1761       if (menu->old_active_menu_item)
1762         g_object_unref (menu->old_active_menu_item);
1763       menu->old_active_menu_item = menu_shell->active_menu_item;
1764       g_object_ref (menu->old_active_menu_item);
1765     }
1766
1767   gtk_menu_shell_deselect (menu_shell);
1768   
1769   /* The X Grab, if present, will automatically be removed when we hide
1770    * the window */
1771   gtk_widget_hide (menu->toplevel);
1772   gtk_window_set_transient_for (GTK_WINDOW (menu->toplevel), NULL);
1773
1774   pointer = _gtk_menu_shell_get_grab_device (menu_shell);
1775
1776   if (menu->torn_off)
1777     {
1778       gtk_widget_set_size_request (menu->tearoff_window, -1, -1);
1779       
1780       if (GTK_BIN (menu->toplevel)->child) 
1781         {
1782           gtk_menu_reparent (menu, menu->tearoff_hbox, TRUE);
1783         } 
1784       else
1785         {
1786           /* We popped up the menu from the tearoff, so we need to
1787            * release the grab - we aren't actually hiding the menu.
1788            */
1789           if (menu_shell->have_xgrab && pointer)
1790             {
1791               GdkDevice *keyboard;
1792
1793               gdk_device_ungrab (pointer, GDK_CURRENT_TIME);
1794               keyboard = gdk_device_get_associated_device (pointer);
1795
1796               if (keyboard)
1797                 gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
1798             }
1799         }
1800
1801       /* gtk_menu_popdown is called each time a menu item is selected from
1802        * a torn off menu. Only scroll back to the saved position if the
1803        * non-tearoff menu was popped down.
1804        */
1805       if (!menu->tearoff_active)
1806         gtk_menu_scroll_to (menu, menu->saved_scroll_offset);
1807       menu->tearoff_active = TRUE;
1808     }
1809   else
1810     gtk_widget_hide (GTK_WIDGET (menu));
1811
1812   menu_shell->have_xgrab = FALSE;
1813
1814   if (pointer)
1815     gtk_device_grab_remove (GTK_WIDGET (menu), pointer);
1816
1817   _gtk_menu_shell_set_grab_device (menu_shell, NULL);
1818
1819   menu_grab_transfer_window_destroy (menu);
1820 }
1821
1822 GtkWidget*
1823 gtk_menu_get_active (GtkMenu *menu)
1824 {
1825   GtkWidget *child;
1826   GList *children;
1827   
1828   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1829   
1830   if (!menu->old_active_menu_item)
1831     {
1832       child = NULL;
1833       children = GTK_MENU_SHELL (menu)->children;
1834       
1835       while (children)
1836         {
1837           child = children->data;
1838           children = children->next;
1839           
1840           if (GTK_BIN (child)->child)
1841             break;
1842           child = NULL;
1843         }
1844       
1845       menu->old_active_menu_item = child;
1846       if (menu->old_active_menu_item)
1847         g_object_ref (menu->old_active_menu_item);
1848     }
1849   
1850   return menu->old_active_menu_item;
1851 }
1852
1853 void
1854 gtk_menu_set_active (GtkMenu *menu,
1855                      guint    index)
1856 {
1857   GtkWidget *child;
1858   GList *tmp_list;
1859   
1860   g_return_if_fail (GTK_IS_MENU (menu));
1861   
1862   tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
1863   if (tmp_list)
1864     {
1865       child = tmp_list->data;
1866       if (GTK_BIN (child)->child)
1867         {
1868           if (menu->old_active_menu_item)
1869             g_object_unref (menu->old_active_menu_item);
1870           menu->old_active_menu_item = child;
1871           g_object_ref (menu->old_active_menu_item);
1872         }
1873     }
1874 }
1875
1876
1877 /**
1878  * gtk_menu_set_accel_group:
1879  * @accel_group: (allow-none):
1880  */
1881 void
1882 gtk_menu_set_accel_group (GtkMenu       *menu,
1883                           GtkAccelGroup *accel_group)
1884 {
1885   g_return_if_fail (GTK_IS_MENU (menu));
1886   
1887   if (menu->accel_group != accel_group)
1888     {
1889       if (menu->accel_group)
1890         g_object_unref (menu->accel_group);
1891       menu->accel_group = accel_group;
1892       if (menu->accel_group)
1893         g_object_ref (menu->accel_group);
1894       _gtk_menu_refresh_accel_paths (menu, TRUE);
1895     }
1896 }
1897
1898 GtkAccelGroup*
1899 gtk_menu_get_accel_group (GtkMenu *menu)
1900 {
1901   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1902
1903   return menu->accel_group;
1904 }
1905
1906 static gboolean
1907 gtk_menu_real_can_activate_accel (GtkWidget *widget,
1908                                   guint      signal_id)
1909 {
1910   /* Menu items chain here to figure whether they can activate their
1911    * accelerators.  Unlike ordinary widgets, menus allow accel
1912    * activation even if invisible since that's the usual case for
1913    * submenus/popup-menus. however, the state of the attach widget
1914    * affects the "activeness" of the menu.
1915    */
1916   GtkWidget *awidget = gtk_menu_get_attach_widget (GTK_MENU (widget));
1917
1918   if (awidget)
1919     return gtk_widget_can_activate_accel (awidget, signal_id);
1920   else
1921     return gtk_widget_is_sensitive (widget);
1922 }
1923
1924 /**
1925  * gtk_menu_set_accel_path
1926  * @menu:       a valid #GtkMenu
1927  * @accel_path: (allow-none): a valid accelerator path
1928  *
1929  * Sets an accelerator path for this menu from which accelerator paths
1930  * for its immediate children, its menu items, can be constructed.
1931  * The main purpose of this function is to spare the programmer the
1932  * inconvenience of having to call gtk_menu_item_set_accel_path() on
1933  * each menu item that should support runtime user changable accelerators.
1934  * Instead, by just calling gtk_menu_set_accel_path() on their parent,
1935  * each menu item of this menu, that contains a label describing its purpose,
1936  * automatically gets an accel path assigned. For example, a menu containing
1937  * menu items "New" and "Exit", will, after 
1938  * <literal>gtk_menu_set_accel_path (menu, "&lt;Gnumeric-Sheet&gt;/File");</literal>
1939  * has been called, assign its items the accel paths:
1940  * <literal>"&lt;Gnumeric-Sheet&gt;/File/New"</literal> and <literal>"&lt;Gnumeric-Sheet&gt;/File/Exit"</literal>.
1941  * Assigning accel paths to menu items then enables the user to change
1942  * their accelerators at runtime. More details about accelerator paths
1943  * and their default setups can be found at gtk_accel_map_add_entry().
1944  * 
1945  * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
1946  * pass a static string, you can save some memory by interning it first with 
1947  * g_intern_static_string().
1948  */
1949 void
1950 gtk_menu_set_accel_path (GtkMenu     *menu,
1951                          const gchar *accel_path)
1952 {
1953   g_return_if_fail (GTK_IS_MENU (menu));
1954   if (accel_path)
1955     g_return_if_fail (accel_path[0] == '<' && strchr (accel_path, '/')); /* simplistic check */
1956
1957   /* FIXME: accel_path should be defined as const gchar* */
1958   menu->accel_path = (gchar*)g_intern_string (accel_path);
1959   if (menu->accel_path)
1960     _gtk_menu_refresh_accel_paths (menu, FALSE);
1961 }
1962
1963 /**
1964  * gtk_menu_get_accel_path
1965  * @menu: a valid #GtkMenu
1966  *
1967  * Retrieves the accelerator path set on the menu.
1968  *
1969  * Returns: the accelerator path set on the menu.
1970  *
1971  * Since: 2.14
1972  */
1973 const gchar*
1974 gtk_menu_get_accel_path (GtkMenu *menu)
1975 {
1976   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1977
1978   return menu->accel_path;
1979 }
1980
1981 typedef struct {
1982   GtkMenu *menu;
1983   gboolean group_changed;
1984 } AccelPropagation;
1985
1986 static void
1987 refresh_accel_paths_foreach (GtkWidget *widget,
1988                              gpointer   data)
1989 {
1990   AccelPropagation *prop = data;
1991
1992   if (GTK_IS_MENU_ITEM (widget))        /* should always be true */
1993     _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
1994                                        prop->menu->accel_path,
1995                                        prop->menu->accel_group,
1996                                        prop->group_changed);
1997 }
1998
1999 static void
2000 _gtk_menu_refresh_accel_paths (GtkMenu  *menu,
2001                                gboolean  group_changed)
2002 {
2003   g_return_if_fail (GTK_IS_MENU (menu));
2004
2005   if (menu->accel_path && menu->accel_group)
2006     {
2007       AccelPropagation prop;
2008
2009       prop.menu = menu;
2010       prop.group_changed = group_changed;
2011       gtk_container_foreach (GTK_CONTAINER (menu),
2012                              refresh_accel_paths_foreach,
2013                              &prop);
2014     }
2015 }
2016
2017 void
2018 gtk_menu_reposition (GtkMenu *menu)
2019 {
2020   g_return_if_fail (GTK_IS_MENU (menu));
2021
2022   if (!menu->torn_off && gtk_widget_is_drawable (GTK_WIDGET (menu)))
2023     gtk_menu_position (menu);
2024 }
2025
2026 static void
2027 gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
2028                             GtkMenu       *menu)
2029 {
2030   g_return_if_fail (GTK_IS_MENU (menu));
2031
2032   if (adjustment->value != menu->scroll_offset)
2033     gtk_menu_scroll_to (menu, adjustment->value);
2034 }
2035
2036 static void
2037 gtk_menu_set_tearoff_hints (GtkMenu *menu,
2038                             gint     width)
2039 {
2040   GdkGeometry geometry_hints;
2041   
2042   if (!menu->tearoff_window)
2043     return;
2044
2045   if (gtk_widget_get_visible (menu->tearoff_scrollbar))
2046     {
2047       gtk_widget_size_request (menu->tearoff_scrollbar, NULL);
2048       width += menu->tearoff_scrollbar->requisition.width;
2049     }
2050
2051   geometry_hints.min_width = width;
2052   geometry_hints.max_width = width;
2053     
2054   geometry_hints.min_height = 0;
2055   geometry_hints.max_height = GTK_WIDGET (menu)->requisition.height;
2056
2057   gtk_window_set_geometry_hints (GTK_WINDOW (menu->tearoff_window),
2058                                  NULL,
2059                                  &geometry_hints,
2060                                  GDK_HINT_MAX_SIZE|GDK_HINT_MIN_SIZE);
2061 }
2062
2063 static void
2064 gtk_menu_update_title (GtkMenu *menu)
2065 {
2066   if (menu->tearoff_window)
2067     {
2068       const gchar *title;
2069       GtkWidget *attach_widget;
2070
2071       title = gtk_menu_get_title (menu);
2072       if (!title)
2073         {
2074           attach_widget = gtk_menu_get_attach_widget (menu);
2075           if (GTK_IS_MENU_ITEM (attach_widget))
2076             {
2077               GtkWidget *child = GTK_BIN (attach_widget)->child;
2078               if (GTK_IS_LABEL (child))
2079                 title = gtk_label_get_text (GTK_LABEL (child));
2080             }
2081         }
2082       
2083       if (title)
2084         gtk_window_set_title (GTK_WINDOW (menu->tearoff_window), title);
2085     }
2086 }
2087
2088 static GtkWidget*
2089 gtk_menu_get_toplevel (GtkWidget *menu)
2090 {
2091   GtkWidget *attach, *toplevel;
2092
2093   attach = gtk_menu_get_attach_widget (GTK_MENU (menu));
2094
2095   if (GTK_IS_MENU_ITEM (attach))
2096     attach = attach->parent;
2097
2098   if (GTK_IS_MENU (attach))
2099     return gtk_menu_get_toplevel (attach);
2100   else if (GTK_IS_WIDGET (attach))
2101     {
2102       toplevel = gtk_widget_get_toplevel (attach);
2103       if (gtk_widget_is_toplevel (toplevel)) 
2104         return toplevel;
2105     }
2106
2107   return NULL;
2108 }
2109
2110 static void
2111 tearoff_window_destroyed (GtkWidget *widget,
2112                           GtkMenu   *menu)
2113 {
2114   gtk_menu_set_tearoff_state (menu, FALSE);
2115 }
2116
2117 void       
2118 gtk_menu_set_tearoff_state (GtkMenu  *menu,
2119                             gboolean  torn_off)
2120 {
2121   gint width, height;
2122   
2123   g_return_if_fail (GTK_IS_MENU (menu));
2124
2125   if (menu->torn_off != torn_off)
2126     {
2127       menu->torn_off = torn_off;
2128       menu->tearoff_active = torn_off;
2129       
2130       if (menu->torn_off)
2131         {
2132           if (gtk_widget_get_visible (GTK_WIDGET (menu)))
2133             gtk_menu_popdown (menu);
2134
2135           if (!menu->tearoff_window)
2136             {
2137               GtkWidget *toplevel;
2138
2139               menu->tearoff_window = g_object_new (GTK_TYPE_WINDOW,
2140                                                      "type", GTK_WINDOW_TOPLEVEL,
2141                                                      "screen", gtk_widget_get_screen (menu->toplevel),
2142                                                      "app-paintable", TRUE,
2143                                                      NULL);
2144
2145               gtk_window_set_type_hint (GTK_WINDOW (menu->tearoff_window),
2146                                         GDK_WINDOW_TYPE_HINT_MENU);
2147               gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->tearoff_window), 0);
2148               g_signal_connect (menu->tearoff_window, "destroy",
2149                                 G_CALLBACK (tearoff_window_destroyed), menu);
2150               g_signal_connect (menu->tearoff_window, "event",
2151                                 G_CALLBACK (gtk_menu_window_event), menu);
2152
2153               gtk_menu_update_title (menu);
2154
2155               gtk_widget_realize (menu->tearoff_window);
2156
2157               toplevel = gtk_menu_get_toplevel (GTK_WIDGET (menu));
2158               if (toplevel != NULL)
2159                 gtk_window_set_transient_for (GTK_WINDOW (menu->tearoff_window),
2160                                               GTK_WINDOW (toplevel));
2161               
2162               menu->tearoff_hbox = gtk_hbox_new (FALSE, FALSE);
2163               gtk_container_add (GTK_CONTAINER (menu->tearoff_window), menu->tearoff_hbox);
2164
2165               gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
2166               menu->tearoff_adjustment =
2167                 GTK_ADJUSTMENT (gtk_adjustment_new (0,
2168                                                     0,
2169                                                     GTK_WIDGET (menu)->requisition.height,
2170                                                     MENU_SCROLL_STEP2,
2171                                                     height/2,
2172                                                     height));
2173               g_object_connect (menu->tearoff_adjustment,
2174                                 "signal::value-changed", gtk_menu_scrollbar_changed, menu,
2175                                 NULL);
2176               menu->tearoff_scrollbar = gtk_vscrollbar_new (menu->tearoff_adjustment);
2177
2178               gtk_box_pack_end (GTK_BOX (menu->tearoff_hbox),
2179                                 menu->tearoff_scrollbar,
2180                                 FALSE, FALSE, 0);
2181               
2182               if (menu->tearoff_adjustment->upper > height)
2183                 gtk_widget_show (menu->tearoff_scrollbar);
2184               
2185               gtk_widget_show (menu->tearoff_hbox);
2186             }
2187           
2188           gtk_menu_reparent (menu, menu->tearoff_hbox, FALSE);
2189
2190           gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, NULL);
2191
2192           /* Update menu->requisition
2193            */
2194           gtk_widget_size_request (GTK_WIDGET (menu), NULL);
2195   
2196           gtk_menu_set_tearoff_hints (menu, width);
2197             
2198           gtk_widget_realize (menu->tearoff_window);
2199           gtk_menu_position (menu);
2200           
2201           gtk_widget_show (GTK_WIDGET (menu));
2202           gtk_widget_show (menu->tearoff_window);
2203
2204           gtk_menu_scroll_to (menu, 0);
2205
2206         }
2207       else
2208         {
2209           gtk_widget_hide (GTK_WIDGET (menu));
2210           gtk_widget_hide (menu->tearoff_window);
2211           if (GTK_IS_CONTAINER (menu->toplevel))
2212             gtk_menu_reparent (menu, menu->toplevel, FALSE);
2213           gtk_widget_destroy (menu->tearoff_window);
2214           
2215           menu->tearoff_window = NULL;
2216           menu->tearoff_hbox = NULL;
2217           menu->tearoff_scrollbar = NULL;
2218           menu->tearoff_adjustment = NULL;
2219         }
2220
2221       g_object_notify (G_OBJECT (menu), "tearoff-state");
2222     }
2223 }
2224
2225 /**
2226  * gtk_menu_get_tearoff_state:
2227  * @menu: a #GtkMenu
2228  *
2229  * Returns whether the menu is torn off. See
2230  * gtk_menu_set_tearoff_state ().
2231  *
2232  * Return value: %TRUE if the menu is currently torn off.
2233  **/
2234 gboolean
2235 gtk_menu_get_tearoff_state (GtkMenu *menu)
2236 {
2237   g_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
2238
2239   return menu->torn_off;
2240 }
2241
2242 /**
2243  * gtk_menu_set_title:
2244  * @menu: a #GtkMenu
2245  * @title: a string containing the title for the menu.
2246  * 
2247  * Sets the title string for the menu.  The title is displayed when the menu
2248  * is shown as a tearoff menu.  If @title is %NULL, the menu will see if it is
2249  * attached to a parent menu item, and if so it will try to use the same text as
2250  * that menu item's label.
2251  **/
2252 void
2253 gtk_menu_set_title (GtkMenu     *menu,
2254                     const gchar *title)
2255 {
2256   GtkMenuPrivate *priv;
2257   char *old_title;
2258
2259   g_return_if_fail (GTK_IS_MENU (menu));
2260
2261   priv = gtk_menu_get_private (menu);
2262
2263   old_title = priv->title;
2264   priv->title = g_strdup (title);
2265   g_free (old_title);
2266        
2267   gtk_menu_update_title (menu);
2268   g_object_notify (G_OBJECT (menu), "tearoff-title");
2269 }
2270
2271 /**
2272  * gtk_menu_get_title:
2273  * @menu: a #GtkMenu
2274  *
2275  * Returns the title of the menu. See gtk_menu_set_title().
2276  *
2277  * Return value: the title of the menu, or %NULL if the menu has no
2278  * title set on it. This string is owned by the widget and should
2279  * not be modified or freed.
2280  **/
2281 G_CONST_RETURN gchar *
2282 gtk_menu_get_title (GtkMenu *menu)
2283 {
2284   GtkMenuPrivate *priv;
2285
2286   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
2287
2288   priv = gtk_menu_get_private (menu);
2289
2290   return priv->title;
2291 }
2292
2293 void
2294 gtk_menu_reorder_child (GtkMenu   *menu,
2295                         GtkWidget *child,
2296                         gint       position)
2297 {
2298   GtkMenuShell *menu_shell;
2299
2300   g_return_if_fail (GTK_IS_MENU (menu));
2301   g_return_if_fail (GTK_IS_MENU_ITEM (child));
2302
2303   menu_shell = GTK_MENU_SHELL (menu);
2304
2305   if (g_list_find (menu_shell->children, child))
2306     {   
2307       menu_shell->children = g_list_remove (menu_shell->children, child);
2308       menu_shell->children = g_list_insert (menu_shell->children, child, position);
2309
2310       menu_queue_resize (menu);
2311     }   
2312 }
2313
2314 static void
2315 gtk_menu_style_set (GtkWidget *widget,
2316                     GtkStyle  *previous_style)
2317 {
2318   if (gtk_widget_get_realized (widget))
2319     {
2320       GtkMenu *menu = GTK_MENU (widget);
2321       
2322       gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
2323       gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
2324       gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
2325     }
2326 }
2327
2328 static void
2329 get_arrows_border (GtkMenu   *menu,
2330                    GtkBorder *border)
2331 {
2332   guint scroll_arrow_height;
2333   GtkArrowPlacement arrow_placement;
2334
2335   gtk_widget_style_get (GTK_WIDGET (menu),
2336                         "scroll-arrow-vlength", &scroll_arrow_height,
2337                         "arrow_placement", &arrow_placement,
2338                         NULL);
2339
2340   switch (arrow_placement)
2341     {
2342     case GTK_ARROWS_BOTH:
2343       border->top = menu->upper_arrow_visible ? scroll_arrow_height : 0;
2344       border->bottom = menu->lower_arrow_visible ? scroll_arrow_height : 0;
2345       break;
2346
2347     case GTK_ARROWS_START:
2348       border->top = (menu->upper_arrow_visible ||
2349                      menu->lower_arrow_visible) ? scroll_arrow_height : 0;
2350       border->bottom = 0;
2351       break;
2352
2353     case GTK_ARROWS_END:
2354       border->top = 0;
2355       border->bottom = (menu->upper_arrow_visible ||
2356                         menu->lower_arrow_visible) ? scroll_arrow_height : 0;
2357       break;
2358     }
2359
2360   border->left = border->right = 0;
2361 }
2362
2363 static void
2364 gtk_menu_realize (GtkWidget *widget)
2365 {
2366   GdkWindowAttr attributes;
2367   gint attributes_mask;
2368   gint border_width;
2369   GtkMenu *menu;
2370   GtkWidget *child;
2371   GList *children;
2372   guint vertical_padding;
2373   guint horizontal_padding;
2374   GtkBorder arrow_border;
2375
2376   g_return_if_fail (GTK_IS_MENU (widget));
2377
2378   menu = GTK_MENU (widget);
2379   
2380   gtk_widget_set_realized (widget, TRUE);
2381   
2382   attributes.window_type = GDK_WINDOW_CHILD;
2383   attributes.x = widget->allocation.x;
2384   attributes.y = widget->allocation.y;
2385   attributes.width = widget->allocation.width;
2386   attributes.height = widget->allocation.height;
2387   attributes.wclass = GDK_INPUT_OUTPUT;
2388   attributes.visual = gtk_widget_get_visual (widget);
2389   attributes.colormap = gtk_widget_get_colormap (widget);
2390   
2391   attributes.event_mask = gtk_widget_get_events (widget);
2392
2393   attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
2394                             GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
2395   
2396   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
2397   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
2398   gdk_window_set_user_data (widget->window, widget);
2399   
2400   border_width = GTK_CONTAINER (widget)->border_width;
2401
2402   gtk_widget_style_get (GTK_WIDGET (menu),
2403                         "vertical-padding", &vertical_padding,
2404                         "horizontal-padding", &horizontal_padding,
2405                         NULL);
2406
2407   attributes.x = border_width + widget->style->xthickness + horizontal_padding;
2408   attributes.y = border_width + widget->style->ythickness + vertical_padding;
2409   attributes.width = MAX (1, widget->allocation.width - attributes.x * 2);
2410   attributes.height = MAX (1, widget->allocation.height - attributes.y * 2);
2411
2412   get_arrows_border (menu, &arrow_border);
2413   attributes.y += arrow_border.top;
2414   attributes.height -= arrow_border.top;
2415   attributes.height -= arrow_border.bottom;
2416
2417   menu->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2418   gdk_window_set_user_data (menu->view_window, menu);
2419
2420   attributes.x = 0;
2421   attributes.y = 0;
2422   attributes.width = MAX (1, widget->allocation.width - (border_width + widget->style->xthickness + horizontal_padding) * 2);
2423   attributes.height = MAX (1, widget->requisition.height - (border_width + widget->style->ythickness + vertical_padding) * 2);
2424   
2425   menu->bin_window = gdk_window_new (menu->view_window, &attributes, attributes_mask);
2426   gdk_window_set_user_data (menu->bin_window, menu);
2427
2428   children = GTK_MENU_SHELL (menu)->children;
2429   while (children)
2430     {
2431       child = children->data;
2432       children = children->next;
2433           
2434       gtk_widget_set_parent_window (child, menu->bin_window);
2435     }
2436   
2437   widget->style = gtk_style_attach (widget->style, widget->window);
2438   gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
2439   gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
2440   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
2441
2442   if (GTK_MENU_SHELL (widget)->active_menu_item)
2443     gtk_menu_scroll_item_visible (GTK_MENU_SHELL (widget),
2444                                   GTK_MENU_SHELL (widget)->active_menu_item);
2445
2446   gdk_window_show (menu->bin_window);
2447   gdk_window_show (menu->view_window);
2448 }
2449
2450 static gboolean 
2451 gtk_menu_focus (GtkWidget       *widget,
2452                 GtkDirectionType direction)
2453 {
2454   /*
2455    * A menu or its menu items cannot have focus
2456    */
2457   return FALSE;
2458 }
2459
2460 /* See notes in gtk_menu_popup() for information about the "grab transfer window"
2461  */
2462 static GdkWindow *
2463 menu_grab_transfer_window_get (GtkMenu *menu)
2464 {
2465   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
2466   if (!window)
2467     {
2468       GdkWindowAttr attributes;
2469       gint attributes_mask;
2470       
2471       attributes.x = -100;
2472       attributes.y = -100;
2473       attributes.width = 10;
2474       attributes.height = 10;
2475       attributes.window_type = GDK_WINDOW_TEMP;
2476       attributes.wclass = GDK_INPUT_ONLY;
2477       attributes.override_redirect = TRUE;
2478       attributes.event_mask = 0;
2479
2480       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
2481       
2482       window = gdk_window_new (gtk_widget_get_root_window (GTK_WIDGET (menu)),
2483                                &attributes, attributes_mask);
2484       gdk_window_set_user_data (window, menu);
2485
2486       gdk_window_show (window);
2487
2488       g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), window);
2489     }
2490
2491   return window;
2492 }
2493
2494 static void
2495 menu_grab_transfer_window_destroy (GtkMenu *menu)
2496 {
2497   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
2498   if (window)
2499     {
2500       gdk_window_set_user_data (window, NULL);
2501       gdk_window_destroy (window);
2502       g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), NULL);
2503     }
2504 }
2505
2506 static void
2507 gtk_menu_unrealize (GtkWidget *widget)
2508 {
2509   GtkMenu *menu = GTK_MENU (widget);
2510
2511   menu_grab_transfer_window_destroy (menu);
2512
2513   gdk_window_set_user_data (menu->view_window, NULL);
2514   gdk_window_destroy (menu->view_window);
2515   menu->view_window = NULL;
2516
2517   gdk_window_set_user_data (menu->bin_window, NULL);
2518   gdk_window_destroy (menu->bin_window);
2519   menu->bin_window = NULL;
2520
2521   GTK_WIDGET_CLASS (gtk_menu_parent_class)->unrealize (widget);
2522 }
2523
2524 static void
2525 gtk_menu_size_request (GtkWidget      *widget,
2526                        GtkRequisition *requisition)
2527 {
2528   gint i;
2529   GtkMenu *menu;
2530   GtkMenuShell *menu_shell;
2531   GtkWidget *child;
2532   GList *children;
2533   guint max_toggle_size;
2534   guint max_accel_width;
2535   guint vertical_padding;
2536   guint horizontal_padding;
2537   GtkRequisition child_requisition;
2538   GtkMenuPrivate *priv;
2539   
2540   g_return_if_fail (GTK_IS_MENU (widget));
2541   g_return_if_fail (requisition != NULL);
2542   
2543   menu = GTK_MENU (widget);
2544   menu_shell = GTK_MENU_SHELL (widget);
2545   priv = gtk_menu_get_private (menu);
2546   
2547   requisition->width = 0;
2548   requisition->height = 0;
2549   
2550   max_toggle_size = 0;
2551   max_accel_width = 0;
2552   
2553   g_free (priv->heights);
2554   priv->heights = g_new0 (guint, gtk_menu_get_n_rows (menu));
2555   priv->heights_length = gtk_menu_get_n_rows (menu);
2556
2557   children = menu_shell->children;
2558   while (children)
2559     {
2560       gint part;
2561       gint toggle_size;
2562       gint l, r, t, b;
2563
2564       child = children->data;
2565       children = children->next;
2566       
2567       if (! gtk_widget_get_visible (child))
2568         continue;
2569
2570       get_effective_child_attach (child, &l, &r, &t, &b);
2571
2572       /* It's important to size_request the child
2573        * before doing the toggle size request, in
2574        * case the toggle size request depends on the size
2575        * request of a child of the child (e.g. for ImageMenuItem)
2576        */
2577
2578        GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
2579        gtk_widget_size_request (child, &child_requisition);
2580
2581        gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
2582        max_toggle_size = MAX (max_toggle_size, toggle_size);
2583        max_accel_width = MAX (max_accel_width,
2584                               GTK_MENU_ITEM (child)->accelerator_width);
2585
2586        part = child_requisition.width / (r - l);
2587        requisition->width = MAX (requisition->width, part);
2588
2589        part = MAX (child_requisition.height, toggle_size) / (b - t);
2590        priv->heights[t] = MAX (priv->heights[t], part);
2591     }
2592
2593   /* If the menu doesn't include any images or check items
2594    * reserve the space so that all menus are consistent.
2595    * We only do this for 'ordinary' menus, not for combobox
2596    * menus or multi-column menus
2597    */
2598   if (max_toggle_size == 0 && 
2599       gtk_menu_get_n_columns (menu) == 1 &&
2600       !priv->no_toggle_size)
2601     {
2602       guint toggle_spacing;
2603       guint indicator_size;
2604
2605       gtk_style_get (widget->style,
2606                      GTK_TYPE_CHECK_MENU_ITEM,
2607                      "toggle-spacing", &toggle_spacing,
2608                      "indicator-size", &indicator_size,
2609                      NULL);
2610
2611       max_toggle_size = indicator_size + toggle_spacing;
2612     }
2613
2614   for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
2615     requisition->height += priv->heights[i];
2616
2617   requisition->width += 2 * max_toggle_size + max_accel_width;
2618   requisition->width *= gtk_menu_get_n_columns (menu);
2619
2620   gtk_widget_style_get (GTK_WIDGET (menu),
2621                         "vertical-padding", &vertical_padding,
2622                         "horizontal-padding", &horizontal_padding,
2623                         NULL);
2624
2625   requisition->width += (GTK_CONTAINER (menu)->border_width + horizontal_padding +
2626                          widget->style->xthickness) * 2;
2627   requisition->height += (GTK_CONTAINER (menu)->border_width + vertical_padding +
2628                           widget->style->ythickness) * 2;
2629   
2630   menu->toggle_size = max_toggle_size;
2631
2632   /* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap).
2633    */
2634   if (menu->tearoff_active)
2635     gtk_menu_set_tearoff_hints (menu, requisition->width);
2636 }
2637
2638 static void
2639 gtk_menu_size_allocate (GtkWidget     *widget,
2640                         GtkAllocation *allocation)
2641 {
2642   GtkMenu *menu;
2643   GtkMenuShell *menu_shell;
2644   GtkWidget *child;
2645   GtkAllocation child_allocation;
2646   GtkRequisition child_requisition;
2647   GtkMenuPrivate *priv;
2648   GList *children;
2649   gint x, y;
2650   gint width, height;
2651   guint vertical_padding;
2652   guint horizontal_padding;
2653   
2654   g_return_if_fail (GTK_IS_MENU (widget));
2655   g_return_if_fail (allocation != NULL);
2656   
2657   menu = GTK_MENU (widget);
2658   menu_shell = GTK_MENU_SHELL (widget);
2659   priv = gtk_menu_get_private (menu);
2660
2661   widget->allocation = *allocation;
2662   gtk_widget_get_child_requisition (GTK_WIDGET (menu), &child_requisition);
2663
2664   gtk_widget_style_get (GTK_WIDGET (menu),
2665                         "vertical-padding", &vertical_padding,
2666                         "horizontal-padding", &horizontal_padding,
2667                         NULL);
2668
2669   x = GTK_CONTAINER (menu)->border_width + widget->style->xthickness + horizontal_padding;
2670   y = GTK_CONTAINER (menu)->border_width + widget->style->ythickness + vertical_padding;
2671
2672   width = MAX (1, allocation->width - x * 2);
2673   height = MAX (1, allocation->height - y * 2);
2674
2675   child_requisition.width -= x * 2;
2676   child_requisition.height -= y * 2;
2677
2678   if (menu_shell->active)
2679     gtk_menu_scroll_to (menu, menu->scroll_offset);
2680
2681   if (!menu->tearoff_active)
2682     {
2683       GtkBorder arrow_border;
2684
2685       get_arrows_border (menu, &arrow_border);
2686       y += arrow_border.top;
2687       height -= arrow_border.top;
2688       height -= arrow_border.bottom;
2689     }
2690
2691   if (gtk_widget_get_realized (widget))
2692     {
2693       gdk_window_move_resize (widget->window,
2694                               allocation->x, allocation->y,
2695                               allocation->width, allocation->height);
2696
2697       gdk_window_move_resize (menu->view_window,
2698                               x,
2699                               y,
2700                               width,
2701                               height);
2702     }
2703
2704   if (menu_shell->children)
2705     {
2706       gint base_width = width / gtk_menu_get_n_columns (menu);
2707
2708       children = menu_shell->children;
2709       while (children)
2710         {
2711           child = children->data;
2712           children = children->next;
2713
2714           if (gtk_widget_get_visible (child))
2715             {
2716               gint i;
2717               gint l, r, t, b;
2718
2719               get_effective_child_attach (child, &l, &r, &t, &b);
2720
2721               if (gtk_widget_get_direction (GTK_WIDGET (menu)) == GTK_TEXT_DIR_RTL)
2722                 {
2723                   guint tmp;
2724                   tmp = gtk_menu_get_n_columns (menu) - l;
2725                   l = gtk_menu_get_n_columns (menu) - r;
2726                   r = tmp;
2727                 }
2728
2729               child_allocation.width = (r - l) * base_width;
2730               child_allocation.height = 0;
2731               child_allocation.x = l * base_width;
2732               child_allocation.y = 0;
2733
2734               for (i = 0; i < b; i++)
2735                 {
2736                   if (i < t)
2737                     child_allocation.y += priv->heights[i];
2738                   else
2739                     child_allocation.height += priv->heights[i];
2740                 }
2741
2742               gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
2743                                                   menu->toggle_size);
2744
2745               gtk_widget_size_allocate (child, &child_allocation);
2746               gtk_widget_queue_draw (child);
2747             }
2748         }
2749       
2750       /* Resize the item window */
2751       if (gtk_widget_get_realized (widget))
2752         {
2753           gint i;
2754           gint width, height;
2755
2756           height = 0;
2757           for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
2758             height += priv->heights[i];
2759
2760           width = gtk_menu_get_n_columns (menu) * base_width;
2761           gdk_window_resize (menu->bin_window, width, height);
2762         }
2763
2764       if (menu->tearoff_active)
2765         {
2766           if (allocation->height >= widget->requisition.height)
2767             {
2768               if (gtk_widget_get_visible (menu->tearoff_scrollbar))
2769                 {
2770                   gtk_widget_hide (menu->tearoff_scrollbar);
2771                   gtk_menu_set_tearoff_hints (menu, allocation->width);
2772
2773                   gtk_menu_scroll_to (menu, 0);
2774                 }
2775             }
2776           else
2777             {
2778               menu->tearoff_adjustment->upper = widget->requisition.height;
2779               menu->tearoff_adjustment->page_size = allocation->height;
2780               
2781               if (menu->tearoff_adjustment->value + menu->tearoff_adjustment->page_size >
2782                   menu->tearoff_adjustment->upper)
2783                 {
2784                   gint value;
2785                   value = menu->tearoff_adjustment->upper - menu->tearoff_adjustment->page_size;
2786                   if (value < 0)
2787                     value = 0;
2788                   gtk_menu_scroll_to (menu, value);
2789                 }
2790               
2791               gtk_adjustment_changed (menu->tearoff_adjustment);
2792               
2793               if (!gtk_widget_get_visible (menu->tearoff_scrollbar))
2794                 {
2795                   gtk_widget_show (menu->tearoff_scrollbar);
2796                   gtk_menu_set_tearoff_hints (menu, allocation->width);
2797                 }
2798             }
2799         }
2800     }
2801 }
2802
2803 static void
2804 get_arrows_visible_area (GtkMenu      *menu,
2805                          GdkRectangle *border,
2806                          GdkRectangle *upper,
2807                          GdkRectangle *lower,
2808                          gint         *arrow_space)
2809 {
2810   GtkWidget *widget = GTK_WIDGET (menu);
2811   guint vertical_padding;
2812   guint horizontal_padding;
2813   gint scroll_arrow_height;
2814   GtkArrowPlacement arrow_placement;
2815
2816   gtk_widget_style_get (widget,
2817                         "vertical-padding", &vertical_padding,
2818                         "horizontal-padding", &horizontal_padding,
2819                         "scroll-arrow-vlength", &scroll_arrow_height,
2820                         "arrow-placement", &arrow_placement,
2821                         NULL);
2822
2823   border->x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness + horizontal_padding;
2824   border->y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness + vertical_padding;
2825   gdk_drawable_get_size (widget->window, &border->width, &border->height);
2826
2827   switch (arrow_placement)
2828     {
2829     case GTK_ARROWS_BOTH:
2830       upper->x = border->x;
2831       upper->y = border->y;
2832       upper->width = border->width - 2 * border->x;
2833       upper->height = scroll_arrow_height;
2834
2835       lower->x = border->x;
2836       lower->y = border->height - border->y - scroll_arrow_height;
2837       lower->width = border->width - 2 * border->x;
2838       lower->height = scroll_arrow_height;
2839       break;
2840
2841     case GTK_ARROWS_START:
2842       upper->x = border->x;
2843       upper->y = border->y;
2844       upper->width = (border->width - 2 * border->x) / 2;
2845       upper->height = scroll_arrow_height;
2846
2847       lower->x = border->x + upper->width;
2848       lower->y = border->y;
2849       lower->width = (border->width - 2 * border->x) / 2;
2850       lower->height = scroll_arrow_height;
2851       break;
2852
2853     case GTK_ARROWS_END:
2854       upper->x = border->x;
2855       upper->y = border->height - border->y - scroll_arrow_height;
2856       upper->width = (border->width - 2 * border->x) / 2;
2857       upper->height = scroll_arrow_height;
2858
2859       lower->x = border->x + upper->width;
2860       lower->y = border->height - border->y - scroll_arrow_height;
2861       lower->width = (border->width - 2 * border->x) / 2;
2862       lower->height = scroll_arrow_height;
2863       break;
2864
2865     default:
2866        g_assert_not_reached();
2867        upper->x = upper->y = upper->width = upper->height = 0;
2868        lower->x = lower->y = lower->width = lower->height = 0;
2869     }
2870
2871   *arrow_space = scroll_arrow_height - 2 * widget->style->ythickness;
2872 }
2873
2874 static void
2875 gtk_menu_paint (GtkWidget      *widget,
2876                 GdkEventExpose *event)
2877 {
2878   GtkMenu *menu;
2879   GtkMenuPrivate *priv;
2880   GdkRectangle border;
2881   GdkRectangle upper;
2882   GdkRectangle lower;
2883   gint arrow_space;
2884   
2885   g_return_if_fail (GTK_IS_MENU (widget));
2886
2887   menu = GTK_MENU (widget);
2888   priv = gtk_menu_get_private (menu);
2889
2890   get_arrows_visible_area (menu, &border, &upper, &lower, &arrow_space);
2891
2892   if (event->window == widget->window)
2893     {
2894       gfloat arrow_scaling;
2895       gint arrow_size;
2896
2897       gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
2898       arrow_size = arrow_scaling * arrow_space;
2899
2900       gtk_paint_box (widget->style,
2901                      widget->window,
2902                      GTK_STATE_NORMAL,
2903                      GTK_SHADOW_OUT,
2904                      &event->area, widget, "menu",
2905                      0, 0, -1, -1);
2906
2907       if (menu->upper_arrow_visible && !menu->tearoff_active)
2908         {
2909           gtk_paint_box (widget->style,
2910                          widget->window,
2911                          priv->upper_arrow_state,
2912                          GTK_SHADOW_OUT,
2913                          &event->area, widget, "menu_scroll_arrow_up",
2914                          upper.x,
2915                          upper.y,
2916                          upper.width,
2917                          upper.height);
2918
2919           gtk_paint_arrow (widget->style,
2920                            widget->window,
2921                            priv->upper_arrow_state,
2922                            GTK_SHADOW_OUT,
2923                            &event->area, widget, "menu_scroll_arrow_up",
2924                            GTK_ARROW_UP,
2925                            TRUE,
2926                            upper.x + (upper.width - arrow_size) / 2,
2927                            upper.y + widget->style->ythickness + (arrow_space - arrow_size) / 2,
2928                            arrow_size, arrow_size);
2929         }
2930
2931       if (menu->lower_arrow_visible && !menu->tearoff_active)
2932         {
2933           gtk_paint_box (widget->style,
2934                          widget->window,
2935                          priv->lower_arrow_state,
2936                          GTK_SHADOW_OUT,
2937                          &event->area, widget, "menu_scroll_arrow_down",
2938                          lower.x,
2939                          lower.y,
2940                          lower.width,
2941                          lower.height);
2942
2943           gtk_paint_arrow (widget->style,
2944                            widget->window,
2945                            priv->lower_arrow_state,
2946                            GTK_SHADOW_OUT,
2947                            &event->area, widget, "menu_scroll_arrow_down",
2948                            GTK_ARROW_DOWN,
2949                            TRUE,
2950                            lower.x + (lower.width - arrow_size) / 2,
2951                            lower.y + widget->style->ythickness + (arrow_space - arrow_size) / 2,
2952                            arrow_size, arrow_size);
2953         }
2954     }
2955   else if (event->window == menu->bin_window)
2956     {
2957       gint y = -border.y + menu->scroll_offset;
2958
2959       if (!menu->tearoff_active)
2960         {
2961           GtkBorder arrow_border;
2962
2963           get_arrows_border (menu, &arrow_border);
2964           y -= arrow_border.top;
2965         }
2966
2967       gtk_paint_box (widget->style,
2968                      menu->bin_window,
2969                      GTK_STATE_NORMAL,
2970                      GTK_SHADOW_OUT,
2971                      &event->area, widget, "menu",
2972                      - border.x, y,
2973                      border.width, border.height);
2974     }
2975 }
2976
2977 static gboolean
2978 gtk_menu_expose (GtkWidget      *widget,
2979                  GdkEventExpose *event)
2980 {
2981   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
2982   g_return_val_if_fail (event != NULL, FALSE);
2983
2984   if (gtk_widget_is_drawable (widget))
2985     {
2986       gtk_menu_paint (widget, event);
2987
2988       GTK_WIDGET_CLASS (gtk_menu_parent_class)->expose_event (widget, event);
2989     }
2990   
2991   return FALSE;
2992 }
2993
2994 static void
2995 gtk_menu_show (GtkWidget *widget)
2996 {
2997   GtkMenu *menu = GTK_MENU (widget);
2998
2999   _gtk_menu_refresh_accel_paths (menu, FALSE);
3000
3001   GTK_WIDGET_CLASS (gtk_menu_parent_class)->show (widget);
3002 }
3003
3004 static gboolean
3005 gtk_menu_button_scroll (GtkMenu        *menu,
3006                         GdkEventButton *event)
3007 {
3008   if (menu->upper_arrow_prelight || menu->lower_arrow_prelight)
3009     {
3010       gboolean touchscreen_mode;
3011
3012       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
3013                     "gtk-touchscreen-mode", &touchscreen_mode,
3014                     NULL);
3015
3016       if (touchscreen_mode)
3017         gtk_menu_handle_scrolling (menu,
3018                                    event->x_root, event->y_root,
3019                                    event->type == GDK_BUTTON_PRESS,
3020                                    FALSE);
3021
3022       return TRUE;
3023     }
3024
3025   return FALSE;
3026 }
3027
3028 static gboolean
3029 pointer_in_menu_window (GtkWidget *widget,
3030                         gdouble    x_root,
3031                         gdouble    y_root)
3032 {
3033   GtkMenu *menu = GTK_MENU (widget);
3034
3035   if (gtk_widget_get_mapped (menu->toplevel))
3036     {
3037       GtkMenuShell *menu_shell;
3038       gint          window_x, window_y;
3039
3040       gdk_window_get_position (menu->toplevel->window, &window_x, &window_y);
3041
3042       if (x_root >= window_x && x_root < window_x + widget->allocation.width &&
3043           y_root >= window_y && y_root < window_y + widget->allocation.height)
3044         return TRUE;
3045
3046       menu_shell = GTK_MENU_SHELL (widget);
3047
3048       if (GTK_IS_MENU (menu_shell->parent_menu_shell))
3049         return pointer_in_menu_window (menu_shell->parent_menu_shell,
3050                                        x_root, y_root);
3051     }
3052
3053   return FALSE;
3054 }
3055
3056 static gboolean
3057 gtk_menu_button_press (GtkWidget      *widget,
3058                        GdkEventButton *event)
3059 {
3060   if (event->type != GDK_BUTTON_PRESS)
3061     return FALSE;
3062
3063   /* Don't pass down to menu shell for presses over scroll arrows
3064    */
3065   if (gtk_menu_button_scroll (GTK_MENU (widget), event))
3066     return TRUE;
3067
3068   /*  Don't pass down to menu shell if a non-menuitem part of the menu
3069    *  was clicked. The check for the event_widget being a GtkMenuShell
3070    *  works because we have the pointer grabbed on menu_shell->window
3071    *  with owner_events=TRUE, so all events that are either outside
3072    *  the menu or on its border are delivered relative to
3073    *  menu_shell->window.
3074    */
3075   if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
3076       pointer_in_menu_window (widget, event->x_root, event->y_root))
3077     return TRUE;
3078
3079   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event);
3080 }
3081
3082 static gboolean
3083 gtk_menu_button_release (GtkWidget      *widget,
3084                          GdkEventButton *event)
3085 {
3086   GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (widget));
3087
3088   if (priv->ignore_button_release)
3089     {
3090       priv->ignore_button_release = FALSE;
3091       return FALSE;
3092     }
3093
3094   if (event->type != GDK_BUTTON_RELEASE)
3095     return FALSE;
3096
3097   /* Don't pass down to menu shell for releases over scroll arrows
3098    */
3099   if (gtk_menu_button_scroll (GTK_MENU (widget), event))
3100     return TRUE;
3101
3102   /*  Don't pass down to menu shell if a non-menuitem part of the menu
3103    *  was clicked (see comment in button_press()).
3104    */
3105   if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
3106       pointer_in_menu_window (widget, event->x_root, event->y_root))
3107     {
3108       /*  Ugly: make sure menu_shell->button gets reset to 0 when we
3109        *  bail out early here so it is in a consistent state for the
3110        *  next button_press/button_release in GtkMenuShell.
3111        *  See bug #449371.
3112        */
3113       if (GTK_MENU_SHELL (widget)->active)
3114         GTK_MENU_SHELL (widget)->button = 0;
3115
3116       return TRUE;
3117     }
3118
3119   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_release_event (widget, event);
3120 }
3121
3122 static const gchar *
3123 get_accel_path (GtkWidget *menu_item,
3124                 gboolean  *locked)
3125 {
3126   const gchar *path;
3127   GtkWidget *label;
3128   GClosure *accel_closure;
3129   GtkAccelGroup *accel_group;    
3130
3131   path = _gtk_widget_get_accel_path (menu_item, locked);
3132   if (!path)
3133     {
3134       path = GTK_MENU_ITEM (menu_item)->accel_path;
3135       
3136       if (locked)
3137         {
3138           *locked = TRUE;
3139
3140           label = GTK_BIN (menu_item)->child;
3141           
3142           if (GTK_IS_ACCEL_LABEL (label))
3143             {
3144               g_object_get (label, 
3145                             "accel-closure", &accel_closure, 
3146                             NULL);
3147               if (accel_closure)
3148                 {
3149                   accel_group = gtk_accel_group_from_accel_closure (accel_closure);
3150                   
3151                   *locked = gtk_accel_group_get_is_locked (accel_group);
3152                 }
3153             }
3154         }
3155     }
3156
3157   return path;
3158 }
3159
3160 static gboolean
3161 gtk_menu_key_press (GtkWidget   *widget,
3162                     GdkEventKey *event)
3163 {
3164   GtkMenuShell *menu_shell;
3165   GtkMenu *menu;
3166   gboolean delete = FALSE;
3167   gboolean can_change_accels;
3168   gchar *accel = NULL;
3169   guint accel_key, accel_mods;
3170   GdkModifierType consumed_modifiers;
3171   GdkDisplay *display;
3172   
3173   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
3174   g_return_val_if_fail (event != NULL, FALSE);
3175       
3176   menu_shell = GTK_MENU_SHELL (widget);
3177   menu = GTK_MENU (widget);
3178   
3179   gtk_menu_stop_navigating_submenu (menu);
3180
3181   if (GTK_WIDGET_CLASS (gtk_menu_parent_class)->key_press_event (widget, event))
3182     return TRUE;
3183
3184   display = gtk_widget_get_display (widget);
3185     
3186   g_object_get (gtk_widget_get_settings (widget),
3187                 "gtk-menu-bar-accel", &accel,
3188                 "gtk-can-change-accels", &can_change_accels,
3189                 NULL);
3190
3191   if (accel && *accel)
3192     {
3193       guint keyval = 0;
3194       GdkModifierType mods = 0;
3195       
3196       gtk_accelerator_parse (accel, &keyval, &mods);
3197
3198       if (keyval == 0)
3199         g_warning ("Failed to parse menu bar accelerator '%s'\n", accel);
3200
3201       /* FIXME this is wrong, needs to be in the global accel resolution
3202        * thing, to properly consider i18n etc., but that probably requires
3203        * AccelGroup changes etc.
3204        */
3205       if (event->keyval == keyval && (mods & event->state) == mods)
3206         {
3207           gtk_menu_shell_cancel (menu_shell);
3208           g_free (accel);
3209           return TRUE;
3210         }
3211     }
3212
3213   g_free (accel);
3214   
3215   switch (event->keyval)
3216     {
3217     case GDK_Delete:
3218     case GDK_KP_Delete:
3219     case GDK_BackSpace:
3220       delete = TRUE;
3221       break;
3222     default:
3223       break;
3224     }
3225
3226   /* Figure out what modifiers went into determining the key symbol */
3227   gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display (display),
3228                                        event->hardware_keycode, event->state, event->group,
3229                                        NULL, NULL, NULL, &consumed_modifiers);
3230
3231   accel_key = gdk_keyval_to_lower (event->keyval);
3232   accel_mods = event->state & gtk_accelerator_get_default_mod_mask () & ~consumed_modifiers;
3233
3234   /* If lowercasing affects the keysym, then we need to include SHIFT in the modifiers,
3235    * We re-upper case when we match against the keyval, but display and save in caseless form.
3236    */
3237   if (accel_key != event->keyval)
3238     accel_mods |= GDK_SHIFT_MASK;
3239   
3240   /* Modify the accelerators */
3241   if (can_change_accels &&
3242       menu_shell->active_menu_item &&
3243       GTK_BIN (menu_shell->active_menu_item)->child &&                  /* no separators */
3244       GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL &&  /* no submenus */
3245       (delete || gtk_accelerator_valid (accel_key, accel_mods)))
3246     {
3247       GtkWidget *menu_item = menu_shell->active_menu_item;
3248       gboolean locked, replace_accels = TRUE;
3249       const gchar *path;
3250
3251       path = get_accel_path (menu_item, &locked);
3252       if (!path || locked)
3253         {
3254           /* can't change accelerators on menu_items without paths
3255            * (basically, those items are accelerator-locked).
3256            */
3257           /* g_print("item has no path or is locked, menu prefix: %s\n", menu->accel_path); */
3258           gtk_widget_error_bell (widget);
3259         }
3260       else
3261         {
3262           gboolean changed;
3263
3264           /* For the keys that act to delete the current setting, we delete
3265            * the current setting if there is one, otherwise, we set the
3266            * key as the accelerator.
3267            */
3268           if (delete)
3269             {
3270               GtkAccelKey key;
3271               
3272               if (gtk_accel_map_lookup_entry (path, &key) &&
3273                   (key.accel_key || key.accel_mods))
3274                 {
3275                   accel_key = 0;
3276                   accel_mods = 0;
3277                 }
3278             }
3279           changed = gtk_accel_map_change_entry (path, accel_key, accel_mods, replace_accels);
3280
3281           if (!changed)
3282             {
3283               /* we failed, probably because this key is in use and
3284                * locked already
3285                */
3286               /* g_print("failed to change\n"); */
3287               gtk_widget_error_bell (widget);
3288             }
3289         }
3290     }
3291   
3292   return TRUE;
3293 }
3294
3295 static gboolean
3296 check_threshold (GtkWidget *widget,
3297                  gint       start_x,
3298                  gint       start_y,
3299                  gint       x,
3300                  gint       y)
3301 {
3302 #define THRESHOLD 8
3303   
3304   return
3305     ABS (start_x - x) > THRESHOLD  ||
3306     ABS (start_y - y) > THRESHOLD;
3307 }
3308
3309 static gboolean
3310 definitely_within_item (GtkWidget *widget,
3311                         gint       x,
3312                         gint       y)
3313 {
3314   GdkWindow *window = GTK_MENU_ITEM (widget)->event_window;
3315   int w, h;
3316
3317   gdk_drawable_get_size (window, &w, &h);
3318   
3319   return
3320     check_threshold (widget, 0, 0, x, y) &&
3321     check_threshold (widget, w - 1, 0, x, y) &&
3322     check_threshold (widget, w - 1, h - 1, x, y) &&
3323     check_threshold (widget, 0, h - 1, x, y);
3324 }
3325
3326 static gboolean
3327 gtk_menu_motion_notify (GtkWidget      *widget,
3328                         GdkEventMotion *event)
3329 {
3330   GtkWidget *menu_item;
3331   GtkMenu *menu;
3332   GtkMenuShell *menu_shell;
3333
3334   gboolean need_enter;
3335
3336   if (GTK_IS_MENU (widget))
3337     {
3338       GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (widget));
3339
3340       if (priv->ignore_button_release)
3341         priv->ignore_button_release = FALSE;
3342
3343       gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root,
3344                                  TRUE, TRUE);
3345     }
3346
3347   /* We received the event for one of two reasons:
3348    *
3349    * a) We are the active menu, and did gtk_grab_add()
3350    * b) The widget is a child of ours, and the event was propagated
3351    *
3352    * Since for computation of navigation regions, we want the menu which
3353    * is the parent of the menu item, for a), we need to find that menu,
3354    * which may be different from 'widget'.
3355    */
3356   menu_item = gtk_get_event_widget ((GdkEvent*) event);
3357   if (!GTK_IS_MENU_ITEM (menu_item) ||
3358       !GTK_IS_MENU (menu_item->parent))
3359     return FALSE;
3360
3361   menu_shell = GTK_MENU_SHELL (menu_item->parent);
3362   menu = GTK_MENU (menu_shell);
3363
3364   if (definitely_within_item (menu_item, event->x, event->y))
3365     menu_shell->activate_time = 0;
3366
3367   need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter);
3368
3369   /* Check to see if we are within an active submenu's navigation region
3370    */
3371   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
3372     return TRUE; 
3373
3374   /* Make sure we pop down if we enter a non-selectable menu item, so we
3375    * don't show a submenu when the cursor is outside the stay-up triangle.
3376    */
3377   if (!_gtk_menu_item_is_selectable (menu_item))
3378     {
3379       /* We really want to deselect, but this gives the menushell code
3380        * a chance to do some bookkeeping about the menuitem.
3381        */
3382       gtk_menu_shell_select_item (menu_shell, menu_item);
3383       return FALSE;
3384     }
3385
3386   if (need_enter)
3387     {
3388       /* The menu is now sensitive to enter events on its items, but
3389        * was previously sensitive.  So we fake an enter event.
3390        */
3391       gint width, height;
3392       
3393       menu_shell->ignore_enter = FALSE; 
3394       
3395       gdk_drawable_get_size (event->window, &width, &height);
3396       if (event->x >= 0 && event->x < width &&
3397           event->y >= 0 && event->y < height)
3398         {
3399           GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
3400           gboolean result;
3401
3402           send_event->crossing.window = g_object_ref (event->window);
3403           send_event->crossing.time = event->time;
3404           send_event->crossing.send_event = TRUE;
3405           send_event->crossing.x_root = event->x_root;
3406           send_event->crossing.y_root = event->y_root;
3407           send_event->crossing.x = event->x;
3408           send_event->crossing.y = event->y;
3409           send_event->crossing.state = event->state;
3410           gdk_event_set_device (send_event, gdk_event_get_device ((GdkEvent *) event));
3411
3412           /* We send the event to 'widget', the currently active menu,
3413            * instead of 'menu', the menu that the pointer is in. This
3414            * will ensure that the event will be ignored unless the
3415            * menuitem is a child of the active menu or some parent
3416            * menu of the active menu.
3417            */
3418           result = gtk_widget_event (widget, send_event);
3419           gdk_event_free (send_event);
3420
3421           return result;
3422         }
3423     }
3424
3425   return FALSE;
3426 }
3427
3428 static gboolean
3429 get_double_arrows (GtkMenu *menu)
3430 {
3431   GtkMenuPrivate   *priv = gtk_menu_get_private (menu);
3432   gboolean          double_arrows;
3433   GtkArrowPlacement arrow_placement;
3434
3435   gtk_widget_style_get (GTK_WIDGET (menu),
3436                         "double-arrows", &double_arrows,
3437                         "arrow-placement", &arrow_placement,
3438                         NULL);
3439
3440   if (arrow_placement != GTK_ARROWS_BOTH)
3441     return TRUE;
3442
3443   return double_arrows || (priv->initially_pushed_in &&
3444                            menu->scroll_offset != 0);
3445 }
3446
3447 static void
3448 gtk_menu_scroll_by (GtkMenu *menu, 
3449                     gint     step)
3450 {
3451   GtkWidget *widget;
3452   gint offset;
3453   gint view_width, view_height;
3454   gboolean double_arrows;
3455   GtkBorder arrow_border;
3456   
3457   widget = GTK_WIDGET (menu);
3458   offset = menu->scroll_offset + step;
3459
3460   get_arrows_border (menu, &arrow_border);
3461
3462   double_arrows = get_double_arrows (menu);
3463
3464   /* If we scroll upward and the non-visible top part
3465    * is smaller than the scroll arrow it would be
3466    * pretty stupid to show the arrow and taking more
3467    * screen space than just scrolling to the top.
3468    */
3469   if (!double_arrows)
3470     if ((step < 0) && (offset < arrow_border.top))
3471       offset = 0;
3472
3473   /* Don't scroll over the top if we weren't before: */
3474   if ((menu->scroll_offset >= 0) && (offset < 0))
3475     offset = 0;
3476
3477   gdk_drawable_get_size (widget->window, &view_width, &view_height);
3478
3479   if (menu->scroll_offset == 0 &&
3480       view_height >= widget->requisition.height)
3481     return;
3482
3483   /* Don't scroll past the bottom if we weren't before: */
3484   if (menu->scroll_offset > 0)
3485     view_height -= arrow_border.top;
3486
3487   /* When both arrows are always shown, reduce
3488    * view height even more.
3489    */
3490   if (double_arrows)
3491     view_height -= arrow_border.bottom;
3492
3493   if ((menu->scroll_offset + view_height <= widget->requisition.height) &&
3494       (offset + view_height > widget->requisition.height))
3495     offset = widget->requisition.height - view_height;
3496
3497   if (offset != menu->scroll_offset)
3498     gtk_menu_scroll_to (menu, offset);
3499 }
3500
3501 static void
3502 gtk_menu_do_timeout_scroll (GtkMenu  *menu,
3503                             gboolean  touchscreen_mode)
3504 {
3505   gboolean upper_visible;
3506   gboolean lower_visible;
3507
3508   upper_visible = menu->upper_arrow_visible;
3509   lower_visible = menu->lower_arrow_visible;
3510
3511   gtk_menu_scroll_by (menu, menu->scroll_step);
3512
3513   if (touchscreen_mode &&
3514       (upper_visible != menu->upper_arrow_visible ||
3515        lower_visible != menu->lower_arrow_visible))
3516     {
3517       /* We are about to hide a scroll arrow while the mouse is pressed,
3518        * this would cause the uncovered menu item to be activated on button
3519        * release. Therefore we need to ignore button release here
3520        */
3521       GTK_MENU_SHELL (menu)->ignore_enter = TRUE;
3522       gtk_menu_get_private (menu)->ignore_button_release = TRUE;
3523     }
3524 }
3525
3526 static gboolean
3527 gtk_menu_scroll_timeout (gpointer data)
3528 {
3529   GtkMenu  *menu;
3530   gboolean  touchscreen_mode;
3531
3532   menu = GTK_MENU (data);
3533
3534   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
3535                 "gtk-touchscreen-mode", &touchscreen_mode,
3536                 NULL);
3537
3538   gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
3539
3540   return TRUE;
3541 }
3542
3543 static gboolean
3544 gtk_menu_scroll_timeout_initial (gpointer data)
3545 {
3546   GtkMenu  *menu;
3547   guint     timeout;
3548   gboolean  touchscreen_mode;
3549
3550   menu = GTK_MENU (data);
3551
3552   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
3553                 "gtk-timeout-repeat", &timeout,
3554                 "gtk-touchscreen-mode", &touchscreen_mode,
3555                 NULL);
3556
3557   gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
3558
3559   gtk_menu_remove_scroll_timeout (menu);
3560
3561   menu->timeout_id = gdk_threads_add_timeout (timeout,
3562                                               gtk_menu_scroll_timeout,
3563                                               menu);
3564
3565   return FALSE;
3566 }
3567
3568 static void
3569 gtk_menu_start_scrolling (GtkMenu *menu)
3570 {
3571   guint    timeout;
3572   gboolean touchscreen_mode;
3573
3574   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
3575                 "gtk-timeout-repeat", &timeout,
3576                 "gtk-touchscreen-mode", &touchscreen_mode,
3577                 NULL);
3578
3579   gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
3580
3581   menu->timeout_id = gdk_threads_add_timeout (timeout,
3582                                               gtk_menu_scroll_timeout_initial,
3583                                               menu);
3584 }
3585
3586 static gboolean
3587 gtk_menu_scroll (GtkWidget      *widget,
3588                  GdkEventScroll *event)
3589 {
3590   GtkMenu *menu = GTK_MENU (widget);
3591
3592   switch (event->direction)
3593     {
3594     case GDK_SCROLL_RIGHT:
3595     case GDK_SCROLL_DOWN:
3596       gtk_menu_scroll_by (menu, MENU_SCROLL_STEP2);
3597       break;
3598     case GDK_SCROLL_LEFT:
3599     case GDK_SCROLL_UP:
3600       gtk_menu_scroll_by (menu, - MENU_SCROLL_STEP2);
3601       break;
3602     }
3603
3604   return TRUE;
3605 }
3606
3607 static void
3608 get_arrows_sensitive_area (GtkMenu      *menu,
3609                            GdkRectangle *upper,
3610                            GdkRectangle *lower)
3611 {
3612   gint width, height;
3613   gint border;
3614   guint vertical_padding;
3615   gint win_x, win_y;
3616   gint scroll_arrow_height;
3617   GtkArrowPlacement arrow_placement;
3618
3619   gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
3620
3621   gtk_widget_style_get (GTK_WIDGET (menu),
3622                         "vertical-padding", &vertical_padding,
3623                         "scroll-arrow-vlength", &scroll_arrow_height,
3624                         "arrow-placement", &arrow_placement,
3625                         NULL);
3626
3627   border = GTK_CONTAINER (menu)->border_width +
3628     GTK_WIDGET (menu)->style->ythickness + vertical_padding;
3629
3630   gdk_window_get_position (GTK_WIDGET (menu)->window, &win_x, &win_y);
3631
3632   switch (arrow_placement)
3633     {
3634     case GTK_ARROWS_BOTH:
3635       if (upper)
3636         {
3637           upper->x = win_x;
3638           upper->y = win_y;
3639           upper->width = width;
3640           upper->height = scroll_arrow_height + border;
3641         }
3642
3643       if (lower)
3644         {
3645           lower->x = win_x;
3646           lower->y = win_y + height - border - scroll_arrow_height;
3647           lower->width = width;
3648           lower->height = scroll_arrow_height + border;
3649         }
3650       break;
3651
3652     case GTK_ARROWS_START:
3653       if (upper)
3654         {
3655           upper->x = win_x;
3656           upper->y = win_y;
3657           upper->width = width / 2;
3658           upper->height = scroll_arrow_height + border;
3659         }
3660
3661       if (lower)
3662         {
3663           lower->x = win_x + width / 2;
3664           lower->y = win_y;
3665           lower->width = width / 2;
3666           lower->height = scroll_arrow_height + border;
3667         }
3668       break;
3669
3670     case GTK_ARROWS_END:
3671       if (upper)
3672         {
3673           upper->x = win_x;
3674           upper->y = win_y + height - border - scroll_arrow_height;
3675           upper->width = width / 2;
3676           upper->height = scroll_arrow_height + border;
3677         }
3678
3679       if (lower)
3680         {
3681           lower->x = win_x + width / 2;
3682           lower->y = win_y + height - border - scroll_arrow_height;
3683           lower->width = width / 2;
3684           lower->height = scroll_arrow_height + border;
3685         }
3686       break;
3687     }
3688 }
3689
3690
3691 static void
3692 gtk_menu_handle_scrolling (GtkMenu *menu,
3693                            gint     x,
3694                            gint     y,
3695                            gboolean enter,
3696                            gboolean motion)
3697 {
3698   GtkMenuShell *menu_shell;
3699   GtkMenuPrivate *priv;
3700   GdkRectangle rect;
3701   gboolean in_arrow;
3702   gboolean scroll_fast = FALSE;
3703   gint top_x, top_y;
3704   gboolean touchscreen_mode;
3705
3706   priv = gtk_menu_get_private (menu);
3707
3708   menu_shell = GTK_MENU_SHELL (menu);
3709
3710   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
3711                 "gtk-touchscreen-mode", &touchscreen_mode,
3712                 NULL);
3713
3714   gdk_window_get_position (menu->toplevel->window, &top_x, &top_y);
3715   x -= top_x;
3716   y -= top_y;
3717
3718   /*  upper arrow handling  */
3719
3720   get_arrows_sensitive_area (menu, &rect, NULL);
3721
3722   in_arrow = FALSE;
3723   if (menu->upper_arrow_visible && !menu->tearoff_active &&
3724       (x >= rect.x) && (x < rect.x + rect.width) &&
3725       (y >= rect.y) && (y < rect.y + rect.height))
3726     {
3727       in_arrow = TRUE;
3728     }
3729
3730   if (touchscreen_mode)
3731     menu->upper_arrow_prelight = in_arrow;
3732
3733   if (priv->upper_arrow_state != GTK_STATE_INSENSITIVE)
3734     {
3735       gboolean arrow_pressed = FALSE;
3736
3737       if (menu->upper_arrow_visible && !menu->tearoff_active)
3738         {
3739           if (touchscreen_mode)
3740             {
3741               if (enter && menu->upper_arrow_prelight)
3742                 {
3743                   if (menu->timeout_id == 0)
3744                     {
3745                       /* Deselect the active item so that
3746                        * any submenus are popped down
3747                        */
3748                       gtk_menu_shell_deselect (menu_shell);
3749
3750                       gtk_menu_remove_scroll_timeout (menu);
3751                       menu->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
3752
3753                       if (!motion)
3754                         {
3755                           /* Only do stuff on click. */
3756                           gtk_menu_start_scrolling (menu);
3757                           arrow_pressed = TRUE;
3758                         }
3759                     }
3760                   else
3761                     {
3762                       arrow_pressed = TRUE;
3763                     }
3764                 }
3765               else if (!enter)
3766                 {
3767                   gtk_menu_stop_scrolling (menu);
3768                 }
3769             }
3770           else /* !touchscreen_mode */
3771             {
3772               scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
3773
3774               if (enter && in_arrow &&
3775                   (!menu->upper_arrow_prelight ||
3776                    menu->scroll_fast != scroll_fast))
3777                 {
3778                   menu->upper_arrow_prelight = TRUE;
3779                   menu->scroll_fast = scroll_fast;
3780
3781                   /* Deselect the active item so that
3782                    * any submenus are popped down
3783                    */
3784                   gtk_menu_shell_deselect (menu_shell);
3785
3786                   gtk_menu_remove_scroll_timeout (menu);
3787                   menu->scroll_step = scroll_fast ?
3788                     -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1;
3789
3790                   menu->timeout_id =
3791                     gdk_threads_add_timeout (scroll_fast ?
3792                                              MENU_SCROLL_TIMEOUT2 :
3793                                              MENU_SCROLL_TIMEOUT1,
3794                                              gtk_menu_scroll_timeout, menu);
3795                 }
3796               else if (!enter && !in_arrow && menu->upper_arrow_prelight)
3797                 {
3798                   gtk_menu_stop_scrolling (menu);
3799                 }
3800             }
3801         }
3802
3803       /*  gtk_menu_start_scrolling() might have hit the top of the
3804        *  menu, so check if the button isn't insensitive before
3805        *  changing it to something else.
3806        */
3807       if (priv->upper_arrow_state != GTK_STATE_INSENSITIVE)
3808         {
3809           GtkStateType arrow_state = GTK_STATE_NORMAL;
3810
3811           if (arrow_pressed)
3812             arrow_state = GTK_STATE_ACTIVE;
3813           else if (menu->upper_arrow_prelight)
3814             arrow_state = GTK_STATE_PRELIGHT;
3815
3816           if (arrow_state != priv->upper_arrow_state)
3817             {
3818               priv->upper_arrow_state = arrow_state;
3819
3820               gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
3821                                           &rect, FALSE);
3822             }
3823         }
3824     }
3825
3826   /*  lower arrow handling  */
3827
3828   get_arrows_sensitive_area (menu, NULL, &rect);
3829
3830   in_arrow = FALSE;
3831   if (menu->lower_arrow_visible && !menu->tearoff_active &&
3832       (x >= rect.x) && (x < rect.x + rect.width) &&
3833       (y >= rect.y) && (y < rect.y + rect.height))
3834     {
3835       in_arrow = TRUE;
3836     }
3837
3838   if (touchscreen_mode)
3839     menu->lower_arrow_prelight = in_arrow;
3840
3841   if (priv->lower_arrow_state != GTK_STATE_INSENSITIVE)
3842     {
3843       gboolean arrow_pressed = FALSE;
3844
3845       if (menu->lower_arrow_visible && !menu->tearoff_active)
3846         {
3847           if (touchscreen_mode)
3848             {
3849               if (enter && menu->lower_arrow_prelight)
3850                 {
3851                   if (menu->timeout_id == 0)
3852                     {
3853                       /* Deselect the active item so that
3854                        * any submenus are popped down
3855                        */
3856                       gtk_menu_shell_deselect (menu_shell);
3857
3858                       gtk_menu_remove_scroll_timeout (menu);
3859                       menu->scroll_step = MENU_SCROLL_STEP2; /* always fast */
3860
3861                       if (!motion)
3862                         {
3863                           /* Only do stuff on click. */
3864                           gtk_menu_start_scrolling (menu);
3865                           arrow_pressed = TRUE;
3866                         }
3867                     }
3868                   else
3869                     {
3870                       arrow_pressed = TRUE;
3871                     }
3872                 }
3873               else if (!enter)
3874                 {
3875                   gtk_menu_stop_scrolling (menu);
3876                 }
3877             }
3878           else /* !touchscreen_mode */
3879             {
3880               scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
3881
3882               if (enter && in_arrow &&
3883                   (!menu->lower_arrow_prelight ||
3884                    menu->scroll_fast != scroll_fast))
3885                 {
3886                   menu->lower_arrow_prelight = TRUE;
3887                   menu->scroll_fast = scroll_fast;
3888
3889                   /* Deselect the active item so that
3890                    * any submenus are popped down
3891                    */
3892                   gtk_menu_shell_deselect (menu_shell);
3893
3894                   gtk_menu_remove_scroll_timeout (menu);
3895                   menu->scroll_step = scroll_fast ?
3896                     MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1;
3897
3898                   menu->timeout_id =
3899                     gdk_threads_add_timeout (scroll_fast ?
3900                                              MENU_SCROLL_TIMEOUT2 :
3901                                              MENU_SCROLL_TIMEOUT1,
3902                                              gtk_menu_scroll_timeout, menu);
3903                 }
3904               else if (!enter && !in_arrow && menu->lower_arrow_prelight)
3905                 {
3906                   gtk_menu_stop_scrolling (menu);
3907                 }
3908             }
3909         }
3910
3911       /*  gtk_menu_start_scrolling() might have hit the bottom of the
3912        *  menu, so check if the button isn't insensitive before
3913        *  changing it to something else.
3914        */
3915       if (priv->lower_arrow_state != GTK_STATE_INSENSITIVE)
3916         {
3917           GtkStateType arrow_state = GTK_STATE_NORMAL;
3918
3919           if (arrow_pressed)
3920             arrow_state = GTK_STATE_ACTIVE;
3921           else if (menu->lower_arrow_prelight)
3922             arrow_state = GTK_STATE_PRELIGHT;
3923
3924           if (arrow_state != priv->lower_arrow_state)
3925             {
3926               priv->lower_arrow_state = arrow_state;
3927
3928               gdk_window_invalidate_rect (GTK_WIDGET (menu)->window,
3929                                           &rect, FALSE);
3930             }
3931         }
3932     }
3933 }
3934
3935 static gboolean
3936 gtk_menu_enter_notify (GtkWidget        *widget,
3937                        GdkEventCrossing *event)
3938 {
3939   GtkWidget *menu_item;
3940   gboolean   touchscreen_mode;
3941
3942   if (event->mode == GDK_CROSSING_GTK_GRAB ||
3943       event->mode == GDK_CROSSING_GTK_UNGRAB ||
3944       event->mode == GDK_CROSSING_STATE_CHANGED)
3945     return TRUE;
3946
3947   g_object_get (gtk_widget_get_settings (widget),
3948                 "gtk-touchscreen-mode", &touchscreen_mode,
3949                 NULL);
3950
3951   menu_item = gtk_get_event_widget ((GdkEvent*) event);
3952   if (GTK_IS_MENU (widget))
3953     {
3954       GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
3955
3956       if (!menu_shell->ignore_enter)
3957         gtk_menu_handle_scrolling (GTK_MENU (widget),
3958                                    event->x_root, event->y_root, TRUE, TRUE);
3959     }
3960
3961   if (!touchscreen_mode && GTK_IS_MENU_ITEM (menu_item))
3962     {
3963       GtkWidget *menu = menu_item->parent;
3964       
3965       if (GTK_IS_MENU (menu))
3966         {
3967           GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (menu));
3968           GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
3969
3970           if (priv->seen_item_enter)
3971             {
3972               /* This is the second enter we see for an item
3973                * on this menu. This means a release should always
3974                * mean activate.
3975                */
3976               menu_shell->activate_time = 0;
3977             }
3978           else if ((event->detail != GDK_NOTIFY_NONLINEAR &&
3979                     event->detail != GDK_NOTIFY_NONLINEAR_VIRTUAL))
3980             {
3981               if (definitely_within_item (menu_item, event->x, event->y))
3982                 {
3983                   /* This is an actual user-enter (ie. not a pop-under)
3984                    * In this case, the user must either have entered
3985                    * sufficiently far enough into the item, or he must move
3986                    * far enough away from the enter point. (see
3987                    * gtk_menu_motion_notify())
3988                    */
3989                   menu_shell->activate_time = 0;
3990                 }
3991             }
3992             
3993           priv->seen_item_enter = TRUE;
3994         }
3995     }
3996   
3997   /* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
3998    * will not correspond to the event widget's parent.  Check to see
3999    * if we are in the parent's navigation region.
4000    */
4001   if (GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) &&
4002       gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent),
4003                                    event->x_root, event->y_root))
4004     return TRUE;
4005
4006   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (widget, event); 
4007 }
4008
4009 static gboolean
4010 gtk_menu_leave_notify (GtkWidget        *widget,
4011                        GdkEventCrossing *event)
4012 {
4013   GtkMenuShell *menu_shell;
4014   GtkMenu *menu;
4015   GtkMenuItem *menu_item;
4016   GtkWidget *event_widget;
4017
4018   if (event->mode == GDK_CROSSING_GTK_GRAB ||
4019       event->mode == GDK_CROSSING_GTK_UNGRAB ||
4020       event->mode == GDK_CROSSING_STATE_CHANGED)
4021     return TRUE;
4022
4023   menu = GTK_MENU (widget);
4024   menu_shell = GTK_MENU_SHELL (widget); 
4025   
4026   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
4027     return TRUE; 
4028
4029   gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
4030
4031   event_widget = gtk_get_event_widget ((GdkEvent*) event);
4032   
4033   if (!GTK_IS_MENU_ITEM (event_widget))
4034     return TRUE;
4035   
4036   menu_item = GTK_MENU_ITEM (event_widget); 
4037
4038   /* Here we check to see if we're leaving an active menu item with a submenu, 
4039    * in which case we enter submenu navigation mode. 
4040    */
4041   if (menu_shell->active_menu_item != NULL
4042       && menu_item->submenu != NULL
4043       && menu_item->submenu_placement == GTK_LEFT_RIGHT)
4044     {
4045       if (GTK_MENU_SHELL (menu_item->submenu)->active)
4046         {
4047           gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
4048           return TRUE;
4049         }
4050       else if (menu_item == GTK_MENU_ITEM (menu_shell->active_menu_item))
4051         {
4052           /* We are leaving an active menu item with nonactive submenu.
4053            * Deselect it so we don't surprise the user with by popping
4054            * up a submenu _after_ he left the item.
4055            */
4056           gtk_menu_shell_deselect (menu_shell);
4057           return TRUE;
4058         }
4059     }
4060   
4061   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event); 
4062 }
4063
4064 static void 
4065 gtk_menu_stop_navigating_submenu (GtkMenu *menu)
4066 {
4067   if (menu->navigation_region) 
4068     {
4069       gdk_region_destroy (menu->navigation_region);
4070       menu->navigation_region = NULL;
4071     }  
4072   if (menu->navigation_timeout)
4073     {
4074       g_source_remove (menu->navigation_timeout);
4075       menu->navigation_timeout = 0;
4076     }
4077 }
4078
4079 /* When the timeout is elapsed, the navigation region is destroyed
4080  * and the menuitem under the pointer (if any) is selected.
4081  */
4082 static gboolean
4083 gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
4084 {
4085   GtkMenuPopdownData *popdown_data = user_data;
4086   GtkMenu *menu = popdown_data->menu;
4087   GdkWindow *child_window;
4088
4089   gtk_menu_stop_navigating_submenu (menu);
4090   
4091   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
4092     {
4093       child_window = gdk_window_get_device_position (menu->bin_window,
4094                                                      popdown_data->device,
4095                                                      NULL, NULL, NULL);
4096
4097       if (child_window)
4098         {
4099           GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
4100
4101           send_event->crossing.window = g_object_ref (child_window);
4102           send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
4103           send_event->crossing.send_event = TRUE;
4104           gdk_event_set_device (send_event, popdown_data->device);
4105
4106           GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
4107
4108           gdk_event_free (send_event);
4109         }
4110     }
4111
4112   return FALSE; 
4113 }
4114
4115 static gboolean
4116 gtk_menu_navigating_submenu (GtkMenu *menu,
4117                              gint     event_x,
4118                              gint     event_y)
4119 {
4120   if (menu->navigation_region)
4121     {
4122       if (gdk_region_point_in (menu->navigation_region, event_x, event_y))
4123         return TRUE;
4124       else
4125         {
4126           gtk_menu_stop_navigating_submenu (menu);
4127           return FALSE;
4128         }
4129     }
4130   return FALSE;
4131 }
4132
4133 #undef DRAW_STAY_UP_TRIANGLE
4134
4135 #ifdef DRAW_STAY_UP_TRIANGLE
4136
4137 static void
4138 draw_stay_up_triangle (GdkWindow *window,
4139                        GdkRegion *region)
4140 {
4141   /* Draw ugly color all over the stay-up triangle */
4142   GdkColor ugly_color = { 0, 50000, 10000, 10000 };
4143   GdkGCValues gc_values;
4144   GdkGC *ugly_gc;
4145   GdkRectangle clipbox;
4146
4147   gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
4148   ugly_gc = gdk_gc_new_with_values (window, &gc_values, 0 | GDK_GC_SUBWINDOW);
4149   gdk_gc_set_rgb_fg_color (ugly_gc, &ugly_color);
4150   gdk_gc_set_clip_region (ugly_gc, region);
4151
4152   gdk_region_get_clipbox (region, &clipbox);
4153   
4154   gdk_draw_rectangle (window,
4155                      ugly_gc,
4156                      TRUE,
4157                      clipbox.x, clipbox.y,
4158                      clipbox.width, clipbox.height);
4159   
4160   g_object_unref (ugly_gc);
4161 }
4162 #endif
4163
4164 static GdkRegion *
4165 flip_region (GdkRegion *region,
4166              gboolean   flip_x,
4167              gboolean   flip_y)
4168 {
4169   gint n_rectangles;
4170   GdkRectangle *rectangles;
4171   GdkRectangle clipbox;
4172   GdkRegion *new_region;
4173   gint i;
4174
4175   new_region = gdk_region_new ();
4176   
4177   gdk_region_get_rectangles (region, &rectangles, &n_rectangles);
4178   gdk_region_get_clipbox (region, &clipbox);
4179
4180   for (i = 0; i < n_rectangles; ++i)
4181     {
4182       GdkRectangle rect = rectangles[i];
4183
4184       if (flip_y)
4185         rect.y -= 2 * (rect.y - clipbox.y) + rect.height;
4186
4187       if (flip_x)
4188         rect.x -= 2 * (rect.x - clipbox.x) + rect.width;
4189
4190       gdk_region_union_with_rect (new_region, &rect);
4191     }
4192
4193   g_free (rectangles);
4194
4195   return new_region;
4196 }
4197
4198 static void
4199 gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
4200                                         GtkMenuItem      *menu_item,
4201                                         GdkEventCrossing *event)
4202 {
4203   gint submenu_left = 0;
4204   gint submenu_right = 0;
4205   gint submenu_top = 0;
4206   gint submenu_bottom = 0;
4207   gint width = 0;
4208   gint height = 0;
4209   GdkPoint point[3];
4210   GtkWidget *event_widget;
4211   GtkMenuPopdownData *popdown_data;
4212
4213   g_return_if_fail (menu_item->submenu != NULL);
4214   g_return_if_fail (event != NULL);
4215   
4216   event_widget = gtk_get_event_widget ((GdkEvent*) event);
4217   
4218   gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top);
4219   gdk_drawable_get_size (menu_item->submenu->window, &width, &height);
4220   
4221   submenu_right = submenu_left + width;
4222   submenu_bottom = submenu_top + height;
4223   
4224   gdk_drawable_get_size (event_widget->window, &width, &height);
4225   
4226   if (event->x >= 0 && event->x < width)
4227     {
4228       gint popdown_delay;
4229       gboolean flip_y = FALSE;
4230       gboolean flip_x = FALSE;
4231       
4232       gtk_menu_stop_navigating_submenu (menu);
4233
4234       if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
4235         {
4236           /* right */
4237           point[0].x = event->x_root;
4238           point[1].x = submenu_left;
4239         }
4240       else
4241         {
4242           /* left */
4243           point[0].x = event->x_root + 1;
4244           point[1].x = 2 * (event->x_root + 1) - submenu_right;
4245
4246           flip_x = TRUE;
4247         }
4248
4249       if (event->y < 0)
4250         {
4251           /* top */
4252           point[0].y = event->y_root + 1;
4253           point[1].y = 2 * (event->y_root + 1) - submenu_top + NAVIGATION_REGION_OVERSHOOT;
4254
4255           if (point[0].y >= point[1].y - NAVIGATION_REGION_OVERSHOOT)
4256             return;
4257
4258           flip_y = TRUE;
4259         }
4260       else
4261         {
4262           /* bottom */
4263           point[0].y = event->y_root;
4264           point[1].y = submenu_bottom + NAVIGATION_REGION_OVERSHOOT;
4265
4266           if (point[0].y >= submenu_bottom)
4267             return;
4268         }
4269
4270       point[2].x = point[1].x;
4271       point[2].y = point[0].y;
4272
4273       menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
4274
4275       if (flip_x || flip_y)
4276         {
4277           GdkRegion *new_region = flip_region (menu->navigation_region, flip_x, flip_y);
4278           gdk_region_destroy (menu->navigation_region);
4279           menu->navigation_region = new_region;
4280         }
4281
4282       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
4283                     "gtk-menu-popdown-delay", &popdown_delay,
4284                     NULL);
4285
4286       popdown_data = g_new (GtkMenuPopdownData, 1);
4287       popdown_data->menu = menu;
4288       popdown_data->device = gdk_event_get_device ((GdkEvent *) event);
4289
4290       menu->navigation_timeout = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
4291                                                                popdown_delay,
4292                                                                gtk_menu_stop_navigating_submenu_cb,
4293                                                                popdown_data,
4294                                                                (GDestroyNotify) g_free);
4295
4296 #ifdef DRAW_STAY_UP_TRIANGLE
4297       draw_stay_up_triangle (gdk_get_default_root_window(),
4298                              menu->navigation_region);
4299 #endif
4300     }
4301 }
4302
4303 static void
4304 gtk_menu_deactivate (GtkMenuShell *menu_shell)
4305 {
4306   GtkWidget *parent;
4307   
4308   g_return_if_fail (GTK_IS_MENU (menu_shell));
4309   
4310   parent = menu_shell->parent_menu_shell;
4311   
4312   menu_shell->activate_time = 0;
4313   gtk_menu_popdown (GTK_MENU (menu_shell));
4314   
4315   if (parent)
4316     gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
4317 }
4318
4319 static void
4320 gtk_menu_position (GtkMenu *menu)
4321 {
4322   GtkWidget *widget;
4323   GtkRequisition requisition;
4324   GtkMenuPrivate *private;
4325   gint x, y;
4326   gint scroll_offset;
4327   gint menu_height;
4328   GdkScreen *screen;
4329   GdkScreen *pointer_screen;
4330   GdkRectangle monitor;
4331   GdkDevice *pointer;
4332
4333   g_return_if_fail (GTK_IS_MENU (menu));
4334
4335   widget = GTK_WIDGET (menu);
4336
4337   screen = gtk_widget_get_screen (widget);
4338   pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (menu));
4339   gdk_display_get_device_state (gdk_screen_get_display (screen),
4340                                 pointer, &pointer_screen, &x, &y, NULL);
4341
4342   /* We need the requisition to figure out the right place to
4343    * popup the menu. In fact, we always need to ask here, since
4344    * if a size_request was queued while we weren't popped up,
4345    * the requisition won't have been recomputed yet.
4346    */
4347   gtk_widget_size_request (widget, &requisition);
4348
4349   if (pointer_screen != screen)
4350     {
4351       /* Pointer is on a different screen; roughly center the
4352        * menu on the screen. If someone was using multiscreen
4353        * + Xinerama together they'd probably want something
4354        * fancier; but that is likely to be vanishingly rare.
4355        */
4356       x = MAX (0, (gdk_screen_get_width (screen) - requisition.width) / 2);
4357       y = MAX (0, (gdk_screen_get_height (screen) - requisition.height) / 2);
4358     }
4359
4360   private = gtk_menu_get_private (menu);
4361   private->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
4362
4363   private->initially_pushed_in = FALSE;
4364
4365   /* Set the type hint here to allow custom position functions to set a different hint */
4366   if (!gtk_widget_get_visible (menu->toplevel))
4367     gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel), GDK_WINDOW_TYPE_HINT_POPUP_MENU);
4368   
4369   if (menu->position_func)
4370     {
4371       (* menu->position_func) (menu, &x, &y, &private->initially_pushed_in,
4372                                menu->position_func_data);
4373
4374       if (private->monitor_num < 0) 
4375         private->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
4376
4377       gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor);
4378     }
4379   else
4380     {
4381       gint space_left, space_right, space_above, space_below;
4382       gint needed_width;
4383       gint needed_height;
4384       gint xthickness = widget->style->xthickness;
4385       gint ythickness = widget->style->ythickness;
4386       gboolean rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
4387
4388       /* The placement of popup menus horizontally works like this (with
4389        * RTL in parentheses)
4390        *
4391        * - If there is enough room to the right (left) of the mouse cursor,
4392        *   position the menu there.
4393        * 
4394        * - Otherwise, if if there is enough room to the left (right) of the 
4395        *   mouse cursor, position the menu there.
4396        * 
4397        * - Otherwise if the menu is smaller than the monitor, position it
4398        *   on the side of the mouse cursor that has the most space available
4399        *
4400        * - Otherwise (if there is simply not enough room for the menu on the
4401        *   monitor), position it as far left (right) as possible.
4402        *
4403        * Positioning in the vertical direction is similar: first try below
4404        * mouse cursor, then above.
4405        */
4406       gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor);
4407
4408       space_left = x - monitor.x;
4409       space_right = monitor.x + monitor.width - x - 1;
4410       space_above = y - monitor.y;
4411       space_below = monitor.y + monitor.height - y - 1;
4412
4413       /* position horizontally */
4414
4415       /* the amount of space we need to position the menu. Note the
4416        * menu is offset "xthickness" pixels 
4417        */
4418       needed_width = requisition.width - xthickness;
4419
4420       if (needed_width <= space_left ||
4421           needed_width <= space_right)
4422         {
4423           if ((rtl  && needed_width <= space_left) ||
4424               (!rtl && needed_width >  space_right))
4425             {
4426               /* position left */
4427               x = x + xthickness - requisition.width + 1;
4428             }
4429           else
4430             {
4431               /* position right */
4432               x = x - xthickness;
4433             }
4434
4435           /* x is clamped on-screen further down */
4436         }
4437       else if (requisition.width <= monitor.width)
4438         {
4439           /* the menu is too big to fit on either side of the mouse
4440            * cursor, but smaller than the monitor. Position it on
4441            * the side that has the most space
4442            */
4443           if (space_left > space_right)
4444             {
4445               /* left justify */
4446               x = monitor.x;
4447             }
4448           else
4449             {
4450               /* right justify */
4451               x = monitor.x + monitor.width - requisition.width;
4452             }
4453         }
4454       else /* menu is simply too big for the monitor */
4455         {
4456           if (rtl)
4457             {
4458               /* right justify */
4459               x = monitor.x + monitor.width - requisition.width;
4460             }
4461           else
4462             {
4463               /* left justify */
4464               x = monitor.x;
4465             }
4466         }
4467
4468       /* Position vertically. The algorithm is the same as above, but
4469        * simpler because we don't have to take RTL into account.
4470        */
4471       needed_height = requisition.height - ythickness;
4472
4473       if (needed_height <= space_above ||
4474           needed_height <= space_below)
4475         {
4476           if (needed_height <= space_below)
4477             y = y - ythickness;
4478           else
4479             y = y + ythickness - requisition.height + 1;
4480           
4481           y = CLAMP (y, monitor.y,
4482                      monitor.y + monitor.height - requisition.height);
4483         }
4484       else if (needed_height > space_below && needed_height > space_above)
4485         {
4486           if (space_below >= space_above)
4487             y = monitor.y + monitor.height - requisition.height;
4488           else
4489             y = monitor.y;
4490         }
4491       else
4492         {
4493           y = monitor.y;
4494         }
4495     }
4496
4497   scroll_offset = 0;
4498
4499   if (private->initially_pushed_in)
4500     {
4501       menu_height = GTK_WIDGET (menu)->requisition.height;
4502
4503       if (y + menu_height > monitor.y + monitor.height)
4504         {
4505           scroll_offset -= y + menu_height - (monitor.y + monitor.height);
4506           y = (monitor.y + monitor.height) - menu_height;
4507         }
4508   
4509       if (y < monitor.y)
4510         {
4511           scroll_offset += monitor.y - y;
4512           y = monitor.y;
4513         }
4514     }
4515
4516   /* FIXME: should this be done in the various position_funcs ? */
4517   x = CLAMP (x, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width));
4518  
4519   if (GTK_MENU_SHELL (menu)->active)
4520     {
4521       private->have_position = TRUE;
4522       private->x = x;
4523       private->y = y;
4524     }
4525   
4526   if (y + requisition.height > monitor.y + monitor.height)
4527     requisition.height = (monitor.y + monitor.height) - y;
4528   
4529   if (y < monitor.y)
4530     {
4531       scroll_offset += monitor.y - y;
4532       requisition.height -= monitor.y - y;
4533       y = monitor.y;
4534     }
4535
4536   if (scroll_offset > 0)
4537     {
4538       GtkBorder arrow_border;
4539
4540       get_arrows_border (menu, &arrow_border);
4541       scroll_offset += arrow_border.top;
4542     }
4543   
4544   gtk_window_move (GTK_WINDOW (GTK_MENU_SHELL (menu)->active ? menu->toplevel : menu->tearoff_window), 
4545                    x, y);
4546
4547   if (!GTK_MENU_SHELL (menu)->active)
4548     {
4549       gtk_window_resize (GTK_WINDOW (menu->tearoff_window),
4550                          requisition.width, requisition.height);
4551     }
4552   
4553   menu->scroll_offset = scroll_offset;
4554 }
4555
4556 static void
4557 gtk_menu_remove_scroll_timeout (GtkMenu *menu)
4558 {
4559   if (menu->timeout_id)
4560     {
4561       g_source_remove (menu->timeout_id);
4562       menu->timeout_id = 0;
4563     }
4564 }
4565
4566 static void
4567 gtk_menu_stop_scrolling (GtkMenu *menu)
4568 {
4569   gboolean touchscreen_mode;
4570
4571   gtk_menu_remove_scroll_timeout (menu);
4572
4573   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
4574                 "gtk-touchscreen-mode", &touchscreen_mode,
4575                 NULL);
4576
4577   if (!touchscreen_mode)
4578     {
4579       menu->upper_arrow_prelight = FALSE;
4580       menu->lower_arrow_prelight = FALSE;
4581     }
4582 }
4583
4584 static void
4585 gtk_menu_scroll_to (GtkMenu *menu,
4586                     gint    offset)
4587 {
4588   GtkWidget *widget;
4589   gint x, y;
4590   gint view_width, view_height;
4591   gint border_width;
4592   gint menu_height;
4593   guint vertical_padding;
4594   guint horizontal_padding;
4595   gboolean double_arrows;
4596   GtkBorder arrow_border;
4597   
4598   widget = GTK_WIDGET (menu);
4599
4600   if (menu->tearoff_active &&
4601       menu->tearoff_adjustment &&
4602       (menu->tearoff_adjustment->value != offset))
4603     {
4604       menu->tearoff_adjustment->value =
4605         CLAMP (offset,
4606                0, menu->tearoff_adjustment->upper - menu->tearoff_adjustment->page_size);
4607       gtk_adjustment_value_changed (menu->tearoff_adjustment);
4608     }
4609   
4610   /* Move/resize the viewport according to arrows: */
4611   view_width = widget->allocation.width;
4612   view_height = widget->allocation.height;
4613
4614   gtk_widget_style_get (GTK_WIDGET (menu),
4615                         "vertical-padding", &vertical_padding,
4616                         "horizontal-padding", &horizontal_padding,
4617                         NULL);
4618
4619   double_arrows = get_double_arrows (menu);
4620
4621   border_width = GTK_CONTAINER (menu)->border_width;
4622   view_width -= (border_width + widget->style->xthickness + horizontal_padding) * 2;
4623   view_height -= (border_width + widget->style->ythickness + vertical_padding) * 2;
4624   menu_height = widget->requisition.height -
4625     (border_width + widget->style->ythickness + vertical_padding) * 2;
4626
4627   x = border_width + widget->style->xthickness + horizontal_padding;
4628   y = border_width + widget->style->ythickness + vertical_padding;
4629
4630   if (double_arrows && !menu->tearoff_active)
4631     {
4632       if (view_height < menu_height               ||
4633           (offset > 0 && menu->scroll_offset > 0) ||
4634           (offset < 0 && menu->scroll_offset < 0))
4635         {
4636           GtkMenuPrivate *priv = gtk_menu_get_private (menu);
4637           GtkStateType    upper_arrow_previous_state = priv->upper_arrow_state;
4638           GtkStateType    lower_arrow_previous_state = priv->lower_arrow_state;
4639
4640           if (!menu->upper_arrow_visible || !menu->lower_arrow_visible)
4641             gtk_widget_queue_draw (GTK_WIDGET (menu));
4642
4643           menu->upper_arrow_visible = menu->lower_arrow_visible = TRUE;
4644
4645           get_arrows_border (menu, &arrow_border);
4646           y += arrow_border.top;
4647           view_height -= arrow_border.top;
4648           view_height -= arrow_border.bottom;
4649
4650           if (offset <= 0)
4651             priv->upper_arrow_state = GTK_STATE_INSENSITIVE;
4652           else if (priv->upper_arrow_state == GTK_STATE_INSENSITIVE)
4653             priv->upper_arrow_state = menu->upper_arrow_prelight ?
4654               GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
4655
4656           if (offset >= menu_height - view_height)
4657             priv->lower_arrow_state = GTK_STATE_INSENSITIVE;
4658           else if (priv->lower_arrow_state == GTK_STATE_INSENSITIVE)
4659             priv->lower_arrow_state = menu->lower_arrow_prelight ?
4660               GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
4661
4662           if ((priv->upper_arrow_state != upper_arrow_previous_state) ||
4663               (priv->lower_arrow_state != lower_arrow_previous_state))
4664             gtk_widget_queue_draw (GTK_WIDGET (menu));
4665
4666           if (upper_arrow_previous_state != GTK_STATE_INSENSITIVE &&
4667               priv->upper_arrow_state == GTK_STATE_INSENSITIVE)
4668             {
4669               /* At the upper border, possibly remove timeout */
4670               if (menu->scroll_step < 0)
4671                 {
4672                   gtk_menu_stop_scrolling (menu);
4673                   gtk_widget_queue_draw (GTK_WIDGET (menu));
4674                 }
4675             }
4676
4677           if (lower_arrow_previous_state != GTK_STATE_INSENSITIVE &&
4678               priv->lower_arrow_state == GTK_STATE_INSENSITIVE)
4679             {
4680               /* At the lower border, possibly remove timeout */
4681               if (menu->scroll_step > 0)
4682                 {
4683                   gtk_menu_stop_scrolling (menu);
4684                   gtk_widget_queue_draw (GTK_WIDGET (menu));
4685                 }
4686             }
4687         }
4688       else if (menu->upper_arrow_visible || menu->lower_arrow_visible)
4689         {
4690           offset = 0;
4691
4692           menu->upper_arrow_visible = menu->lower_arrow_visible = FALSE;
4693           menu->upper_arrow_prelight = menu->lower_arrow_prelight = FALSE;
4694
4695           gtk_menu_stop_scrolling (menu);
4696           gtk_widget_queue_draw (GTK_WIDGET (menu));
4697         }
4698     }
4699   else if (!menu->tearoff_active)
4700     {
4701       gboolean last_visible;
4702
4703       last_visible = menu->upper_arrow_visible;
4704       menu->upper_arrow_visible = offset > 0;
4705       
4706       /* upper_arrow_visible may have changed, so requery the border */
4707       get_arrows_border (menu, &arrow_border);
4708       view_height -= arrow_border.top;
4709       
4710       if ((last_visible != menu->upper_arrow_visible) &&
4711           !menu->upper_arrow_visible)
4712         {
4713           menu->upper_arrow_prelight = FALSE;
4714
4715           /* If we hid the upper arrow, possibly remove timeout */
4716           if (menu->scroll_step < 0)
4717             {
4718               gtk_menu_stop_scrolling (menu);
4719               gtk_widget_queue_draw (GTK_WIDGET (menu));
4720             }
4721         }
4722
4723       last_visible = menu->lower_arrow_visible;
4724       menu->lower_arrow_visible = offset < menu_height - view_height;
4725       
4726       /* lower_arrow_visible may have changed, so requery the border */
4727       get_arrows_border (menu, &arrow_border);
4728       view_height -= arrow_border.bottom;
4729       
4730       if ((last_visible != menu->lower_arrow_visible) &&
4731            !menu->lower_arrow_visible)
4732         {
4733           menu->lower_arrow_prelight = FALSE;
4734
4735           /* If we hid the lower arrow, possibly remove timeout */
4736           if (menu->scroll_step > 0)
4737             {
4738               gtk_menu_stop_scrolling (menu);
4739               gtk_widget_queue_draw (GTK_WIDGET (menu));
4740             }
4741         }
4742       
4743       y += arrow_border.top;
4744     }
4745
4746   /* Scroll the menu: */
4747   if (gtk_widget_get_realized (widget))
4748     gdk_window_move (menu->bin_window, 0, -offset);
4749
4750   if (gtk_widget_get_realized (widget))
4751     gdk_window_move_resize (menu->view_window,
4752                             x,
4753                             y,
4754                             view_width,
4755                             view_height);
4756
4757   menu->scroll_offset = offset;
4758 }
4759
4760 static gboolean
4761 compute_child_offset (GtkMenu   *menu,
4762                       GtkWidget *menu_item,
4763                       gint      *offset,
4764                       gint      *height,
4765                       gboolean  *is_last_child)
4766 {
4767   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
4768   gint item_top_attach;
4769   gint item_bottom_attach;
4770   gint child_offset = 0;
4771   gint i;
4772
4773   get_effective_child_attach (menu_item, NULL, NULL,
4774                               &item_top_attach, &item_bottom_attach);
4775
4776   /* there is a possibility that we get called before _size_request, so
4777    * check the height table for safety.
4778    */
4779   if (!priv->heights || priv->heights_length < gtk_menu_get_n_rows (menu))
4780     return FALSE;
4781
4782   /* when we have a row with only invisible children, it's height will
4783    * be zero, so there's no need to check WIDGET_VISIBLE here
4784    */
4785   for (i = 0; i < item_top_attach; i++)
4786     child_offset += priv->heights[i];
4787
4788   if (is_last_child)
4789     *is_last_child = (item_bottom_attach == gtk_menu_get_n_rows (menu));
4790   if (offset)
4791     *offset = child_offset;
4792   if (height)
4793     *height = priv->heights[item_top_attach];
4794
4795   return TRUE;
4796 }
4797
4798 static void
4799 gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
4800                               GtkWidget    *menu_item)
4801 {
4802   GtkMenu *menu;
4803   gint child_offset, child_height;
4804   gint width, height;
4805   gint y;
4806   gint arrow_height;
4807   gboolean last_child = 0;
4808   
4809   menu = GTK_MENU (menu_shell);
4810
4811   /* We need to check if the selected item fully visible.
4812    * If not we need to scroll the menu so that it becomes fully
4813    * visible.
4814    */
4815
4816   if (compute_child_offset (menu, menu_item,
4817                             &child_offset, &child_height, &last_child))
4818     {
4819       guint vertical_padding;
4820       gboolean double_arrows;
4821       
4822       y = menu->scroll_offset;
4823       gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
4824
4825       gtk_widget_style_get (GTK_WIDGET (menu),
4826                             "vertical-padding", &vertical_padding,
4827                             NULL);
4828
4829       double_arrows = get_double_arrows (menu);
4830
4831       height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness + 2*vertical_padding;
4832       
4833       if (child_offset < y)
4834         {
4835           /* Ignore the enter event we might get if the pointer is on the menu
4836            */
4837           menu_shell->ignore_enter = TRUE;
4838           gtk_menu_scroll_to (menu, child_offset);
4839         }
4840       else
4841         {
4842           GtkBorder arrow_border;
4843
4844           arrow_height = 0;
4845
4846           get_arrows_border (menu, &arrow_border);
4847           if (!menu->tearoff_active)
4848             arrow_height = arrow_border.top + arrow_border.bottom;
4849           
4850           if (child_offset + child_height > y + height - arrow_height)
4851             {
4852               arrow_height = 0;
4853               if ((!last_child && !menu->tearoff_active) || double_arrows)
4854                 arrow_height += arrow_border.bottom;
4855
4856               y = child_offset + child_height - height + arrow_height;
4857               if (((y > 0) && !menu->tearoff_active) || double_arrows)
4858                 {
4859                   /* Need upper arrow */
4860                   arrow_height += arrow_border.top;
4861                   y = child_offset + child_height - height + arrow_height;
4862                 }
4863               /* Ignore the enter event we might get if the pointer is on the menu
4864                */
4865               menu_shell->ignore_enter = TRUE;
4866               gtk_menu_scroll_to (menu, y);
4867             }
4868         }    
4869       
4870     }
4871 }
4872
4873 static void
4874 gtk_menu_select_item (GtkMenuShell *menu_shell,
4875                       GtkWidget    *menu_item)
4876 {
4877   GtkMenu *menu = GTK_MENU (menu_shell);
4878
4879   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
4880     gtk_menu_scroll_item_visible (menu_shell, menu_item);
4881
4882   GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->select_item (menu_shell, menu_item);
4883 }
4884
4885
4886 /* Reparent the menu, taking care of the refcounting
4887  *
4888  * If unrealize is true we force a unrealize while reparenting the parent.
4889  * This can help eliminate flicker in some cases.
4890  *
4891  * What happens is that when the menu is unrealized and then re-realized,
4892  * the allocations are as follows:
4893  *
4894  *  parent - 1x1 at (0,0) 
4895  *  child1 - 100x20 at (0,0)
4896  *  child2 - 100x20 at (0,20)
4897  *  child3 - 100x20 at (0,40)
4898  *
4899  * That is, the parent is small but the children are full sized. Then,
4900  * when the queued_resize gets processed, the parent gets resized to
4901  * full size. 
4902  *
4903  * But in order to eliminate flicker when scrolling, gdkgeometry-x11.c
4904  * contains the following logic:
4905  * 
4906  * - if a move or resize operation on a window would change the clip 
4907  *   region on the children, then before the window is resized
4908  *   the background for children is temporarily set to None, the
4909  *   move/resize done, and the background for the children restored.
4910  *
4911  * So, at the point where the parent is resized to final size, the
4912  * background for the children is temporarily None, and thus they
4913  * are not cleared to the background color and the previous background
4914  * (the image of the menu) is left in place.
4915  */
4916 static void 
4917 gtk_menu_reparent (GtkMenu   *menu,
4918                    GtkWidget *new_parent,
4919                    gboolean   unrealize)
4920 {
4921   GtkObject *object = GTK_OBJECT (menu);
4922   GtkWidget *widget = GTK_WIDGET (menu);
4923   gboolean was_floating = g_object_is_floating (object);
4924
4925   g_object_ref_sink (object);
4926
4927   if (unrealize)
4928     {
4929       g_object_ref (object);
4930       gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
4931       gtk_container_add (GTK_CONTAINER (new_parent), widget);
4932       g_object_unref (object);
4933     }
4934   else
4935     gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
4936   
4937   if (was_floating)
4938     g_object_force_floating (G_OBJECT (object));
4939   else
4940     g_object_unref (object);
4941 }
4942
4943 static void
4944 gtk_menu_show_all (GtkWidget *widget)
4945 {
4946   /* Show children, but not self. */
4947   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
4948 }
4949
4950
4951 static void
4952 gtk_menu_hide_all (GtkWidget *widget)
4953 {
4954   /* Hide children, but not self. */
4955   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
4956 }
4957
4958 /**
4959  * gtk_menu_set_screen:
4960  * @menu: a #GtkMenu.
4961  * @screen: (allow-none): a #GdkScreen, or %NULL if the screen should be
4962  *          determined by the widget the menu is attached to.
4963  *
4964  * Sets the #GdkScreen on which the menu will be displayed.
4965  *
4966  * Since: 2.2
4967  **/
4968 void
4969 gtk_menu_set_screen (GtkMenu   *menu, 
4970                      GdkScreen *screen)
4971 {
4972   g_return_if_fail (GTK_IS_MENU (menu));
4973   g_return_if_fail (!screen || GDK_IS_SCREEN (screen));
4974
4975   g_object_set_data (G_OBJECT (menu), I_("gtk-menu-explicit-screen"), screen);
4976
4977   if (screen)
4978     {
4979       menu_change_screen (menu, screen);
4980     }
4981   else
4982     {
4983       GtkWidget *attach_widget = gtk_menu_get_attach_widget (menu);
4984       if (attach_widget)
4985         attach_widget_screen_changed (attach_widget, NULL, menu);
4986     }
4987 }
4988
4989 /**
4990  * gtk_menu_attach:
4991  * @menu: a #GtkMenu.
4992  * @child: a #GtkMenuItem.
4993  * @left_attach: The column number to attach the left side of the item to.
4994  * @right_attach: The column number to attach the right side of the item to.
4995  * @top_attach: The row number to attach the top of the item to.
4996  * @bottom_attach: The row number to attach the bottom of the item to.
4997  *
4998  * Adds a new #GtkMenuItem to a (table) menu. The number of 'cells' that
4999  * an item will occupy is specified by @left_attach, @right_attach,
5000  * @top_attach and @bottom_attach. These each represent the leftmost,
5001  * rightmost, uppermost and lower column and row numbers of the table.
5002  * (Columns and rows are indexed from zero).
5003  *
5004  * Note that this function is not related to gtk_menu_detach().
5005  *
5006  * Since: 2.4
5007  **/
5008 void
5009 gtk_menu_attach (GtkMenu   *menu,
5010                  GtkWidget *child,
5011                  guint      left_attach,
5012                  guint      right_attach,
5013                  guint      top_attach,
5014                  guint      bottom_attach)
5015 {
5016   GtkMenuShell *menu_shell;
5017   
5018   g_return_if_fail (GTK_IS_MENU (menu));
5019   g_return_if_fail (GTK_IS_MENU_ITEM (child));
5020   g_return_if_fail (child->parent == NULL || 
5021                     child->parent == GTK_WIDGET (menu));
5022   g_return_if_fail (left_attach < right_attach);
5023   g_return_if_fail (top_attach < bottom_attach);
5024
5025   menu_shell = GTK_MENU_SHELL (menu);
5026   
5027   if (!child->parent)
5028     {
5029       AttachInfo *ai = get_attach_info (child);
5030       
5031       ai->left_attach = left_attach;
5032       ai->right_attach = right_attach;
5033       ai->top_attach = top_attach;
5034       ai->bottom_attach = bottom_attach;
5035       
5036       menu_shell->children = g_list_append (menu_shell->children, child);
5037
5038       gtk_widget_set_parent (child, GTK_WIDGET (menu));
5039
5040       menu_queue_resize (menu);
5041     }
5042   else
5043     {
5044       gtk_container_child_set (GTK_CONTAINER (child->parent), child,
5045                                "left-attach",   left_attach,
5046                                "right-attach",  right_attach,
5047                                "top-attach",    top_attach,
5048                                "bottom-attach", bottom_attach,
5049                                NULL);
5050     }
5051 }
5052
5053 static gint
5054 gtk_menu_get_popup_delay (GtkMenuShell *menu_shell)
5055 {
5056   gint popup_delay;
5057
5058   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
5059                 "gtk-menu-popup-delay", &popup_delay,
5060                 NULL);
5061
5062   return popup_delay;
5063 }
5064
5065 static GtkWidget *
5066 find_child_containing (GtkMenuShell *menu_shell,
5067                        int           left,
5068                        int           right,
5069                        int           top,
5070                        int           bottom)
5071 {
5072   GList *list;
5073
5074   /* find a child which includes the area given by
5075    * left, right, top, bottom.
5076    */
5077
5078   for (list = menu_shell->children; list; list = list->next)
5079     {
5080       gint l, r, t, b;
5081
5082       if (!_gtk_menu_item_is_selectable (list->data))
5083         continue;
5084
5085       get_effective_child_attach (list->data, &l, &r, &t, &b);
5086
5087       if (l <= left && right <= r
5088           && t <= top && bottom <= b)
5089         return GTK_WIDGET (list->data);
5090     }
5091
5092   return NULL;
5093 }
5094
5095 static void
5096 gtk_menu_move_current (GtkMenuShell         *menu_shell,
5097                        GtkMenuDirectionType  direction)
5098 {
5099   GtkMenu *menu = GTK_MENU (menu_shell);
5100   gint i;
5101   gint l, r, t, b;
5102   GtkWidget *match = NULL;
5103
5104   if (gtk_widget_get_direction (GTK_WIDGET (menu_shell)) == GTK_TEXT_DIR_RTL)
5105     {
5106       switch (direction)
5107         {
5108         case GTK_MENU_DIR_CHILD:
5109           direction = GTK_MENU_DIR_PARENT;
5110           break;
5111         case GTK_MENU_DIR_PARENT:
5112           direction = GTK_MENU_DIR_CHILD;
5113           break;
5114         default: ;
5115         }
5116     }
5117
5118   /* use special table menu key bindings */
5119   if (menu_shell->active_menu_item && gtk_menu_get_n_columns (menu) > 1)
5120     {
5121       get_effective_child_attach (menu_shell->active_menu_item, &l, &r, &t, &b);
5122
5123       if (direction == GTK_MENU_DIR_NEXT)
5124         {
5125           for (i = b; i < gtk_menu_get_n_rows (menu); i++)
5126             {
5127               match = find_child_containing (menu_shell, l, l + 1, i, i + 1);
5128               if (match)
5129                 break;
5130             }
5131
5132           if (!match)
5133             {
5134               /* wrap around */
5135               for (i = 0; i < t; i++)
5136                 {
5137                   match = find_child_containing (menu_shell,
5138                                                  l, l + 1, i, i + 1);
5139                   if (match)
5140                     break;
5141                 }
5142             }
5143         }
5144       else if (direction == GTK_MENU_DIR_PREV)
5145         {
5146           for (i = t; i > 0; i--)
5147             {
5148               match = find_child_containing (menu_shell, l, l + 1, i - 1, i);
5149               if (match)
5150                 break;
5151             }
5152
5153           if (!match)
5154             {
5155               /* wrap around */
5156               for (i = gtk_menu_get_n_rows (menu); i > b; i--)
5157                 {
5158                   match = find_child_containing (menu_shell,
5159                                                  l, l + 1, i - 1, i);
5160                   if (match)
5161                     break;
5162                 }
5163             }
5164         }
5165       else if (direction == GTK_MENU_DIR_PARENT)
5166         {
5167           /* we go one left if possible */
5168           if (l > 0)
5169             match = find_child_containing (menu_shell, l - 1, l, t, t + 1);
5170
5171           if (!match)
5172             {
5173               GtkWidget *parent = menu_shell->parent_menu_shell;
5174
5175               if (!parent
5176                   || g_list_length (GTK_MENU_SHELL (parent)->children) <= 1)
5177                 match = menu_shell->active_menu_item;
5178             }
5179         }
5180       else if (direction == GTK_MENU_DIR_CHILD)
5181         {
5182           /* we go one right if possible */
5183           if (r < gtk_menu_get_n_columns (menu))
5184             match = find_child_containing (menu_shell, r, r + 1, t, t + 1);
5185
5186           if (!match)
5187             {
5188               GtkWidget *parent = menu_shell->parent_menu_shell;
5189
5190               if (! GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu &&
5191                   (!parent ||
5192                    g_list_length (GTK_MENU_SHELL (parent)->children) <= 1))
5193                 match = menu_shell->active_menu_item;
5194             }
5195         }
5196
5197       if (match)
5198         {
5199           gtk_menu_shell_select_item (menu_shell, match);
5200           return;
5201         }
5202     }
5203
5204   GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->move_current (menu_shell, direction);
5205 }
5206
5207 static gint
5208 get_visible_size (GtkMenu *menu)
5209 {
5210   GtkWidget *widget = GTK_WIDGET (menu);
5211   GtkContainer *container = GTK_CONTAINER (menu);
5212   
5213   gint menu_height = (widget->allocation.height
5214                       - 2 * (container->border_width
5215                              + widget->style->ythickness));
5216
5217   if (!menu->tearoff_active)
5218     {
5219       GtkBorder arrow_border;
5220
5221       get_arrows_border (menu, &arrow_border);
5222       menu_height -= arrow_border.top;
5223       menu_height -= arrow_border.bottom;
5224     }
5225   
5226   return menu_height;
5227 }
5228
5229 /* Find the sensitive on-screen child containing @y, or if none,
5230  * the nearest selectable onscreen child. (%NULL if none)
5231  */
5232 static GtkWidget *
5233 child_at (GtkMenu *menu,
5234           gint     y)
5235 {
5236   GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
5237   GtkWidget *child = NULL;
5238   gint child_offset = 0;
5239   GList *children;
5240   gint menu_height;
5241   gint lower, upper;            /* Onscreen bounds */
5242
5243   menu_height = get_visible_size (menu);
5244   lower = menu->scroll_offset;
5245   upper = menu->scroll_offset + menu_height;
5246   
5247   for (children = menu_shell->children; children; children = children->next)
5248     {
5249       if (gtk_widget_get_visible (children->data))
5250         {
5251           GtkRequisition child_requisition;
5252
5253           gtk_widget_size_request (children->data, &child_requisition);
5254
5255           if (_gtk_menu_item_is_selectable (children->data) &&
5256               child_offset >= lower &&
5257               child_offset + child_requisition.height <= upper)
5258             {
5259               child = children->data;
5260               
5261               if (child_offset + child_requisition.height > y &&
5262                   !GTK_IS_TEAROFF_MENU_ITEM (child))
5263                 return child;
5264             }
5265       
5266           child_offset += child_requisition.height;
5267         }
5268     }
5269
5270   return child;
5271 }
5272
5273 static gint
5274 get_menu_height (GtkMenu *menu)
5275 {
5276   gint height;
5277   GtkWidget *widget = GTK_WIDGET (menu);
5278
5279   height = widget->requisition.height;
5280   height -= (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2;
5281
5282   if (!menu->tearoff_active)
5283     {
5284       GtkBorder arrow_border;
5285
5286       get_arrows_border (menu, &arrow_border);
5287       height -= arrow_border.top;
5288       height -= arrow_border.bottom;
5289     }
5290
5291   return height;
5292 }
5293
5294 static void
5295 gtk_menu_real_move_scroll (GtkMenu       *menu,
5296                            GtkScrollType  type)
5297 {
5298   gint page_size = get_visible_size (menu);
5299   gint end_position = get_menu_height (menu);
5300   GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
5301   
5302   switch (type)
5303     {
5304     case GTK_SCROLL_PAGE_UP:
5305     case GTK_SCROLL_PAGE_DOWN:
5306       {
5307         gint old_offset;
5308         gint new_offset;
5309         gint child_offset = 0;
5310         gboolean old_upper_arrow_visible;
5311         gint step;
5312
5313         if (type == GTK_SCROLL_PAGE_UP)
5314           step = - page_size;
5315         else
5316           step = page_size;
5317
5318         if (menu_shell->active_menu_item)
5319           {
5320             gint child_height;
5321             
5322             compute_child_offset (menu, menu_shell->active_menu_item,
5323                                   &child_offset, &child_height, NULL);
5324             child_offset += child_height / 2;
5325           }
5326
5327         menu_shell->ignore_enter = TRUE;
5328         old_upper_arrow_visible = menu->upper_arrow_visible && !menu->tearoff_active;
5329         old_offset = menu->scroll_offset;
5330
5331         new_offset = menu->scroll_offset + step;
5332         new_offset = CLAMP (new_offset, 0, end_position - page_size);
5333
5334         gtk_menu_scroll_to (menu, new_offset);
5335         
5336         if (menu_shell->active_menu_item)
5337           {
5338             GtkWidget *new_child;
5339             gboolean new_upper_arrow_visible = menu->upper_arrow_visible && !menu->tearoff_active;
5340             GtkBorder arrow_border;
5341
5342             get_arrows_border (menu, &arrow_border);
5343
5344             if (menu->scroll_offset != old_offset)
5345               step = menu->scroll_offset - old_offset;
5346
5347             step -= (new_upper_arrow_visible - old_upper_arrow_visible) * arrow_border.top;
5348
5349             new_child = child_at (menu, child_offset + step);
5350             if (new_child)
5351               gtk_menu_shell_select_item (menu_shell, new_child);
5352           }
5353       }
5354       break;
5355     case GTK_SCROLL_START:
5356       /* Ignore the enter event we might get if the pointer is on the menu
5357        */
5358       menu_shell->ignore_enter = TRUE;
5359       gtk_menu_scroll_to (menu, 0);
5360       gtk_menu_shell_select_first (menu_shell, TRUE);
5361       break;
5362     case GTK_SCROLL_END:
5363       /* Ignore the enter event we might get if the pointer is on the menu
5364        */
5365       menu_shell->ignore_enter = TRUE;
5366       gtk_menu_scroll_to (menu, end_position - page_size);
5367       _gtk_menu_shell_select_last (menu_shell, TRUE);
5368       break;
5369     default:
5370       break;
5371     }
5372 }
5373
5374
5375 /**
5376  * gtk_menu_set_monitor:
5377  * @menu: a #GtkMenu
5378  * @monitor_num: the number of the monitor on which the menu should
5379  *    be popped up
5380  * 
5381  * Informs GTK+ on which monitor a menu should be popped up. 
5382  * See gdk_screen_get_monitor_geometry().
5383  *
5384  * This function should be called from a #GtkMenuPositionFunc if the
5385  * menu should not appear on the same monitor as the pointer. This 
5386  * information can't be reliably inferred from the coordinates returned
5387  * by a #GtkMenuPositionFunc, since, for very long menus, these coordinates 
5388  * may extend beyond the monitor boundaries or even the screen boundaries. 
5389  *
5390  * Since: 2.4
5391  **/
5392 void
5393 gtk_menu_set_monitor (GtkMenu *menu,
5394                       gint     monitor_num)
5395 {
5396   GtkMenuPrivate *priv;
5397   g_return_if_fail (GTK_IS_MENU (menu));
5398
5399   priv = gtk_menu_get_private (menu);
5400   
5401   priv->monitor_num = monitor_num;
5402 }
5403
5404 /**
5405  * gtk_menu_get_monitor:
5406  * @menu: a #GtkMenu
5407  *
5408  * Retrieves the number of the monitor on which to show the menu.
5409  *
5410  * Returns: the number of the monitor on which the menu should
5411  *    be popped up or -1, if no monitor has been set
5412  *
5413  * Since: 2.14
5414  **/
5415 gint
5416 gtk_menu_get_monitor (GtkMenu *menu)
5417 {
5418   GtkMenuPrivate *priv;
5419   g_return_val_if_fail (GTK_IS_MENU (menu), -1);
5420
5421   priv = gtk_menu_get_private (menu);
5422   
5423   return priv->monitor_num;
5424 }
5425
5426 /**
5427  * gtk_menu_get_for_attach_widget:
5428  * @widget: a #GtkWidget
5429  *
5430  * Returns a list of the menus which are attached to this widget.
5431  * This list is owned by GTK+ and must not be modified.
5432  *
5433  * Return value: (element-type GtkWidget) (transfer none): the list of menus attached to his widget.
5434  *
5435  * Since: 2.6
5436  **/
5437 GList*
5438 gtk_menu_get_for_attach_widget (GtkWidget *widget)
5439 {
5440   GList *list;
5441   
5442   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
5443   
5444   list = g_object_get_data (G_OBJECT (widget), ATTACHED_MENUS);
5445   return list;
5446 }
5447
5448 static void
5449 gtk_menu_grab_notify (GtkWidget *widget,
5450                       gboolean   was_grabbed)
5451 {
5452   GtkWidget *toplevel;
5453   GtkWindowGroup *group;
5454   GtkWidget *grab;
5455   GdkDevice *pointer;
5456
5457   pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (widget));
5458
5459   if (!pointer ||
5460       !gtk_widget_device_is_shadowed (widget, pointer))
5461     return;
5462
5463   toplevel = gtk_widget_get_toplevel (widget);
5464
5465   if (!GTK_IS_WINDOW (toplevel))
5466     return;
5467
5468   group = gtk_window_get_group (GTK_WINDOW (toplevel));
5469   grab = gtk_window_group_get_current_device_grab (group, pointer);
5470
5471   if (GTK_MENU_SHELL (widget)->active && !GTK_IS_MENU_SHELL (grab))
5472     gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
5473 }
5474
5475 /**
5476  * gtk_menu_set_reserve_toggle_size:
5477  * @menu: a #GtkMenu
5478  * @reserve_toggle_size: whether to reserve size for toggles
5479  *
5480  * Sets whether the menu should reserve space for drawing toggles 
5481  * or icons, regardless of their actual presence.
5482  *
5483  * Since: 2.18
5484  */
5485 void
5486 gtk_menu_set_reserve_toggle_size (GtkMenu  *menu,
5487                                   gboolean  reserve_toggle_size)
5488 {
5489   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
5490   gboolean no_toggle_size;
5491   
5492   no_toggle_size = !reserve_toggle_size;
5493
5494   if (priv->no_toggle_size != no_toggle_size)
5495     {
5496       priv->no_toggle_size = no_toggle_size;
5497
5498       g_object_notify (G_OBJECT (menu), "reserve-toggle-size");
5499     }
5500 }
5501
5502 /**
5503  * gtk_menu_get_reserve_toggle_size:
5504  * @menu: a #GtkMenu
5505  *
5506  * Returns whether the menu reserves space for toggles and
5507  * icons, regardless of their actual presence.
5508  *
5509  * Returns: Whether the menu reserves toggle space
5510  *
5511  * Since: 2.18
5512  */
5513 gboolean
5514 gtk_menu_get_reserve_toggle_size (GtkMenu *menu)
5515 {
5516   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
5517
5518   return !priv->no_toggle_size;
5519 }
5520
5521 #define __GTK_MENU_C__
5522 #include "gtkaliasdef.c"