]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenuitem.c
Applied patch from Nils Barth (bug # 51041) to replace "gint" with
[~andy/gtk] / gtk / gtkmenuitem.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 <string.h>
28 #include "gtkaccellabel.h"
29 #include "gtkmain.h"
30 #include "gtkmenu.h"
31 #include "gtkmenubar.h"
32 #include "gtkmenuitem.h"
33 #include "gtksignal.h"
34
35
36 #define BORDER_SPACING  3
37 #define SELECT_TIMEOUT  75
38
39 #define MENU_ITEM_CLASS(w)  GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
40
41
42 enum {
43   ACTIVATE,
44   ACTIVATE_ITEM,
45   TOGGLE_SIZE_REQUEST,
46   TOGGLE_SIZE_ALLOCATE,
47   LAST_SIGNAL
48 };
49
50
51 static void gtk_menu_item_class_init     (GtkMenuItemClass *klass);
52 static void gtk_menu_item_init           (GtkMenuItem      *menu_item);
53 static void gtk_menu_item_destroy        (GtkObject        *object);
54 static void gtk_menu_item_size_request   (GtkWidget        *widget,
55                                           GtkRequisition   *requisition);
56 static void gtk_menu_item_size_allocate  (GtkWidget        *widget,
57                                           GtkAllocation    *allocation);
58 static void gtk_menu_item_paint          (GtkWidget        *widget,
59                                           GdkRectangle     *area);
60 static gint gtk_menu_item_expose         (GtkWidget        *widget,
61                                           GdkEventExpose   *event);
62
63 static void gtk_real_menu_item_select               (GtkItem     *item);
64 static void gtk_real_menu_item_deselect             (GtkItem     *item);
65 static void gtk_real_menu_item_activate_item        (GtkMenuItem *item);
66 static void gtk_real_menu_item_toggle_size_request  (GtkMenuItem *menu_item,
67                                                      gint        *requisition);
68 static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
69                                                      gint         allocation);
70
71 static gint gtk_menu_item_select_timeout (gpointer          data);
72 static void gtk_menu_item_popup_submenu  (gpointer     data);
73 static void gtk_menu_item_position_menu  (GtkMenu          *menu,
74                                           gint             *x,
75                                           gint             *y,
76                                           gboolean         *push_in,
77                                           gpointer          user_data);
78 static void gtk_menu_item_show_all       (GtkWidget        *widget);
79 static void gtk_menu_item_hide_all       (GtkWidget        *widget);
80 static void gtk_menu_item_forall         (GtkContainer    *container,
81                                           gboolean         include_internals,
82                                           GtkCallback      callback,
83                                           gpointer         callback_data);
84
85 static GtkItemClass *parent_class;
86 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
87 static guint32  last_submenu_deselect_time = 0;
88
89
90
91 GtkType
92 gtk_menu_item_get_type (void)
93 {
94   static GtkType menu_item_type = 0;
95
96   if (!menu_item_type)
97     {
98       static const GTypeInfo menu_item_info =
99       {
100         sizeof (GtkMenuItemClass),
101         NULL,           /* base_init */
102         NULL,           /* base_finalize */
103         (GClassInitFunc) gtk_menu_item_class_init,
104         NULL,           /* class_finalize */
105         NULL,           /* class_data */
106         sizeof (GtkMenuItem),
107         16,             /* n_preallocs */
108         (GInstanceInitFunc) gtk_menu_item_init,
109       };
110
111       menu_item_type = g_type_register_static (GTK_TYPE_ITEM, "GtkMenuItem", &menu_item_info, 0);
112     }
113
114   return menu_item_type;
115 }
116
117 static void
118 gtk_menu_item_class_init (GtkMenuItemClass *klass)
119 {
120   GtkObjectClass *object_class;
121   GtkWidgetClass *widget_class;
122   GtkContainerClass *container_class;
123   GtkItemClass *item_class;
124
125   object_class = (GtkObjectClass*) klass;
126   widget_class = (GtkWidgetClass*) klass;
127   container_class = (GtkContainerClass*) klass;
128   item_class = (GtkItemClass*) klass;
129
130   parent_class = gtk_type_class (gtk_item_get_type ());
131
132
133   object_class->destroy = gtk_menu_item_destroy;
134
135   widget_class->size_request = gtk_menu_item_size_request;
136   widget_class->size_allocate = gtk_menu_item_size_allocate;
137   widget_class->expose_event = gtk_menu_item_expose;
138   widget_class->show_all = gtk_menu_item_show_all;
139   widget_class->hide_all = gtk_menu_item_hide_all;
140
141   container_class->forall = gtk_menu_item_forall;
142
143   item_class->select = gtk_real_menu_item_select;
144   item_class->deselect = gtk_real_menu_item_deselect;
145
146   klass->activate = NULL;
147   klass->activate_item = gtk_real_menu_item_activate_item;
148   klass->toggle_size_request = gtk_real_menu_item_toggle_size_request;
149   klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
150
151   klass->hide_on_activate = TRUE;
152
153   menu_item_signals[ACTIVATE] =
154     gtk_signal_new ("activate",
155                     GTK_RUN_FIRST | GTK_RUN_ACTION,
156                     GTK_CLASS_TYPE (object_class),
157                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate),
158                     gtk_marshal_VOID__VOID,
159                     GTK_TYPE_NONE, 0);
160   widget_class->activate_signal = menu_item_signals[ACTIVATE];
161
162   menu_item_signals[ACTIVATE_ITEM] =
163     gtk_signal_new ("activate_item",
164                     GTK_RUN_FIRST,
165                     GTK_CLASS_TYPE (object_class),
166                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate_item),
167                     gtk_signal_default_marshaller,
168                     GTK_TYPE_NONE, 0);
169
170   menu_item_signals[TOGGLE_SIZE_REQUEST] =
171     gtk_signal_new ("toggle_size_request",
172                     GTK_RUN_FIRST,
173                     GTK_CLASS_TYPE (object_class),
174                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_request),
175                     gtk_marshal_NONE__POINTER,
176                     GTK_TYPE_NONE, 1,
177                     GTK_TYPE_POINTER);
178
179   menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
180     gtk_signal_new ("toggle_size_allocate",
181                     GTK_RUN_FIRST,
182                     GTK_CLASS_TYPE (object_class),
183                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_allocate),
184                     gtk_marshal_NONE__INT,
185                     GTK_TYPE_NONE, 1,
186                     GTK_TYPE_INT);
187 }
188
189 static void
190 gtk_menu_item_init (GtkMenuItem *menu_item)
191 {
192   menu_item->submenu = NULL;
193   menu_item->accelerator_signal = menu_item_signals[ACTIVATE];
194   menu_item->toggle_size = 0;
195   menu_item->accelerator_width = 0;
196   menu_item->show_toggle_indicator = FALSE;
197   menu_item->show_submenu_indicator = FALSE;
198   menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
199   menu_item->submenu_placement = GTK_TOP_BOTTOM;
200   menu_item->right_justify = FALSE;
201
202   menu_item->timer = 0;
203 }
204
205 GtkWidget*
206 gtk_menu_item_new (void)
207 {
208   return GTK_WIDGET (gtk_type_new (gtk_menu_item_get_type ()));
209 }
210
211 GtkWidget*
212 gtk_menu_item_new_with_label (const gchar *label)
213 {
214   GtkWidget *menu_item;
215   GtkWidget *accel_label;
216
217   menu_item = gtk_menu_item_new ();
218   accel_label = gtk_accel_label_new (label);
219   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
220
221   gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
222   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
223   gtk_widget_show (accel_label);
224
225   return menu_item;
226 }
227
228 static void
229 gtk_menu_item_destroy (GtkObject *object)
230 {
231   GtkMenuItem *menu_item;
232
233   g_return_if_fail (object != NULL);
234   g_return_if_fail (GTK_IS_MENU_ITEM (object));
235
236   menu_item = GTK_MENU_ITEM (object);
237
238   if (menu_item->submenu)
239     gtk_widget_destroy (menu_item->submenu);
240
241   if (GTK_OBJECT_CLASS (parent_class)->destroy)
242     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
243 }
244
245 static void
246 gtk_menu_item_detacher (GtkWidget     *widget,
247                         GtkMenu       *menu)
248 {
249   GtkMenuItem *menu_item;
250
251   g_return_if_fail (widget != NULL);
252   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
253
254   menu_item = GTK_MENU_ITEM (widget);
255   g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);
256
257   menu_item->submenu = NULL;
258 }
259
260 void
261 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
262                            GtkWidget   *submenu)
263 {
264   g_return_if_fail (menu_item != NULL);
265   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
266   
267   if (menu_item->submenu != submenu)
268     {
269       gtk_menu_item_remove_submenu (menu_item);
270       
271       menu_item->submenu = submenu;
272       gtk_menu_attach_to_widget (GTK_MENU (submenu),
273                                  GTK_WIDGET (menu_item),
274                                  gtk_menu_item_detacher);
275       
276       if (GTK_WIDGET (menu_item)->parent)
277         gtk_widget_queue_resize (GTK_WIDGET (menu_item));
278     }
279 }
280
281 void
282 gtk_menu_item_remove_submenu (GtkMenuItem         *menu_item)
283 {
284   g_return_if_fail (menu_item != NULL);
285   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
286       
287   if (menu_item->submenu)
288     gtk_menu_detach (GTK_MENU (menu_item->submenu));
289 }
290
291 void
292 gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
293                              GtkSubmenuPlacement  placement)
294 {
295   g_return_if_fail (menu_item != NULL);
296   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
297
298   menu_item->submenu_placement = placement;
299 }
300
301 void
302 gtk_menu_item_configure (GtkMenuItem *menu_item,
303                          gboolean     show_toggle_indicator,
304                          gboolean     show_submenu_indicator)
305 {
306   g_return_if_fail (menu_item != NULL);
307   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
308
309   menu_item->show_toggle_indicator = show_toggle_indicator;
310   menu_item->show_submenu_indicator = show_submenu_indicator;
311 }
312
313 void
314 gtk_menu_item_select (GtkMenuItem *menu_item)
315 {
316   g_return_if_fail (menu_item != NULL);
317   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
318   
319   gtk_item_select (GTK_ITEM (menu_item));
320 }
321
322 void
323 gtk_menu_item_deselect (GtkMenuItem *menu_item)
324 {
325   g_return_if_fail (menu_item != NULL);
326   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
327   
328   gtk_item_deselect (GTK_ITEM (menu_item));
329 }
330
331 void
332 gtk_menu_item_activate (GtkMenuItem *menu_item)
333 {
334   g_return_if_fail (menu_item != NULL);
335   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
336   
337   gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[ACTIVATE]);
338 }
339
340 void
341 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
342                                    gint        *requisition)
343 {
344   g_return_if_fail (menu_item != NULL);
345   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
346
347   gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_REQUEST], requisition);
348 }
349
350 void
351 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
352                                     gint         allocation)
353 {
354   g_return_if_fail (menu_item != NULL);
355   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
356
357   gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_ALLOCATE], allocation);
358 }
359
360 static void
361 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
362                                    gpointer data)
363 {
364   guint *width = data;
365
366   if (GTK_IS_ACCEL_LABEL (widget))
367     {
368       guint w;
369
370       w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
371       *width = MAX (*width, w);
372     }
373   else if (GTK_IS_CONTAINER (widget))
374     gtk_container_foreach (GTK_CONTAINER (widget),
375                            gtk_menu_item_accel_width_foreach,
376                            data);
377 }
378
379 static void
380 gtk_menu_item_size_request (GtkWidget      *widget,
381                             GtkRequisition *requisition)
382 {
383   GtkMenuItem *menu_item;
384   GtkBin *bin;
385   guint accel_width;
386
387   g_return_if_fail (widget != NULL);
388   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
389   g_return_if_fail (requisition != NULL);
390
391   bin = GTK_BIN (widget);
392   menu_item = GTK_MENU_ITEM (widget);
393
394   requisition->width = (GTK_CONTAINER (widget)->border_width +
395                         widget->style->xthickness +
396                         BORDER_SPACING) * 2;
397   requisition->height = (GTK_CONTAINER (widget)->border_width +
398                          widget->style->ythickness) * 2;
399
400   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
401     {
402       GtkRequisition child_requisition;
403       
404       gtk_widget_size_request (bin->child, &child_requisition);
405
406       requisition->width += child_requisition.width;
407       requisition->height += child_requisition.height;
408     }
409
410   if (menu_item->submenu && menu_item->show_submenu_indicator)
411     requisition->width += 21;
412
413   accel_width = 0;
414   gtk_container_foreach (GTK_CONTAINER (menu_item),
415                          gtk_menu_item_accel_width_foreach,
416                          &accel_width);
417   menu_item->accelerator_width = accel_width;
418 }
419
420 static void
421 gtk_menu_item_size_allocate (GtkWidget     *widget,
422                              GtkAllocation *allocation)
423 {
424   GtkMenuItem *menu_item;
425   GtkBin *bin;
426   GtkAllocation child_allocation;
427
428   g_return_if_fail (widget != NULL);
429   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
430   g_return_if_fail (allocation != NULL);
431
432   menu_item = GTK_MENU_ITEM (widget);
433   bin = GTK_BIN (widget);
434   
435   widget->allocation = *allocation;
436
437   if (bin->child)
438     {
439       child_allocation.x = (GTK_CONTAINER (widget)->border_width +
440                             widget->style->xthickness +
441                             BORDER_SPACING);
442       child_allocation.y = (GTK_CONTAINER (widget)->border_width +
443                             widget->style->ythickness);
444       child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
445       child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
446       child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
447       child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
448       if (menu_item->submenu && menu_item->show_submenu_indicator)
449         child_allocation.width -= 21;
450       
451       gtk_widget_size_allocate (bin->child, &child_allocation);
452     }
453
454   if (GTK_WIDGET_REALIZED (widget))
455     gdk_window_move_resize (widget->window,
456                             allocation->x, allocation->y,
457                             allocation->width, allocation->height);
458
459   if (menu_item->submenu)
460     gtk_menu_reposition (GTK_MENU (menu_item->submenu));
461 }
462
463 static void
464 gtk_menu_item_paint (GtkWidget    *widget,
465                      GdkRectangle *area)
466 {
467   GtkMenuItem *menu_item;
468   GtkStateType state_type;
469   GtkShadowType shadow_type;
470   gint width, height;
471   gint x, y;
472
473   g_return_if_fail (widget != NULL);
474   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
475
476   if (GTK_WIDGET_DRAWABLE (widget))
477     {
478       menu_item = GTK_MENU_ITEM (widget);
479
480       state_type = widget->state;
481
482       x = GTK_CONTAINER (menu_item)->border_width;
483       y = GTK_CONTAINER (menu_item)->border_width;
484       width = widget->allocation.width - x * 2;
485       height = widget->allocation.height - y * 2;
486       
487       if ((state_type == GTK_STATE_PRELIGHT) &&
488           (GTK_BIN (menu_item)->child))
489         gtk_paint_box (widget->style,
490                        widget->window,
491                        GTK_STATE_PRELIGHT,
492                        GTK_SHADOW_OUT,
493                        area, widget, "menuitem",
494                        x, y, width, height);
495       else
496         {
497           gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
498           gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
499         }
500
501       if (menu_item->submenu && menu_item->show_submenu_indicator)
502         {
503           shadow_type = GTK_SHADOW_OUT;
504           if (state_type == GTK_STATE_PRELIGHT)
505             shadow_type = GTK_SHADOW_IN;
506
507           gtk_paint_arrow (widget->style, widget->window,
508                            state_type, shadow_type, 
509                            area, widget, "menuitem", 
510                            GTK_ARROW_RIGHT, TRUE,
511                            x + width - 15, y + height / 2 - 5, 10, 10);
512         }
513       else if (!GTK_BIN (menu_item)->child)
514         {
515            gtk_paint_hline (widget->style, widget->window, GTK_STATE_NORMAL,
516                             area, widget, "menuitem",
517                             0, widget->allocation.width, 0);
518         }
519     }
520 }
521
522 static gint
523 gtk_menu_item_expose (GtkWidget      *widget,
524                       GdkEventExpose *event)
525 {
526   g_return_val_if_fail (widget != NULL, FALSE);
527   g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), FALSE);
528   g_return_val_if_fail (event != NULL, FALSE);
529
530   if (GTK_WIDGET_DRAWABLE (widget))
531     {
532       gtk_menu_item_paint (widget, &event->area);
533
534       (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
535     }
536
537   return FALSE;
538 }
539
540 static void
541 gtk_real_menu_item_select (GtkItem *item)
542 {
543   GtkMenuItem *menu_item;
544
545   g_return_if_fail (item != NULL);
546   g_return_if_fail (GTK_IS_MENU_ITEM (item));
547
548   menu_item = GTK_MENU_ITEM (item);
549
550   /*  if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))*/
551   if (menu_item->submenu)
552     {
553       guint32 etime;
554       GdkEvent *event = gtk_get_current_event ();
555
556       etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
557       if (etime >= last_submenu_deselect_time &&
558           last_submenu_deselect_time + SELECT_TIMEOUT > etime)
559         menu_item->timer = gtk_timeout_add (SELECT_TIMEOUT - (etime - last_submenu_deselect_time),
560                                             gtk_menu_item_select_timeout,
561                                             menu_item);
562       else
563         gtk_menu_item_popup_submenu (menu_item);
564       if(event) gdk_event_free(event);
565     }
566   
567   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
568   gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
569 }
570
571 static void
572 gtk_real_menu_item_deselect (GtkItem *item)
573 {
574   GtkMenuItem *menu_item;
575
576   g_return_if_fail (item != NULL);
577   g_return_if_fail (GTK_IS_MENU_ITEM (item));
578
579   menu_item = GTK_MENU_ITEM (item);
580
581   if (menu_item->submenu)
582     {
583       guint32 etime;
584       GdkEvent *event = gtk_get_current_event ();
585
586       if (menu_item->timer)
587         {
588           gtk_timeout_remove (menu_item->timer);
589           menu_item->timer = 0;
590         }
591       else
592         gtk_menu_popdown (GTK_MENU (menu_item->submenu));
593
594       etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
595       if (etime > last_submenu_deselect_time)
596         last_submenu_deselect_time = etime;
597       if(event) gdk_event_free(event);
598     }
599
600   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
601   gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
602 }
603
604 static void
605 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
606 {
607   GtkWidget *widget;
608   GtkMenuShell *submenu; 
609
610   g_return_if_fail (menu_item != NULL);
611   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
612
613   widget = GTK_WIDGET (menu_item);
614   
615   if (widget->parent &&
616       GTK_IS_MENU_SHELL (widget->parent))
617     {
618       if (menu_item->submenu == NULL)
619         gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
620                                       widget, TRUE);
621       else
622         {
623           GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);
624
625           if (!menu_shell->active)
626             {
627               gtk_grab_add (GTK_WIDGET (menu_shell));
628               menu_shell->have_grab = TRUE;
629               menu_shell->active = TRUE;
630             }
631
632           gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget); 
633           gtk_menu_item_popup_submenu (widget); 
634
635           submenu = GTK_MENU_SHELL (menu_item->submenu);
636           if (submenu->children)
637             gtk_menu_shell_select_item (submenu, submenu->children->data);
638         }
639     }
640 }
641 static void
642 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
643                                         gint        *requisition)
644 {
645   g_return_if_fail (menu_item != NULL);
646   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
647
648   *requisition = 0;
649 }
650
651 static void
652 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
653                                          gint         allocation)
654 {
655   g_return_if_fail (menu_item != NULL);
656   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
657
658   menu_item->toggle_size = allocation;
659 }
660
661 static gint
662 gtk_menu_item_select_timeout (gpointer data)
663 {
664   GDK_THREADS_ENTER ();
665
666   gtk_menu_item_popup_submenu (data);
667
668   GDK_THREADS_LEAVE ();
669
670   return FALSE;  
671 }
672
673 static void
674 gtk_menu_item_popup_submenu (gpointer data)
675 {
676   GtkMenuItem *menu_item;
677
678   menu_item = GTK_MENU_ITEM (data);
679   menu_item->timer = 0;
680
681   if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu))
682     {
683       gtk_menu_popup (GTK_MENU (menu_item->submenu),
684                       GTK_WIDGET (menu_item)->parent,
685                       GTK_WIDGET (menu_item),
686                       gtk_menu_item_position_menu,
687                       menu_item,
688                       GTK_MENU_SHELL (GTK_WIDGET (menu_item)->parent)->button,
689                       0);
690     }
691 }
692
693 static void
694 gtk_menu_item_position_menu (GtkMenu  *menu,
695                              gint     *x,
696                              gint     *y,
697                              gboolean *push_in,
698                              gpointer  user_data)
699 {
700   GtkMenuItem *menu_item;
701   GtkWidget *parent_menu_item;
702   gint screen_width;
703   gint screen_height;
704   gint twidth, theight;
705   gint tx, ty;
706
707   g_return_if_fail (menu != NULL);
708   g_return_if_fail (x != NULL);
709   g_return_if_fail (y != NULL);
710
711   menu_item = GTK_MENU_ITEM (user_data);
712
713   twidth = GTK_WIDGET (menu)->requisition.width;
714   theight = GTK_WIDGET (menu)->requisition.height;
715
716   screen_width = gdk_screen_width ();
717   screen_height = gdk_screen_height ();
718
719   if (!gdk_window_get_origin (GTK_WIDGET (menu_item)->window, &tx, &ty))
720     {
721       g_warning ("Menu not on screen");
722       return;
723     }
724
725   switch (menu_item->submenu_placement)
726     {
727     case GTK_TOP_BOTTOM:
728       if ((ty + GTK_WIDGET (menu_item)->allocation.height + theight) <= screen_height)
729         ty += GTK_WIDGET (menu_item)->allocation.height;
730       else if ((ty - theight) >= 0)
731         ty -= theight;
732       else if (screen_height - (ty + GTK_WIDGET (menu_item)->allocation.height) > ty)
733         ty += GTK_WIDGET (menu_item)->allocation.height;
734       else
735         ty -= theight;
736       break;
737
738     case GTK_LEFT_RIGHT:
739       menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
740       parent_menu_item = GTK_MENU (GTK_WIDGET (menu_item)->parent)->parent_menu_item;
741       if (parent_menu_item)
742         menu_item->submenu_direction = GTK_MENU_ITEM (parent_menu_item)->submenu_direction;
743
744       switch (menu_item->submenu_direction)
745         {
746         case GTK_DIRECTION_LEFT:
747           if ((tx - twidth) >= 0)
748             tx -= twidth;
749           else
750             {
751               menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
752               tx += GTK_WIDGET (menu_item)->allocation.width - 5;
753             }
754           break;
755
756         case GTK_DIRECTION_RIGHT:
757           if ((tx + GTK_WIDGET (menu_item)->allocation.width + twidth - 5) <= screen_width)
758             tx += GTK_WIDGET (menu_item)->allocation.width - 5;
759           else
760             {
761               menu_item->submenu_direction = GTK_DIRECTION_LEFT;
762               tx -= twidth;
763             }
764           break;
765         }
766
767       ty += GTK_WIDGET (menu_item)->allocation.height / 4;
768
769       /* If the height of the menu doesn't fit we move it upward. */
770       ty = CLAMP (ty, 0, MAX (0, screen_height - theight));
771       break;
772     }
773
774   /* If we have negative, tx, here it is because we can't get
775    * the menu all the way on screen. Favor the left portion.
776    */
777   *x = CLAMP (tx, 0, MAX (0, screen_width - twidth));
778   *y = ty;
779 }
780
781 void
782 gtk_menu_item_right_justify(GtkMenuItem *menuitem)
783 {
784   g_return_if_fail (menuitem != NULL);
785   g_return_if_fail (GTK_IS_MENU_ITEM (menuitem));
786
787   menuitem->right_justify = 1;
788 }
789
790
791 static void
792 gtk_menu_item_show_all (GtkWidget *widget)
793 {
794   GtkMenuItem *menu_item;
795
796   g_return_if_fail (widget != NULL);
797   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
798
799   menu_item = GTK_MENU_ITEM (widget);
800
801   /* show children including submenu */
802   if (menu_item->submenu)
803     gtk_widget_show_all (menu_item->submenu);
804   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
805
806   gtk_widget_show (widget);
807 }
808
809 static void
810 gtk_menu_item_hide_all (GtkWidget *widget)
811 {
812   GtkMenuItem *menu_item;
813
814   g_return_if_fail (widget != NULL);
815   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
816
817   gtk_widget_hide (widget);
818
819   menu_item = GTK_MENU_ITEM (widget);
820
821   /* hide children including submenu */
822   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
823   if (menu_item->submenu)
824     gtk_widget_hide_all (menu_item->submenu);
825 }
826
827 static void
828 gtk_menu_item_forall (GtkContainer *container,
829                       gboolean      include_internals,
830                       GtkCallback   callback,
831                       gpointer      callback_data)
832 {
833   GtkBin *bin;
834   GtkMenuItem *menu_item;
835
836   g_return_if_fail (container != NULL);
837   g_return_if_fail (GTK_IS_MENU_ITEM (container));
838   g_return_if_fail (callback != NULL);
839
840   bin = GTK_BIN (container);
841   menu_item = GTK_MENU_ITEM (container);
842
843   if (bin->child)
844     (* callback) (bin->child, callback_data);
845 }