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