]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenu.c
in gtk_menu_set_submenu_navigation_region do stop_navigating_submenu, to
[~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 #include <ctype.h>
28 #include "gdk/gdkkeysyms.h"
29 #include "gtkbindings.h"
30 #include "gtklabel.h"
31 #include "gtkmain.h"
32 #include "gtkmenu.h"
33 #include "gtkmenuitem.h"
34 #include "gtksignal.h"
35 #include "gtkwindow.h"
36
37
38 #define MENU_ITEM_CLASS(w)   GTK_MENU_ITEM_GET_CLASS (w)
39 #define MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
40
41 #define SUBMENU_NAV_REGION_PADDING 2
42 #define SUBMENU_NAV_HYSTERESIS_TIMEOUT 333
43
44 typedef struct _GtkMenuAttachData       GtkMenuAttachData;
45
46 struct _GtkMenuAttachData
47 {
48   GtkWidget *attach_widget;
49   GtkMenuDetachFunc detacher;
50 };
51
52
53 static void     gtk_menu_class_init    (GtkMenuClass      *klass);
54 static void     gtk_menu_init          (GtkMenu           *menu);
55 static void     gtk_menu_destroy       (GtkObject         *object);
56 static void     gtk_menu_realize       (GtkWidget         *widget);
57 static void     gtk_menu_size_request  (GtkWidget         *widget,
58                                         GtkRequisition    *requisition);
59 static void     gtk_menu_size_allocate (GtkWidget         *widget,
60                                         GtkAllocation     *allocation);
61 static void     gtk_menu_paint         (GtkWidget         *widget);
62 static void     gtk_menu_draw          (GtkWidget         *widget,
63                                         GdkRectangle      *area);
64 static gboolean gtk_menu_expose        (GtkWidget         *widget,
65                                         GdkEventExpose    *event);
66 static gboolean gtk_menu_key_press     (GtkWidget         *widget,
67                                         GdkEventKey       *event);
68 static gboolean gtk_menu_motion_notify (GtkWidget         *widget,
69                                         GdkEventMotion    *event);
70 static gboolean gtk_menu_enter_notify  (GtkWidget         *widget,
71                                         GdkEventCrossing  *event); 
72 static gboolean gtk_menu_leave_notify  (GtkWidget         *widget,
73                                         GdkEventCrossing  *event);
74
75 static void     gtk_menu_stop_navigating_submenu       (GtkMenu          *menu);
76 static gboolean gtk_menu_stop_navigating_submenu_cb    (gpointer          user_data);
77 static gboolean gtk_menu_navigating_submenu            (GtkMenu          *menu,
78                                                         gint              event_x,
79                                                         gint              event_y);
80 static void     gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
81                                                         GtkMenuItem      *menu_item,
82                                                         GdkEventCrossing *event);
83  
84 static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
85 static void gtk_menu_show_all       (GtkWidget         *widget);
86 static void gtk_menu_hide_all       (GtkWidget         *widget);
87 static void gtk_menu_position       (GtkMenu           *menu);
88 static void gtk_menu_reparent       (GtkMenu           *menu, 
89                                      GtkWidget         *new_parent, 
90                                      gboolean           unrealize);
91
92 static GtkMenuShellClass *parent_class = NULL;
93 static const gchar       *attach_data_key = "gtk-menu-attach-data";
94 static GQuark             quark_uline_accel_group = 0;
95
96 GtkType
97 gtk_menu_get_type (void)
98 {
99   static GtkType menu_type = 0;
100   
101   if (!menu_type)
102     {
103       static const GtkTypeInfo menu_info =
104       {
105         "GtkMenu",
106         sizeof (GtkMenu),
107         sizeof (GtkMenuClass),
108         (GtkClassInitFunc) gtk_menu_class_init,
109         (GtkObjectInitFunc) gtk_menu_init,
110         /* reserved_1 */ NULL,
111         /* reserved_2 */ NULL,
112         (GtkClassInitFunc) NULL,
113       };
114       
115       menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
116     }
117   
118   return menu_type;
119 }
120
121 static void
122 gtk_menu_class_init (GtkMenuClass *class)
123 {
124   GtkObjectClass *object_class;
125   GtkWidgetClass *widget_class;
126   GtkContainerClass *container_class;
127   GtkMenuShellClass *menu_shell_class;
128
129   GtkBindingSet *binding_set;
130   
131   object_class = (GtkObjectClass*) class;
132   widget_class = (GtkWidgetClass*) class;
133   container_class = (GtkContainerClass*) class;
134   menu_shell_class = (GtkMenuShellClass*) class;
135   parent_class = gtk_type_class (gtk_menu_shell_get_type ());
136   
137   object_class->destroy = gtk_menu_destroy;
138   
139   widget_class->realize = gtk_menu_realize;
140   widget_class->draw = gtk_menu_draw;
141   widget_class->size_request = gtk_menu_size_request;
142   widget_class->size_allocate = gtk_menu_size_allocate;
143   widget_class->expose_event = gtk_menu_expose;
144   widget_class->key_press_event = gtk_menu_key_press;
145   widget_class->motion_notify_event = gtk_menu_motion_notify;
146   widget_class->show_all = gtk_menu_show_all;
147   widget_class->hide_all = gtk_menu_hide_all;
148   widget_class->enter_notify_event = gtk_menu_enter_notify;
149   widget_class->leave_notify_event = gtk_menu_leave_notify;
150   
151   menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
152   menu_shell_class->deactivate = gtk_menu_deactivate;
153
154   binding_set = gtk_binding_set_by_class (class);
155   gtk_binding_entry_add_signal (binding_set,
156                                 GDK_Up, 0,
157                                 "move_current", 1,
158                                 GTK_TYPE_MENU_DIRECTION_TYPE,
159                                 GTK_MENU_DIR_PREV);
160   gtk_binding_entry_add_signal (binding_set,
161                                 GDK_Down, 0,
162                                 "move_current", 1,
163                                 GTK_TYPE_MENU_DIRECTION_TYPE,
164                                 GTK_MENU_DIR_NEXT);
165   gtk_binding_entry_add_signal (binding_set,
166                                 GDK_Left, 0,
167                                 "move_current", 1,
168                                 GTK_TYPE_MENU_DIRECTION_TYPE,
169                                 GTK_MENU_DIR_PARENT);
170   gtk_binding_entry_add_signal (binding_set,
171                                 GDK_Right, 0,
172                                 "move_current", 1,
173                                 GTK_TYPE_MENU_DIRECTION_TYPE,
174                                 GTK_MENU_DIR_CHILD);
175 }
176
177 static gboolean
178 gtk_menu_window_event (GtkWidget *window,
179                        GdkEvent  *event,
180                        GtkWidget *menu)
181 {
182   gboolean handled = FALSE;
183
184   gtk_widget_ref (window);
185   gtk_widget_ref (menu);
186
187   switch (event->type)
188     {
189     case GDK_KEY_PRESS:
190     case GDK_KEY_RELEASE:
191       gtk_widget_event (menu, event);
192       handled = TRUE;
193       break;
194     default:
195       break;
196     }
197
198   gtk_widget_unref (window);
199   gtk_widget_unref (menu);
200
201   return handled;
202 }
203
204 static void
205 gtk_menu_init (GtkMenu *menu)
206 {
207   menu->parent_menu_item = NULL;
208   menu->old_active_menu_item = NULL;
209   menu->accel_group = NULL;
210   menu->position_func = NULL;
211   menu->position_func_data = NULL;
212
213   menu->toplevel = gtk_widget_new (GTK_TYPE_WINDOW,
214                                    "type", GTK_WINDOW_POPUP,
215                                    "signal::event", gtk_menu_window_event, menu,
216                                    "signal::destroy", gtk_widget_destroyed, &menu->toplevel,
217                                    "child", menu,
218                                    NULL);
219   gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
220                          FALSE, FALSE, TRUE);
221
222   /* Refloat the menu, so that reference counting for the menu isn't
223    * affected by it being a child of the toplevel
224    */
225   GTK_WIDGET_SET_FLAGS (menu, GTK_FLOATING);
226   menu->needs_destruction_ref_count = TRUE;
227
228   menu->tearoff_window = NULL;
229   menu->torn_off = FALSE;
230
231   MENU_NEEDS_RESIZE (menu) = TRUE;
232 }
233
234 static void
235 gtk_menu_destroy (GtkObject *object)
236 {
237   GtkMenu *menu;
238   GtkMenuAttachData *data;
239   
240   g_return_if_fail (GTK_IS_MENU (object));
241
242   menu = GTK_MENU (object);
243   
244   data = gtk_object_get_data (object, attach_data_key);
245   if (data)
246     gtk_menu_detach (menu);
247   
248   gtk_menu_stop_navigating_submenu (menu);
249
250   gtk_menu_set_accel_group (menu, NULL);
251
252   if (menu->old_active_menu_item)
253     {
254       gtk_widget_unref (menu->old_active_menu_item);
255       menu->old_active_menu_item = NULL;
256     }
257
258   /* Add back the reference count for being a child */
259   if (menu->needs_destruction_ref_count)
260     {
261       menu->needs_destruction_ref_count = FALSE;
262       gtk_object_ref (object);
263     }
264   
265   if (menu->toplevel)
266     gtk_widget_destroy (menu->toplevel);
267   if (menu->tearoff_window)
268     gtk_widget_destroy (menu->tearoff_window);
269
270   GTK_OBJECT_CLASS (parent_class)->destroy (object);
271 }
272
273
274 void
275 gtk_menu_attach_to_widget (GtkMenu             *menu,
276                            GtkWidget           *attach_widget,
277                            GtkMenuDetachFunc    detacher)
278 {
279   GtkMenuAttachData *data;
280   
281   g_return_if_fail (menu != NULL);
282   g_return_if_fail (GTK_IS_MENU (menu));
283   g_return_if_fail (attach_widget != NULL);
284   g_return_if_fail (GTK_IS_WIDGET (attach_widget));
285   g_return_if_fail (detacher != NULL);
286   
287   /* keep this function in sync with gtk_widget_set_parent()
288    */
289   
290   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
291   if (data)
292     {
293       g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
294                  gtk_type_name (GTK_OBJECT_TYPE (data->attach_widget)));
295       return;
296     }
297   
298   gtk_object_ref (GTK_OBJECT (menu));
299   gtk_object_sink (GTK_OBJECT (menu));
300   
301   data = g_new (GtkMenuAttachData, 1);
302   data->attach_widget = attach_widget;
303   data->detacher = detacher;
304   gtk_object_set_data (GTK_OBJECT (menu), attach_data_key, data);
305   
306   if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
307     gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
308   
309   /* we don't need to set the style here, since
310    * we are a toplevel widget.
311    */
312 }
313
314 GtkWidget*
315 gtk_menu_get_attach_widget (GtkMenu *menu)
316 {
317   GtkMenuAttachData *data;
318   
319   g_return_val_if_fail (menu != NULL, NULL);
320   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
321   
322   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
323   if (data)
324     return data->attach_widget;
325   return NULL;
326 }
327
328 void
329 gtk_menu_detach (GtkMenu *menu)
330 {
331   GtkMenuAttachData *data;
332   
333   g_return_if_fail (menu != NULL);
334   g_return_if_fail (GTK_IS_MENU (menu));
335   
336   /* keep this function in sync with gtk_widget_unparent()
337    */
338   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
339   if (!data)
340     {
341       g_warning ("gtk_menu_detach(): menu is not attached");
342       return;
343     }
344   gtk_object_remove_data (GTK_OBJECT (menu), attach_data_key);
345   
346   data->detacher (data->attach_widget, menu);
347   
348   if (GTK_WIDGET_REALIZED (menu))
349     gtk_widget_unrealize (GTK_WIDGET (menu));
350   
351   g_free (data);
352   
353   gtk_widget_unref (GTK_WIDGET (menu));
354 }
355
356 GtkWidget*
357 gtk_menu_new (void)
358 {
359   return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
360 }
361
362 void
363 gtk_menu_append (GtkMenu   *menu,
364                  GtkWidget *child)
365 {
366   gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
367 }
368
369 void
370 gtk_menu_prepend (GtkMenu   *menu,
371                   GtkWidget *child)
372 {
373   gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), child);
374 }
375
376 void
377 gtk_menu_insert (GtkMenu   *menu,
378                  GtkWidget *child,
379                  gint       position)
380 {
381   gtk_menu_shell_insert (GTK_MENU_SHELL (menu), child, position);
382 }
383
384 static void
385 gtk_menu_tearoff_bg_copy (GtkMenu *menu)
386 {
387   GtkWidget *widget;
388
389   widget = GTK_WIDGET (menu);
390
391   if (menu->torn_off)
392     {
393       GdkPixmap *pixmap;
394       GdkGC *gc;
395       GdkGCValues gc_values;
396       
397       gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
398       gc = gdk_gc_new_with_values (widget->window,
399                                    &gc_values, GDK_GC_SUBWINDOW);
400       
401       pixmap = gdk_pixmap_new (widget->window,
402                                widget->requisition.width,
403                                widget->requisition.height,
404                                -1);
405
406       gdk_draw_pixmap (pixmap, gc,
407                        widget->window,
408                        0, 0, 0, 0, -1, -1);
409       gdk_gc_unref (gc);
410       
411       gtk_widget_set_usize (menu->tearoff_window,
412                             widget->requisition.width,
413                             widget->requisition.height);
414       
415       gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
416       gdk_pixmap_unref (pixmap);
417     }
418 }
419
420 void
421 gtk_menu_popup (GtkMenu             *menu,
422                 GtkWidget           *parent_menu_shell,
423                 GtkWidget           *parent_menu_item,
424                 GtkMenuPositionFunc  func,
425                 gpointer             data,
426                 guint                button,
427                 guint32              activate_time)
428 {
429   GtkWidget *widget;
430   GtkWidget *xgrab_shell;
431   GtkWidget *parent;
432   GdkEvent *current_event;
433   GtkMenuShell *menu_shell;
434   
435   g_return_if_fail (menu != NULL);
436   g_return_if_fail (GTK_IS_MENU (menu));
437   
438   widget = GTK_WIDGET (menu);
439   menu_shell = GTK_MENU_SHELL (menu);
440   
441   menu_shell->parent_menu_shell = parent_menu_shell;
442   menu_shell->active = TRUE;
443   menu_shell->button = button;
444
445   /* If we are popping up the menu from something other than, a button
446    * press then, as a heuristic, we ignore enter events for the menu
447    * until we get a MOTION_NOTIFY.  
448    */
449
450   current_event = gtk_get_current_event();
451   if (current_event)
452     {
453       if ((current_event->type != GDK_BUTTON_PRESS) &&
454           (current_event->type != GDK_ENTER_NOTIFY))
455         menu_shell->ignore_enter = TRUE;
456       gdk_event_free (current_event);
457     }
458
459   if (menu->torn_off)
460     {
461       gtk_menu_tearoff_bg_copy (menu);
462
463       /* We force an unrealize here so that we don't trigger redrawing/
464        * clearing code - we just want to reveal our backing pixmap.
465        */
466       gtk_menu_reparent (menu, menu->toplevel, TRUE);
467     }
468   
469   menu->parent_menu_item = parent_menu_item;
470   menu->position_func = func;
471   menu->position_func_data = data;
472   menu_shell->activate_time = activate_time;
473
474   gtk_menu_position (menu);
475
476   /* We need to show the menu _here_ because code expects to be
477    * able to tell if the menu is onscreen by looking at the
478    * GTK_WIDGET_VISIBLE (menu)
479    */
480   gtk_widget_show (GTK_WIDGET (menu));
481   gtk_widget_show (menu->toplevel);
482   
483   /* Find the last viewable ancestor, and make an X grab on it
484    */
485   parent = GTK_WIDGET (menu);
486   xgrab_shell = NULL;
487   while (parent)
488     {
489       gboolean viewable = TRUE;
490       GtkWidget *tmp = parent;
491       
492       while (tmp)
493         {
494           if (!GTK_WIDGET_MAPPED (tmp))
495             {
496               viewable = FALSE;
497               break;
498             }
499           tmp = tmp->parent;
500         }
501       
502       if (viewable)
503         xgrab_shell = parent;
504       
505       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
506     }
507   
508   if (xgrab_shell && (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
509     {
510       GdkCursor *cursor = gdk_cursor_new (GDK_ARROW);
511
512       if ((gdk_pointer_grab (xgrab_shell->window, TRUE,
513                              GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
514                              GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
515                              GDK_POINTER_MOTION_MASK,
516                              NULL, cursor, activate_time) == 0))
517         {
518           if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
519                               activate_time) == 0)
520             GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
521           else
522             {
523               gdk_pointer_ungrab (activate_time);
524             }
525         }
526
527       gdk_cursor_destroy (cursor);
528     }
529   
530   gtk_grab_add (GTK_WIDGET (menu));
531 }
532
533 void
534 gtk_menu_popdown (GtkMenu *menu)
535 {
536   GtkMenuShell *menu_shell;
537   
538   g_return_if_fail (menu != NULL);
539   g_return_if_fail (GTK_IS_MENU (menu));
540   
541   menu_shell = GTK_MENU_SHELL (menu);
542   
543   menu_shell->parent_menu_shell = NULL;
544   menu_shell->active = FALSE;
545   menu_shell->ignore_enter = FALSE;
546   
547   gtk_menu_stop_navigating_submenu (menu);
548   
549   if (menu_shell->active_menu_item)
550     {
551       if (menu->old_active_menu_item)
552         gtk_widget_unref (menu->old_active_menu_item);
553       menu->old_active_menu_item = menu_shell->active_menu_item;
554       gtk_widget_ref (menu->old_active_menu_item);
555     }
556
557   gtk_menu_shell_deselect (menu_shell);
558   
559   /* The X Grab, if present, will automatically be removed when we hide
560    * the window */
561   gtk_widget_hide (menu->toplevel);
562
563   if (menu->torn_off)
564     {
565       if (GTK_BIN (menu->toplevel)->child) 
566         {
567           gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
568         } 
569       else
570         {
571           /* We popped up the menu from the tearoff, so we need to 
572            * release the grab - we aren't actually hiding the menu.
573            */
574           if (menu_shell->have_xgrab)
575             {
576               gdk_pointer_ungrab (GDK_CURRENT_TIME);
577               gdk_keyboard_ungrab (GDK_CURRENT_TIME);
578             }
579         }
580     }
581   else
582     gtk_widget_hide (GTK_WIDGET (menu));
583
584   menu_shell->have_xgrab = FALSE;
585   gtk_grab_remove (GTK_WIDGET (menu));
586 }
587
588 GtkWidget*
589 gtk_menu_get_active (GtkMenu *menu)
590 {
591   GtkWidget *child;
592   GList *children;
593   
594   g_return_val_if_fail (menu != NULL, NULL);
595   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
596   
597   if (!menu->old_active_menu_item)
598     {
599       child = NULL;
600       children = GTK_MENU_SHELL (menu)->children;
601       
602       while (children)
603         {
604           child = children->data;
605           children = children->next;
606           
607           if (GTK_BIN (child)->child)
608             break;
609           child = NULL;
610         }
611       
612       menu->old_active_menu_item = child;
613       if (menu->old_active_menu_item)
614         gtk_widget_ref (menu->old_active_menu_item);
615     }
616   
617   return menu->old_active_menu_item;
618 }
619
620 void
621 gtk_menu_set_active (GtkMenu *menu,
622                      guint    index)
623 {
624   GtkWidget *child;
625   GList *tmp_list;
626   
627   g_return_if_fail (menu != NULL);
628   g_return_if_fail (GTK_IS_MENU (menu));
629   
630   tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
631   if (tmp_list)
632     {
633       child = tmp_list->data;
634       if (GTK_BIN (child)->child)
635         {
636           if (menu->old_active_menu_item)
637             gtk_widget_unref (menu->old_active_menu_item);
638           menu->old_active_menu_item = child;
639           gtk_widget_ref (menu->old_active_menu_item);
640         }
641     }
642 }
643
644 void
645 gtk_menu_set_accel_group (GtkMenu       *menu,
646                           GtkAccelGroup *accel_group)
647 {
648   g_return_if_fail (GTK_IS_MENU (menu));
649   
650   if (menu->accel_group != accel_group)
651     {
652       if (menu->accel_group)
653         gtk_accel_group_unref (menu->accel_group);
654       menu->accel_group = accel_group;
655       if (menu->accel_group)
656         gtk_accel_group_ref (menu->accel_group);
657     }
658 }
659
660 GtkAccelGroup*
661 gtk_menu_get_accel_group (GtkMenu *menu)
662 {
663   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
664
665   return menu->accel_group;
666 }
667
668 GtkAccelGroup*
669 gtk_menu_ensure_uline_accel_group (GtkMenu *menu)
670 {
671   GtkAccelGroup *accel_group;
672
673   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
674
675   if (!quark_uline_accel_group)
676     quark_uline_accel_group = g_quark_from_static_string ("GtkMenu-uline-accel-group");
677
678   accel_group = gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group);
679   if (!accel_group)
680     {
681       accel_group = gtk_accel_group_new ();
682       gtk_accel_group_attach (accel_group, GTK_OBJECT (menu));
683       gtk_object_set_data_by_id_full (GTK_OBJECT (menu),
684                                       quark_uline_accel_group,
685                                       accel_group,
686                                       (GtkDestroyNotify) gtk_accel_group_unref);
687     }
688
689   return accel_group;
690 }
691
692 GtkAccelGroup*
693 gtk_menu_get_uline_accel_group (GtkMenu *menu)
694 {
695   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
696
697   return gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group);
698 }
699
700 void
701 gtk_menu_reposition (GtkMenu *menu)
702 {
703   g_return_if_fail (menu != NULL);
704   g_return_if_fail (GTK_IS_MENU (menu));
705
706   if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
707     gtk_menu_position (menu);
708 }
709
710
711 void       
712 gtk_menu_set_tearoff_state (GtkMenu  *menu,
713                             gboolean  torn_off)
714 {
715   g_return_if_fail (menu != NULL);
716   g_return_if_fail (GTK_IS_MENU (menu));
717
718   if (menu->torn_off != torn_off)
719     {
720       menu->torn_off = torn_off;
721       
722       if (menu->torn_off)
723         {
724           if (GTK_WIDGET_VISIBLE (menu))
725             gtk_menu_popdown (menu);
726
727           if (!menu->tearoff_window)
728             {
729               GtkWidget *attach_widget;
730               gchar *title;
731               
732               menu->tearoff_window = gtk_widget_new (GTK_TYPE_WINDOW,
733                                                      "type", GTK_WINDOW_TOPLEVEL,
734                                                      "signal::destroy", gtk_widget_destroyed, &menu->tearoff_window,
735                                                      NULL);
736               gtk_widget_set_app_paintable (menu->tearoff_window, TRUE);
737               gtk_signal_connect (GTK_OBJECT (menu->tearoff_window),  
738                                   "event",
739                                   GTK_SIGNAL_FUNC (gtk_menu_window_event), 
740                                   GTK_OBJECT (menu));
741               gtk_widget_realize (menu->tearoff_window);
742               
743               title = gtk_object_get_data (GTK_OBJECT (menu), "gtk-menu-title");
744               if (!title)
745                 {
746                   attach_widget = gtk_menu_get_attach_widget (menu);
747                   if (GTK_IS_MENU_ITEM (attach_widget))
748                     {
749                       GtkWidget *child = GTK_BIN (attach_widget)->child;
750                       if (GTK_IS_LABEL (child))
751                         gtk_label_get (GTK_LABEL (child), &title);
752                     }
753                 }
754
755               if (title)
756                 gdk_window_set_title (menu->tearoff_window->window, title);
757
758               gdk_window_set_decorations (menu->tearoff_window->window, 
759                                           GDK_DECOR_ALL |
760                                           GDK_DECOR_RESIZEH |
761                                           GDK_DECOR_MINIMIZE |
762                                           GDK_DECOR_MAXIMIZE);
763               gtk_window_set_policy (GTK_WINDOW (menu->tearoff_window),
764                                      FALSE, FALSE, TRUE);
765             }
766           gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
767
768           gtk_menu_position (menu);
769           
770           gtk_widget_show (GTK_WIDGET (menu));
771           gtk_widget_show (menu->tearoff_window);
772         }
773       else
774         {
775           gtk_widget_hide (menu->tearoff_window);
776           gtk_menu_reparent (menu, menu->toplevel, FALSE);
777         }
778     }
779 }
780
781 void       
782 gtk_menu_set_title (GtkMenu     *menu,
783                     const gchar *title)
784 {
785   g_return_if_fail (menu != NULL);
786   g_return_if_fail (GTK_IS_MENU (menu));
787
788   gtk_object_set_data_full (GTK_OBJECT (menu), "gtk-menu-title",
789                             g_strdup (title), (GtkDestroyNotify) g_free);
790 }
791
792 void
793 gtk_menu_reorder_child (GtkMenu   *menu,
794                         GtkWidget *child,
795                         gint       position)
796 {
797   GtkMenuShell *menu_shell;
798   g_return_if_fail (GTK_IS_MENU (menu));
799   g_return_if_fail (GTK_IS_MENU_ITEM (child));
800   menu_shell = GTK_MENU_SHELL (menu);
801   if (g_list_find (menu_shell->children, child))
802     {   
803       menu_shell->children = g_list_remove (menu_shell->children, child);
804       menu_shell->children = g_list_insert (menu_shell->children, child, position);   
805       if (GTK_WIDGET_VISIBLE (menu_shell))
806         gtk_widget_queue_resize (GTK_WIDGET (menu_shell));
807     }   
808 }
809
810 static void
811 gtk_menu_realize (GtkWidget *widget)
812 {
813   GdkWindowAttr attributes;
814   gint attributes_mask;
815   
816   g_return_if_fail (widget != NULL);
817   g_return_if_fail (GTK_IS_MENU (widget));
818   
819   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
820   
821   attributes.window_type = GDK_WINDOW_CHILD;
822   attributes.x = widget->allocation.x;
823   attributes.y = widget->allocation.y;
824   attributes.width = widget->allocation.width;
825   attributes.height = widget->allocation.height;
826   attributes.wclass = GDK_INPUT_OUTPUT;
827   attributes.visual = gtk_widget_get_visual (widget);
828   attributes.colormap = gtk_widget_get_colormap (widget);
829   attributes.event_mask = gtk_widget_get_events (widget);
830   attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK);
831   
832   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
833   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
834   gdk_window_set_user_data (widget->window, widget);
835   
836   widget->style = gtk_style_attach (widget->style, widget->window);
837   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
838   gtk_menu_paint(widget);
839 }
840
841 static void
842 gtk_menu_size_request (GtkWidget      *widget,
843                        GtkRequisition *requisition)
844 {
845   GtkMenu *menu;
846   GtkMenuShell *menu_shell;
847   GtkWidget *child;
848   GList *children;
849   guint max_toggle_size;
850   guint max_accel_width;
851   GtkRequisition child_requisition;
852   
853   g_return_if_fail (widget != NULL);
854   g_return_if_fail (GTK_IS_MENU (widget));
855   g_return_if_fail (requisition != NULL);
856   
857   menu = GTK_MENU (widget);
858   menu_shell = GTK_MENU_SHELL (widget);
859   
860   requisition->width = 0;
861   requisition->height = 0;
862   
863   max_toggle_size = 0;
864   max_accel_width = 0;
865   
866   children = menu_shell->children;
867   while (children)
868     {
869       child = children->data;
870       children = children->next;
871       
872       if (GTK_WIDGET_VISIBLE (child))
873         {
874           GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
875           gtk_widget_size_request (child, &child_requisition);
876           
877           requisition->width = MAX (requisition->width, child_requisition.width);
878           requisition->height += child_requisition.height;
879           
880           max_toggle_size = MAX (max_toggle_size, MENU_ITEM_CLASS (child)->toggle_size);
881           max_accel_width = MAX (max_accel_width, GTK_MENU_ITEM (child)->accelerator_width);
882         }
883     }
884   
885   requisition->width += max_toggle_size + max_accel_width;
886   requisition->width += (GTK_CONTAINER (menu)->border_width +
887                          widget->style->xthickness) * 2;
888   requisition->height += (GTK_CONTAINER (menu)->border_width +
889                           widget->style->ythickness) * 2;
890   
891   children = menu_shell->children;
892   while (children)
893     {
894       child = children->data;
895       children = children->next;
896       
897       GTK_MENU_ITEM (child)->toggle_size = max_toggle_size;
898     }
899 }
900
901 static void
902 gtk_menu_size_allocate (GtkWidget     *widget,
903                         GtkAllocation *allocation)
904 {
905   GtkMenu *menu;
906   GtkMenuShell *menu_shell;
907   GtkWidget *child;
908   GtkAllocation child_allocation;
909   GList *children;
910   
911   g_return_if_fail (widget != NULL);
912   g_return_if_fail (GTK_IS_MENU (widget));
913   g_return_if_fail (allocation != NULL);
914   
915   menu = GTK_MENU (widget);
916   menu_shell = GTK_MENU_SHELL (widget);
917   
918   widget->allocation = *allocation;
919   if (GTK_WIDGET_REALIZED (widget))
920     gdk_window_move_resize (widget->window,
921                             allocation->x, allocation->y,
922                             allocation->width, allocation->height);
923
924
925   if (menu_shell->children)
926     {
927       child_allocation.x = (GTK_CONTAINER (menu)->border_width +
928                             widget->style->xthickness);
929       child_allocation.y = (GTK_CONTAINER (menu)->border_width +
930                             widget->style->ythickness);
931       child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
932       
933       children = menu_shell->children;
934       while (children)
935         {
936           child = children->data;
937           children = children->next;
938           
939           if (GTK_WIDGET_VISIBLE (child))
940             {
941               GtkRequisition child_requisition;
942               gtk_widget_get_child_requisition (child, &child_requisition);
943               
944               child_allocation.height = child_requisition.height;
945               
946               gtk_widget_size_allocate (child, &child_allocation);
947               gtk_widget_queue_draw (child);
948               
949               child_allocation.y += child_allocation.height;
950             }
951         }
952     }
953 }
954
955 static void
956 gtk_menu_paint (GtkWidget *widget)
957 {
958   g_return_if_fail (widget != NULL);
959   g_return_if_fail (GTK_IS_MENU (widget));
960   
961   if (GTK_WIDGET_DRAWABLE (widget))
962     {
963       gtk_paint_box (widget->style,
964                      widget->window,
965                      GTK_STATE_NORMAL,
966                      GTK_SHADOW_OUT,
967                      NULL, widget, "menu",
968                      0, 0, -1, -1);
969     }
970 }
971
972 static void
973 gtk_menu_draw (GtkWidget    *widget,
974                GdkRectangle *area)
975 {
976   GtkMenuShell *menu_shell;
977   GtkWidget *child;
978   GdkRectangle child_area;
979   GList *children;
980   
981   g_return_if_fail (widget != NULL);
982   g_return_if_fail (GTK_IS_MENU (widget));
983   g_return_if_fail (area != NULL);
984   
985   if (GTK_WIDGET_DRAWABLE (widget))
986     {
987       gtk_menu_paint (widget);
988       
989       menu_shell = GTK_MENU_SHELL (widget);
990       
991       children = menu_shell->children;
992       while (children)
993         {
994           child = children->data;
995           children = children->next;
996           
997           if (gtk_widget_intersect (child, area, &child_area))
998             gtk_widget_draw (child, &child_area);
999         }
1000     }
1001 }
1002
1003 static gboolean
1004 gtk_menu_expose (GtkWidget      *widget,
1005                  GdkEventExpose *event)
1006 {
1007   GtkMenuShell *menu_shell;
1008   GtkWidget *child;
1009   GdkEventExpose child_event;
1010   GList *children;
1011   GtkMenu *menu;
1012   
1013   g_return_val_if_fail (widget != NULL, FALSE);
1014   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1015   g_return_val_if_fail (event != NULL, FALSE);
1016
1017   menu_shell = GTK_MENU_SHELL (widget);
1018   menu = GTK_MENU (widget);
1019   
1020   if (GTK_WIDGET_DRAWABLE (widget))
1021     {
1022       gtk_menu_paint (widget);
1023       
1024       child_event = *event;
1025       
1026       children = menu_shell->children;
1027       while (children)
1028         {
1029           child = children->data;
1030           children = children->next;
1031           
1032           if (GTK_WIDGET_NO_WINDOW (child) &&
1033               gtk_widget_intersect (child, &event->area, &child_event.area))
1034             gtk_widget_event (child, (GdkEvent*) &child_event);
1035         }
1036     }
1037   
1038   return FALSE;
1039 }
1040
1041 static gboolean
1042 gtk_menu_key_press (GtkWidget   *widget,
1043                     GdkEventKey *event)
1044 {
1045   GtkMenuShell *menu_shell;
1046   gboolean delete = FALSE;
1047   
1048   g_return_val_if_fail (widget != NULL, FALSE);
1049   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1050   g_return_val_if_fail (event != NULL, FALSE);
1051       
1052   menu_shell = GTK_MENU_SHELL (widget);
1053
1054   gtk_menu_stop_navigating_submenu (GTK_MENU (widget));
1055
1056   if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1057     return TRUE;
1058
1059   switch (event->keyval)
1060     {
1061     case GDK_Delete:
1062     case GDK_KP_Delete:
1063     case GDK_BackSpace:
1064       delete = TRUE;
1065       break;
1066     default:
1067       break;
1068     }
1069
1070   /* Modify the accelerators */
1071   if (menu_shell->active_menu_item &&
1072       GTK_BIN (menu_shell->active_menu_item)->child &&
1073       GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL &&
1074       !gtk_widget_accelerators_locked (menu_shell->active_menu_item) &&
1075       (delete ||
1076        (gtk_accelerator_valid (event->keyval, event->state) &&
1077         (event->state ||
1078          !gtk_menu_get_uline_accel_group (GTK_MENU (menu_shell)) ||
1079          (event->keyval >= GDK_F1 && event->keyval <= GDK_F35)))))
1080     {
1081       GtkMenuItem *menu_item;
1082       GtkAccelGroup *accel_group;
1083       
1084       menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
1085       
1086       if (!GTK_MENU (widget)->accel_group)
1087         accel_group = gtk_accel_group_get_default ();
1088       else
1089         accel_group = GTK_MENU (widget)->accel_group;
1090       
1091       gtk_widget_remove_accelerators (GTK_WIDGET (menu_item),
1092                                       gtk_signal_name (menu_item->accelerator_signal),
1093                                       TRUE);
1094       
1095       if (!delete &&
1096           0 == gtk_widget_accelerator_signal (GTK_WIDGET (menu_item),
1097                                               accel_group,
1098                                               event->keyval,
1099                                               event->state))
1100         {
1101           GSList *slist;
1102           
1103           slist = gtk_accel_group_entries_from_object (GTK_OBJECT (menu_item));
1104           while (slist)
1105             {
1106               GtkAccelEntry *ac_entry;
1107               
1108               ac_entry = slist->data;
1109               
1110               if (ac_entry->signal_id == menu_item->accelerator_signal)
1111                 break;
1112               
1113               slist = slist->next;
1114             }
1115           
1116           if (!slist)
1117             gtk_widget_add_accelerator (GTK_WIDGET (menu_item),
1118                                         gtk_signal_name (menu_item->accelerator_signal),
1119                                         accel_group,
1120                                         event->keyval,
1121                                         event->state,
1122                                         GTK_ACCEL_VISIBLE);
1123         }
1124     }
1125   
1126   return TRUE;
1127 }
1128
1129 static gboolean
1130 gtk_menu_motion_notify  (GtkWidget         *widget,
1131                          GdkEventMotion    *event)
1132 {
1133   GtkWidget *menu_item;
1134   GtkMenu *menu;
1135   GtkMenuShell *menu_shell;
1136
1137   gboolean need_enter;
1138
1139   /* We received the event for one of two reasons:
1140    *
1141    * a) We are the active menu, and did gtk_grab_add()
1142    * b) The widget is a child of ours, and the event was propagated
1143    *
1144    * Since for computation of navigation regions, we want the menu which
1145    * is the parent of the menu item, for a), we need to find that menu,
1146    * which may be different from 'widget'.
1147    */
1148   
1149   menu_item = gtk_get_event_widget ((GdkEvent*) event);
1150   if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) || !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
1151       !GTK_IS_MENU (menu_item->parent))
1152     return FALSE;
1153
1154   menu_shell = GTK_MENU_SHELL (menu_item->parent);
1155   menu = GTK_MENU (menu_shell);
1156   
1157   need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter);
1158
1159   /* Check to see if we are within an active submenu's navigation region
1160    */
1161   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
1162     return TRUE; 
1163
1164   if (need_enter)
1165     {
1166       /* The menu is now sensitive to enter events on its items, but
1167        * was previously sensitive.  So we fake an enter event.
1168        */
1169       gint width, height;
1170       
1171       menu_shell->ignore_enter = FALSE; 
1172       
1173       gdk_window_get_size (event->window, &width, &height);
1174       if (event->x >= 0 && event->x < width &&
1175           event->y >= 0 && event->y < height)
1176         {
1177           GdkEvent send_event;
1178           
1179           send_event.crossing.type = GDK_ENTER_NOTIFY;
1180           send_event.crossing.window = event->window;
1181           send_event.crossing.time = event->time;
1182           send_event.crossing.send_event = TRUE;
1183           send_event.crossing.x_root = event->x_root;
1184           send_event.crossing.y_root = event->y_root;
1185           send_event.crossing.x = event->x;
1186           send_event.crossing.y = event->y;
1187
1188           /* We send the event to 'widget', the currently active menu,
1189            * instead of 'menu', the menu that the pointer is in. This
1190            * will ensure that the event will be ignored unless the
1191            * menuitem is a child of the active menu or some parent
1192            * menu of the active menu.
1193            */
1194           return gtk_widget_event (widget, &send_event);
1195         }
1196     }
1197
1198   return FALSE;
1199 }
1200
1201 static gboolean
1202 gtk_menu_enter_notify (GtkWidget        *widget,
1203                        GdkEventCrossing *event)
1204 {
1205   GtkWidget *menu_item;
1206
1207   /* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
1208    * will not correspond to the event widget's parent.  Check to see
1209    * if we are in the parent's navigation region.
1210    */
1211   menu_item = gtk_get_event_widget ((GdkEvent*) event);
1212   if (menu_item && GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) &&
1213       gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent), event->x_root, event->y_root))
1214     return TRUE; 
1215
1216   return GTK_WIDGET_CLASS (parent_class)->enter_notify_event (widget, event); 
1217 }
1218
1219 static gboolean
1220 gtk_menu_leave_notify (GtkWidget        *widget,
1221                        GdkEventCrossing *event)
1222 {
1223   GtkMenuShell *menu_shell;
1224   GtkMenu *menu;
1225   GtkMenuItem *menu_item;
1226   GtkWidget *event_widget; 
1227
1228   menu = GTK_MENU (widget);
1229   menu_shell = GTK_MENU_SHELL (widget); 
1230   
1231   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
1232     return TRUE; 
1233   
1234   event_widget = gtk_get_event_widget ((GdkEvent*) event);
1235   
1236   if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
1237     return TRUE;
1238   
1239   menu_item = GTK_MENU_ITEM (event_widget); 
1240   
1241   /* Here we check to see if we're leaving an active menu item with a submenu, 
1242    * in which case we enter submenu navigation mode. 
1243    */
1244   if (menu_shell->active_menu_item != NULL
1245       && menu_item->submenu != NULL
1246       && menu_item->submenu_placement == GTK_LEFT_RIGHT)
1247     {
1248       if (menu_item->submenu->window != NULL) 
1249         {
1250           gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
1251           return TRUE;
1252         }
1253     }
1254   
1255   return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event); 
1256 }
1257
1258 static void 
1259 gtk_menu_stop_navigating_submenu (GtkMenu *menu)
1260 {
1261   if (menu->navigation_region) 
1262     {
1263       gdk_region_destroy (menu->navigation_region);
1264       menu->navigation_region = NULL;
1265     }
1266   
1267   if (menu->navigation_timeout)
1268     {
1269       gtk_timeout_remove (menu->navigation_timeout);
1270       menu->navigation_timeout = 0;
1271     }
1272 }
1273
1274 /* When the timeout is elapsed, the navigation region is destroyed
1275  * and the menuitem under the pointer (if any) is selected.
1276  */
1277 static gboolean
1278 gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
1279 {
1280   GdkEventCrossing send_event;
1281
1282   GtkMenu *menu = user_data;
1283   GdkWindow *child_window;
1284
1285   gtk_menu_stop_navigating_submenu (menu);
1286   
1287   if (GTK_WIDGET_REALIZED (menu))
1288     {
1289       child_window = gdk_window_get_pointer (GTK_WIDGET (menu)->window, NULL, NULL, NULL);
1290
1291       if (child_window)
1292         {
1293           send_event.window = child_window;
1294           send_event.type = GDK_ENTER_NOTIFY;
1295           send_event.time = GDK_CURRENT_TIME; /* Bogus */
1296           send_event.send_event = TRUE;
1297
1298           GTK_WIDGET_CLASS (parent_class)->enter_notify_event (GTK_WIDGET (menu), &send_event); 
1299         }
1300     }
1301
1302   return FALSE; 
1303 }
1304
1305 static gboolean
1306 gtk_menu_navigating_submenu (GtkMenu *menu,
1307                              gint     event_x,
1308                              gint     event_y)
1309 {
1310   if (menu->navigation_region)
1311     {
1312       if (gdk_region_point_in (menu->navigation_region, event_x, event_y))
1313         return TRUE;
1314       else
1315         {
1316           gtk_menu_stop_navigating_submenu (menu);
1317           return FALSE;
1318         }
1319     }
1320   return FALSE;
1321 }
1322
1323 static void
1324 gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
1325                                         GtkMenuItem      *menu_item,
1326                                         GdkEventCrossing *event)
1327 {
1328   gint submenu_left = 0;
1329   gint submenu_right = 0;
1330   gint submenu_top = 0;
1331   gint submenu_bottom = 0;
1332   gint width = 0;
1333   gint height = 0;
1334   GdkPoint point[3];
1335   GtkWidget *event_widget;
1336
1337   g_return_if_fail (menu_item->submenu != NULL);
1338   g_return_if_fail (event != NULL);
1339   
1340   event_widget = gtk_get_event_widget ((GdkEvent*) event);
1341   
1342   gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top);
1343   gdk_window_get_size (menu_item->submenu->window, &width, &height);
1344   submenu_right = submenu_left + width;
1345   submenu_bottom = submenu_top + height;
1346   
1347   gdk_window_get_size (event_widget->window, &width, &height);
1348   
1349   if (event->x >= 0 && event->x < width)
1350     {
1351       /* Set navigation region */
1352       /* We fudge/give a little padding in case the user
1353        * ``misses the vertex'' of the triangle/is off by a pixel or two.
1354        */ 
1355       if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
1356         point[0].x = event->x_root - SUBMENU_NAV_REGION_PADDING; 
1357       else                             
1358         point[0].x = event->x_root + SUBMENU_NAV_REGION_PADDING;  
1359       
1360       /* Exiting the top or bottom? */ 
1361       if (event->y < 0)
1362         { /* top */
1363           point[0].y = event->y_root + SUBMENU_NAV_REGION_PADDING;
1364           point[1].y = submenu_top;
1365
1366           if (point[0].y <= point[1].y)
1367             return;
1368         }
1369       else
1370         { /* bottom */
1371           point[0].y = event->y_root - SUBMENU_NAV_REGION_PADDING; 
1372           point[1].y = submenu_bottom;
1373
1374           if (point[0].y >= point[1].y)
1375             return;
1376         }
1377       
1378       /* Submenu is to the left or right? */ 
1379       if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
1380         point[1].x = submenu_left;  /* right */
1381       else
1382         point[1].x = submenu_right; /* left */
1383       
1384       point[2].x = point[1].x;
1385       point[2].y = point[0].y;
1386
1387       gtk_menu_stop_navigating_submenu (menu);
1388       
1389       menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
1390
1391       menu->navigation_timeout = gtk_timeout_add (SUBMENU_NAV_HYSTERESIS_TIMEOUT, 
1392                                                   gtk_menu_stop_navigating_submenu_cb, menu);
1393     }
1394 }
1395
1396 static void
1397 gtk_menu_deactivate (GtkMenuShell *menu_shell)
1398 {
1399   GtkWidget *parent;
1400   
1401   g_return_if_fail (menu_shell != NULL);
1402   g_return_if_fail (GTK_IS_MENU (menu_shell));
1403   
1404   parent = menu_shell->parent_menu_shell;
1405   
1406   menu_shell->activate_time = 0;
1407   gtk_menu_popdown (GTK_MENU (menu_shell));
1408   
1409   if (parent)
1410     gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
1411 }
1412
1413 static void
1414 gtk_menu_position (GtkMenu *menu)
1415 {
1416   GtkWidget *widget;
1417   GtkRequisition requisition;
1418   gint x, y;
1419  
1420   g_return_if_fail (menu != NULL);
1421   g_return_if_fail (GTK_IS_MENU (menu));
1422
1423   widget = GTK_WIDGET (menu);
1424
1425   gdk_window_get_pointer (NULL, &x, &y, NULL);
1426
1427   /* We need the requisition to figure out the right place to
1428    * popup the menu. In fact, we always need to ask here, since
1429    * if a size_request was queued while we weren't popped up,
1430    * the requisition won't have been recomputed yet.
1431    */
1432   gtk_widget_size_request (widget, &requisition);
1433       
1434   if (menu->position_func)
1435     (* menu->position_func) (menu, &x, &y, menu->position_func_data);
1436   else
1437     {
1438       gint screen_width;
1439       gint screen_height;
1440       
1441       screen_width = gdk_screen_width ();
1442       screen_height = gdk_screen_height ();
1443           
1444       x = CLAMP (x - 2, 0, MAX (0, screen_width - requisition.width));
1445       y = CLAMP (y - 2, 0, MAX (0, screen_height - requisition.height));
1446     }
1447
1448   /* FIXME: The MAX() here is because gtk_widget_set_uposition
1449    * is broken. Once we provide an alternate interface that
1450    * allows negative values, then we can remove them.
1451    */
1452   gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
1453                                 menu->toplevel : menu->tearoff_window, 
1454                             MAX (x, 0), MAX (y, 0));
1455 }
1456
1457 /* Reparent the menu, taking care of the refcounting
1458  */
1459 static void 
1460 gtk_menu_reparent (GtkMenu      *menu, 
1461                    GtkWidget    *new_parent, 
1462                    gboolean      unrealize)
1463 {
1464   GtkObject *object = GTK_OBJECT (menu);
1465   GtkWidget *widget = GTK_WIDGET (menu);
1466   gboolean was_floating = GTK_OBJECT_FLOATING (object);
1467
1468   gtk_object_ref (object);
1469   gtk_object_sink (object);
1470
1471   if (unrealize)
1472     {
1473       gtk_object_ref (object);
1474       gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
1475       gtk_container_add (GTK_CONTAINER (new_parent), widget);
1476       gtk_object_unref (object);
1477     }
1478   else
1479     gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
1480   gtk_widget_set_usize (new_parent, -1, -1);
1481   
1482   if (was_floating)
1483     GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
1484   else
1485     gtk_object_unref (object);
1486 }
1487
1488 static void
1489 gtk_menu_show_all (GtkWidget *widget)
1490 {
1491   g_return_if_fail (widget != NULL);
1492   g_return_if_fail (GTK_IS_MENU (widget));
1493
1494   /* Show children, but not self. */
1495   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
1496 }
1497
1498
1499 static void
1500 gtk_menu_hide_all (GtkWidget *widget)
1501 {
1502   g_return_if_fail (widget != NULL);
1503   g_return_if_fail (GTK_IS_MENU (widget));
1504
1505   /* Hide children, but not self. */
1506   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
1507 }
1508