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