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