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