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