]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenubar.c
menubar: Only compute the necessary size
[~andy/gtk] / gtk / gtkmenubar.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 /**
28  * SECTION:gtkmenubar
29  * @Title: GtkMenuBar
30  * @Short_description: A subclass of GtkMenuShell which holds GtkMenuItem widgets
31  * @See_also: #GtkMenuShell, #GtkMenu, #GtkMenuItem
32  *
33  * The #GtkMenuBar is a subclass of #GtkMenuShell which contains one or
34  * more #GtkMenuItems. The result is a standard menu bar which can hold
35  * many menu items.
36  */
37
38 #include "config.h"
39
40 #include "gtkmenubar.h"
41
42 #include "gtkbindings.h"
43 #include "gtkmain.h"
44 #include "gtkmarshalers.h"
45 #include "gtkmenuitemprivate.h"
46 #include "gtkmenuprivate.h"
47 #include "gtkmenushellprivate.h"
48 #include "gtksettings.h"
49 #include "gtksizerequest.h"
50 #include "gtkwindow.h"
51 #include "gtkintl.h"
52 #include "gtkprivate.h"
53 #include "gtktypebuiltins.h"
54
55 #define BORDER_SPACING  0
56 #define DEFAULT_IPADDING 1
57
58 /* Properties */
59 enum {
60   PROP_0,
61   PROP_PACK_DIRECTION,
62   PROP_CHILD_PACK_DIRECTION
63 };
64
65 struct _GtkMenuBarPrivate
66 {
67   GtkPackDirection pack_direction;
68   GtkPackDirection child_pack_direction;
69 };
70
71
72 static void gtk_menu_bar_set_property      (GObject             *object,
73                                             guint                prop_id,
74                                             const GValue        *value,
75                                             GParamSpec          *pspec);
76 static void gtk_menu_bar_get_property      (GObject             *object,
77                                             guint                prop_id,
78                                             GValue              *value,
79                                             GParamSpec          *pspec);
80 static void gtk_menu_bar_get_preferred_width (GtkWidget     *widget,
81                                               gint          *minimum,
82                                               gint          *natural);
83 static void gtk_menu_bar_get_preferred_height (GtkWidget    *widget,
84                                                gint         *minimum,
85                                                gint         *natural);
86 static void gtk_menu_bar_size_allocate     (GtkWidget       *widget,
87                                             GtkAllocation   *allocation);
88 static gint gtk_menu_bar_draw              (GtkWidget       *widget,
89                                             cairo_t         *cr);
90 static void gtk_menu_bar_hierarchy_changed (GtkWidget       *widget,
91                                             GtkWidget       *old_toplevel);
92 static gint gtk_menu_bar_get_popup_delay   (GtkMenuShell    *menu_shell);
93 static void gtk_menu_bar_move_current      (GtkMenuShell     *menu_shell,
94                                             GtkMenuDirectionType direction);
95
96 static GtkShadowType get_shadow_type   (GtkMenuBar      *menubar);
97
98 G_DEFINE_TYPE (GtkMenuBar, gtk_menu_bar, GTK_TYPE_MENU_SHELL)
99
100 static void
101 gtk_menu_bar_class_init (GtkMenuBarClass *class)
102 {
103   GObjectClass *gobject_class;
104   GtkWidgetClass *widget_class;
105   GtkMenuShellClass *menu_shell_class;
106
107   GtkBindingSet *binding_set;
108
109   gobject_class = (GObjectClass*) class;
110   widget_class = (GtkWidgetClass*) class;
111   menu_shell_class = (GtkMenuShellClass*) class;
112
113   gobject_class->get_property = gtk_menu_bar_get_property;
114   gobject_class->set_property = gtk_menu_bar_set_property;
115
116   widget_class->get_preferred_width = gtk_menu_bar_get_preferred_width;
117   widget_class->get_preferred_height = gtk_menu_bar_get_preferred_height;
118   widget_class->size_allocate = gtk_menu_bar_size_allocate;
119   widget_class->draw = gtk_menu_bar_draw;
120   widget_class->hierarchy_changed = gtk_menu_bar_hierarchy_changed;
121   
122   menu_shell_class->submenu_placement = GTK_TOP_BOTTOM;
123   menu_shell_class->get_popup_delay = gtk_menu_bar_get_popup_delay;
124   menu_shell_class->move_current = gtk_menu_bar_move_current;
125
126   binding_set = gtk_binding_set_by_class (class);
127   gtk_binding_entry_add_signal (binding_set,
128                                 GDK_KEY_Left, 0,
129                                 "move-current", 1,
130                                 GTK_TYPE_MENU_DIRECTION_TYPE,
131                                 GTK_MENU_DIR_PREV);
132   gtk_binding_entry_add_signal (binding_set,
133                                 GDK_KEY_KP_Left, 0,
134                                 "move-current", 1,
135                                 GTK_TYPE_MENU_DIRECTION_TYPE,
136                                 GTK_MENU_DIR_PREV);
137   gtk_binding_entry_add_signal (binding_set,
138                                 GDK_KEY_Right, 0,
139                                 "move-current", 1,
140                                 GTK_TYPE_MENU_DIRECTION_TYPE,
141                                 GTK_MENU_DIR_NEXT);
142   gtk_binding_entry_add_signal (binding_set,
143                                 GDK_KEY_KP_Right, 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_KEY_Up, 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_KEY_KP_Up, 0,
154                                 "move-current", 1,
155                                 GTK_TYPE_MENU_DIRECTION_TYPE,
156                                 GTK_MENU_DIR_PARENT);
157   gtk_binding_entry_add_signal (binding_set,
158                                 GDK_KEY_Down, 0,
159                                 "move-current", 1,
160                                 GTK_TYPE_MENU_DIRECTION_TYPE,
161                                 GTK_MENU_DIR_CHILD);
162   gtk_binding_entry_add_signal (binding_set,
163                                 GDK_KEY_KP_Down, 0,
164                                 "move-current", 1,
165                                 GTK_TYPE_MENU_DIRECTION_TYPE,
166                                 GTK_MENU_DIR_CHILD);
167
168   /**
169    * GtkMenuBar:pack-direction:
170    *
171    * The pack direction of the menubar. It determines how
172    * menuitems are arranged in the menubar.
173    *
174    * Since: 2.8
175    */
176   g_object_class_install_property (gobject_class,
177                                    PROP_PACK_DIRECTION,
178                                    g_param_spec_enum ("pack-direction",
179                                                       P_("Pack direction"),
180                                                       P_("The pack direction of the menubar"),
181                                                       GTK_TYPE_PACK_DIRECTION,
182                                                       GTK_PACK_DIRECTION_LTR,
183                                                       GTK_PARAM_READWRITE));
184   
185   /**
186    * GtkMenuBar:child-pack-direction:
187    *
188    * The child pack direction of the menubar. It determines how
189    * the widgets contained in child menuitems are arranged.
190    *
191    * Since: 2.8
192    */
193   g_object_class_install_property (gobject_class,
194                                    PROP_CHILD_PACK_DIRECTION,
195                                    g_param_spec_enum ("child-pack-direction",
196                                                       P_("Child Pack direction"),
197                                                       P_("The child pack direction of the menubar"),
198                                                       GTK_TYPE_PACK_DIRECTION,
199                                                       GTK_PACK_DIRECTION_LTR,
200                                                       GTK_PARAM_READWRITE));
201   
202
203   gtk_widget_class_install_style_property (widget_class,
204                                            g_param_spec_enum ("shadow-type",
205                                                               P_("Shadow type"),
206                                                               P_("Style of bevel around the menubar"),
207                                                               GTK_TYPE_SHADOW_TYPE,
208                                                               GTK_SHADOW_OUT,
209                                                               GTK_PARAM_READABLE));
210
211   gtk_widget_class_install_style_property (widget_class,
212                                            g_param_spec_int ("internal-padding",
213                                                              P_("Internal padding"),
214                                                              P_("Amount of border space between the menubar shadow and the menu items"),
215                                                              0,
216                                                              G_MAXINT,
217                                                              DEFAULT_IPADDING,
218                                                              GTK_PARAM_READABLE));
219
220   g_type_class_add_private (gobject_class, sizeof (GtkMenuBarPrivate));
221 }
222
223 static void
224 gtk_menu_bar_init (GtkMenuBar *menu_bar)
225 {
226   GtkStyleContext *context;
227
228   menu_bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (menu_bar,
229                                                 GTK_TYPE_MENU_BAR,
230                                                 GtkMenuBarPrivate);
231
232   context = gtk_widget_get_style_context (GTK_WIDGET (menu_bar));
233   gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUBAR);
234 }
235
236 /**
237  * gtk_menu_bar_new:
238  *
239  * Creates a new #GtkMenuBar
240  *
241  * Returns: the new menu bar, as a #GtkWidget
242  */
243 GtkWidget*
244 gtk_menu_bar_new (void)
245 {
246   return g_object_new (GTK_TYPE_MENU_BAR, NULL);
247 }
248
249 static void
250 gtk_menu_bar_set_property (GObject      *object,
251                            guint         prop_id,
252                            const GValue *value,
253                            GParamSpec   *pspec)
254 {
255   GtkMenuBar *menubar = GTK_MENU_BAR (object);
256   
257   switch (prop_id)
258     {
259     case PROP_PACK_DIRECTION:
260       gtk_menu_bar_set_pack_direction (menubar, g_value_get_enum (value));
261       break;
262     case PROP_CHILD_PACK_DIRECTION:
263       gtk_menu_bar_set_child_pack_direction (menubar, g_value_get_enum (value));
264       break;
265     default:
266       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267       break;
268     }
269 }
270
271 static void
272 gtk_menu_bar_get_property (GObject    *object,
273                            guint       prop_id,
274                            GValue     *value,
275                            GParamSpec *pspec)
276 {
277   GtkMenuBar *menubar = GTK_MENU_BAR (object);
278   
279   switch (prop_id)
280     {
281     case PROP_PACK_DIRECTION:
282       g_value_set_enum (value, gtk_menu_bar_get_pack_direction (menubar));
283       break;
284     case PROP_CHILD_PACK_DIRECTION:
285       g_value_set_enum (value, gtk_menu_bar_get_child_pack_direction (menubar));
286       break;
287     default:
288       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
289       break;
290     }
291 }
292
293 static void
294 gtk_menu_bar_size_request (GtkWidget      *widget,
295                            GtkOrientation  orientation,
296                            gint           *minimum,
297                            gint           *natural)
298 {
299   GtkMenuBar *menu_bar;
300   GtkMenuBarPrivate *priv;
301   GtkMenuShell *menu_shell;
302   GtkWidget *child;
303   GList *children;
304   GtkRequisition child_requisition;
305   gint ipadding;
306   guint border_width;
307   gboolean use_toggle_size;
308   gint size = 0;
309
310   menu_bar = GTK_MENU_BAR (widget);
311   menu_shell = GTK_MENU_SHELL (widget);
312   priv = menu_bar->priv;
313
314   children = menu_shell->priv->children;
315
316   if (priv->child_pack_direction == GTK_PACK_DIRECTION_LTR ||
317       priv->child_pack_direction == GTK_PACK_DIRECTION_RTL)
318     use_toggle_size = (orientation == GTK_ORIENTATION_HORIZONTAL);
319   else
320     use_toggle_size = (orientation == GTK_ORIENTATION_VERTICAL);
321
322   while (children)
323     {
324       child = children->data;
325       children = children->next;
326
327       if (gtk_widget_get_visible (child))
328         {
329           gtk_widget_get_preferred_size (child, &child_requisition, NULL);
330
331           if (use_toggle_size)
332             {
333               gint toggle_size;
334
335               gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child),
336                                                  &toggle_size);
337
338               child_requisition.width += toggle_size;
339               child_requisition.height += toggle_size;
340             }
341
342           if (priv->pack_direction == GTK_PACK_DIRECTION_LTR ||
343               priv->pack_direction == GTK_PACK_DIRECTION_RTL)
344             {
345               if (orientation == GTK_ORIENTATION_HORIZONTAL)
346                 size += child_requisition.width;
347               else
348                 size = MAX (size, child_requisition.height);
349             }
350           else
351             {
352               if (orientation == GTK_ORIENTATION_HORIZONTAL)
353                 size = MAX (size, child_requisition.width);
354               else
355                 size += child_requisition.height;
356             }
357         }
358     }
359
360   gtk_widget_style_get (widget, "internal-padding", &ipadding, NULL);
361
362   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu_bar));
363   size += (border_width + ipadding + BORDER_SPACING) * 2;
364
365   if (get_shadow_type (menu_bar) != GTK_SHADOW_NONE)
366     {
367       GtkStyleContext *context;
368       GtkBorder *border;
369
370       context = gtk_widget_get_style_context (widget);
371
372       gtk_style_context_get (context, 0,
373                              "border-width", &border,
374                              NULL);
375
376       if (orientation == GTK_ORIENTATION_HORIZONTAL)
377         size += border->left + border->right;
378       else
379         size += border->top + border->bottom;
380       gtk_border_free (border);
381     }
382
383   *minimum = *natural = size;
384 }
385
386 static void
387 gtk_menu_bar_get_preferred_width (GtkWidget *widget,
388                                   gint      *minimum,
389                                   gint      *natural)
390 {
391   gtk_menu_bar_size_request (widget, GTK_ORIENTATION_HORIZONTAL, minimum, natural);
392 }
393
394 static void
395 gtk_menu_bar_get_preferred_height (GtkWidget *widget,
396                                    gint      *minimum,
397                                    gint      *natural)
398 {
399   gtk_menu_bar_size_request (widget, GTK_ORIENTATION_VERTICAL, minimum, natural);
400 }
401
402 static void
403 gtk_menu_bar_size_allocate (GtkWidget     *widget,
404                             GtkAllocation *allocation)
405 {
406   GtkMenuBar *menu_bar;
407   GtkMenuShell *menu_shell;
408   GtkMenuBarPrivate *priv;
409   GtkWidget *child;
410   GList *children;
411   GtkAllocation child_allocation;
412   GtkRequisition child_requisition;
413   guint offset;
414   GtkTextDirection direction;
415   gint ltr_x, ltr_y;
416   gint ipadding;
417   guint border_width;
418
419   g_return_if_fail (GTK_IS_MENU_BAR (widget));
420   g_return_if_fail (allocation != NULL);
421
422   menu_bar = GTK_MENU_BAR (widget);
423   menu_shell = GTK_MENU_SHELL (widget);
424   priv = menu_bar->priv;
425
426   direction = gtk_widget_get_direction (widget);
427
428   gtk_widget_set_allocation (widget, allocation);
429
430   if (gtk_widget_get_realized (widget))
431     gdk_window_move_resize (gtk_widget_get_window (widget),
432                             allocation->x, allocation->y,
433                             allocation->width, allocation->height);
434
435   gtk_widget_style_get (widget, "internal-padding", &ipadding, NULL);
436   
437   if (menu_shell->priv->children)
438     {
439       border_width = gtk_container_get_border_width (GTK_CONTAINER (menu_bar));
440       child_allocation.x = (border_width +
441                             ipadding + 
442                             BORDER_SPACING);
443       child_allocation.y = (border_width +
444                             BORDER_SPACING);
445       
446       if (get_shadow_type (menu_bar) != GTK_SHADOW_NONE)
447         {
448           GtkStyleContext *context;
449           GtkBorder *border;
450
451           context = gtk_widget_get_style_context (widget);
452           gtk_style_context_get (context, 0,
453                                  "border-width", &border,
454                                  NULL);
455
456           child_allocation.x += border->left;
457           child_allocation.y += border->top;
458
459           gtk_border_free (border);
460         }
461       
462       if (priv->pack_direction == GTK_PACK_DIRECTION_LTR ||
463           priv->pack_direction == GTK_PACK_DIRECTION_RTL)
464         {
465           child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
466
467           offset = child_allocation.x;  /* Window edge to menubar start */
468           ltr_x = child_allocation.x;
469
470           children = menu_shell->priv->children;
471           while (children)
472             {
473               gint toggle_size;
474
475               child = children->data;
476               children = children->next;
477               
478               gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child),
479                                                  &toggle_size);
480               gtk_widget_get_preferred_size (child, &child_requisition, NULL);
481
482               if (priv->child_pack_direction == GTK_PACK_DIRECTION_LTR ||
483                   priv->child_pack_direction == GTK_PACK_DIRECTION_RTL)
484                 child_requisition.width += toggle_size;
485               else
486                 child_requisition.height += toggle_size;
487
488               /* Support for the right justified help menu */
489               if (children == NULL &&
490                   GTK_IS_MENU_ITEM (child) &&
491                   GTK_MENU_ITEM (child)->priv->right_justify)
492                 {
493                   ltr_x = allocation->width -
494                     child_requisition.width - offset;
495                 }
496               if (gtk_widget_get_visible (child))
497                 {
498                   if ((direction == GTK_TEXT_DIR_LTR) == (priv->pack_direction == GTK_PACK_DIRECTION_LTR))
499                     child_allocation.x = ltr_x;
500                   else
501                     child_allocation.x = allocation->width -
502                       child_requisition.width - ltr_x; 
503                   
504                   child_allocation.width = child_requisition.width;
505                   
506                   gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
507                                                       toggle_size);
508                   gtk_widget_size_allocate (child, &child_allocation);
509                   
510                   ltr_x += child_allocation.width;
511                 }
512             }
513         }
514       else
515         {
516           child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
517
518           offset = child_allocation.y;  /* Window edge to menubar start */
519           ltr_y = child_allocation.y;
520
521           children = menu_shell->priv->children;
522           while (children)
523             {
524               gint toggle_size;
525
526               child = children->data;
527               children = children->next;
528               
529               gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child),
530                                                  &toggle_size);
531               gtk_widget_get_preferred_size (child, &child_requisition, NULL);
532
533               if (priv->child_pack_direction == GTK_PACK_DIRECTION_LTR ||
534                   priv->child_pack_direction == GTK_PACK_DIRECTION_RTL)
535                 child_requisition.width += toggle_size;
536               else
537                 child_requisition.height += toggle_size;
538
539               /* Support for the right justified help menu */
540               if (children == NULL &&
541                   GTK_IS_MENU_ITEM (child) &&
542                   GTK_MENU_ITEM (child)->priv->right_justify)
543                 {
544                   ltr_y = allocation->height -
545                     child_requisition.height - offset;
546                 }
547               if (gtk_widget_get_visible (child))
548                 {
549                   if ((direction == GTK_TEXT_DIR_LTR) ==
550                       (priv->pack_direction == GTK_PACK_DIRECTION_TTB))
551                     child_allocation.y = ltr_y;
552                   else
553                     child_allocation.y = allocation->height -
554                       child_requisition.height - ltr_y; 
555                   child_allocation.height = child_requisition.height;
556                   
557                   gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
558                                                       toggle_size);
559                   gtk_widget_size_allocate (child, &child_allocation);
560                   
561                   ltr_y += child_allocation.height;
562                 }
563             }
564         }
565     }
566 }
567
568 static gint
569 gtk_menu_bar_draw (GtkWidget *widget,
570                    cairo_t   *cr)
571 {
572   GtkStyleContext *context;
573   GtkStateFlags state;
574   int border;
575
576   border = gtk_container_get_border_width (GTK_CONTAINER (widget));
577   context = gtk_widget_get_style_context (widget);
578
579   state = gtk_widget_get_state_flags (widget);
580   gtk_style_context_set_state (context, state);
581
582   if (get_shadow_type (GTK_MENU_BAR (widget)) != GTK_SHADOW_NONE)
583     gtk_render_background (context, cr,
584                            border, border,
585                            gtk_widget_get_allocated_width (widget) - border * 2,
586                            gtk_widget_get_allocated_height (widget) - border * 2);
587
588   gtk_render_frame (context, cr,
589                     border, border,
590                     gtk_widget_get_allocated_width (widget) - border * 2,
591                     gtk_widget_get_allocated_height (widget) - border * 2);
592
593   GTK_WIDGET_CLASS (gtk_menu_bar_parent_class)->draw (widget, cr);
594
595   return FALSE;
596 }
597
598 static GList *
599 get_menu_bars (GtkWindow *window)
600 {
601   return g_object_get_data (G_OBJECT (window), "gtk-menu-bar-list");
602 }
603
604 static GList *
605 get_viewable_menu_bars (GtkWindow *window)
606 {
607   GList *menu_bars;
608   GList *viewable_menu_bars = NULL;
609
610   for (menu_bars = get_menu_bars (window);
611        menu_bars;
612        menu_bars = menu_bars->next)
613     {
614       GtkWidget *widget = menu_bars->data;
615       gboolean viewable = TRUE;
616       
617       while (widget)
618         {
619           if (!gtk_widget_get_mapped (widget))
620             viewable = FALSE;
621
622           widget = gtk_widget_get_parent (widget);
623         }
624
625       if (viewable)
626         viewable_menu_bars = g_list_prepend (viewable_menu_bars, menu_bars->data);
627     }
628
629   return g_list_reverse (viewable_menu_bars);
630 }
631
632 static void
633 set_menu_bars (GtkWindow *window,
634                GList     *menubars)
635 {
636   g_object_set_data (G_OBJECT (window), I_("gtk-menu-bar-list"), menubars);
637 }
638
639 static gboolean
640 window_key_press_handler (GtkWidget   *widget,
641                           GdkEventKey *event,
642                           gpointer     data)
643 {
644   gchar *accel = NULL;
645   gboolean retval = FALSE;
646   
647   g_object_get (gtk_widget_get_settings (widget),
648                 "gtk-menu-bar-accel", &accel,
649                 NULL);
650
651   if (accel && *accel)
652     {
653       guint keyval = 0;
654       GdkModifierType mods = 0;
655
656       gtk_accelerator_parse (accel, &keyval, &mods);
657
658       if (keyval == 0)
659         g_warning ("Failed to parse menu bar accelerator '%s'\n", accel);
660
661       /* FIXME this is wrong, needs to be in the global accel resolution
662        * thing, to properly consider i18n etc., but that probably requires
663        * AccelGroup changes etc.
664        */
665       if (event->keyval == keyval &&
666           ((event->state & gtk_accelerator_get_default_mod_mask ()) ==
667            (mods & gtk_accelerator_get_default_mod_mask ())))
668         {
669           GList *tmp_menubars = get_viewable_menu_bars (GTK_WINDOW (widget));
670           GList *menubars;
671
672           menubars = _gtk_container_focus_sort (GTK_CONTAINER (widget), tmp_menubars,
673                                                 GTK_DIR_TAB_FORWARD, NULL);
674           g_list_free (tmp_menubars);
675           
676           if (menubars)
677             {
678               GtkMenuShell *menu_shell = GTK_MENU_SHELL (menubars->data);
679
680               _gtk_menu_shell_set_keyboard_mode (menu_shell, TRUE);
681               gtk_menu_shell_select_first (menu_shell, FALSE);
682               
683               g_list_free (menubars);
684               
685               retval = TRUE;          
686             }
687         }
688     }
689
690   g_free (accel);
691
692   return retval;
693 }
694
695 static void
696 add_to_window (GtkWindow  *window,
697                GtkMenuBar *menubar)
698 {
699   GList *menubars = get_menu_bars (window);
700
701   if (!menubars)
702     {
703       g_signal_connect (window,
704                         "key-press-event",
705                         G_CALLBACK (window_key_press_handler),
706                         NULL);
707     }
708
709   set_menu_bars (window, g_list_prepend (menubars, menubar));
710 }
711
712 static void
713 remove_from_window (GtkWindow  *window,
714                     GtkMenuBar *menubar)
715 {
716   GList *menubars = get_menu_bars (window);
717
718   menubars = g_list_remove (menubars, menubar);
719
720   if (!menubars)
721     {
722       g_signal_handlers_disconnect_by_func (window,
723                                             window_key_press_handler,
724                                             NULL);
725     }
726
727   set_menu_bars (window, menubars);
728 }
729
730 static void
731 gtk_menu_bar_hierarchy_changed (GtkWidget *widget,
732                                 GtkWidget *old_toplevel)
733 {
734   GtkWidget *toplevel;  
735   GtkMenuBar *menubar;
736
737   menubar = GTK_MENU_BAR (widget);
738
739   toplevel = gtk_widget_get_toplevel (widget);
740
741   if (old_toplevel)
742     remove_from_window (GTK_WINDOW (old_toplevel), menubar);
743   
744   if (gtk_widget_is_toplevel (toplevel))
745     add_to_window (GTK_WINDOW (toplevel), menubar);
746 }
747
748 /**
749  * _gtk_menu_bar_cycle_focus:
750  * @menubar: a #GtkMenuBar
751  * @dir: direction in which to cycle the focus
752  * 
753  * Move the focus between menubars in the toplevel.
754  **/
755 void
756 _gtk_menu_bar_cycle_focus (GtkMenuBar       *menubar,
757                            GtkDirectionType  dir)
758 {
759   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menubar));
760   GtkMenuItem *to_activate = NULL;
761
762   if (gtk_widget_is_toplevel (toplevel))
763     {
764       GList *tmp_menubars = get_viewable_menu_bars (GTK_WINDOW (toplevel));
765       GList *menubars;
766       GList *current;
767
768       menubars = _gtk_container_focus_sort (GTK_CONTAINER (toplevel), tmp_menubars,
769                                             dir, GTK_WIDGET (menubar));
770       g_list_free (tmp_menubars);
771
772       if (menubars)
773         {
774           current = g_list_find (menubars, menubar);
775
776           if (current && current->next)
777             {
778               GtkMenuShell *new_menushell = GTK_MENU_SHELL (current->next->data);
779               if (new_menushell->priv->children)
780                 to_activate = new_menushell->priv->children->data;
781             }
782         }
783           
784       g_list_free (menubars);
785     }
786
787   gtk_menu_shell_cancel (GTK_MENU_SHELL (menubar));
788
789   if (to_activate)
790     g_signal_emit_by_name (to_activate, "activate_item");
791 }
792
793 static GtkShadowType
794 get_shadow_type (GtkMenuBar *menubar)
795 {
796   GtkShadowType shadow_type = GTK_SHADOW_OUT;
797   
798   gtk_widget_style_get (GTK_WIDGET (menubar),
799                         "shadow-type", &shadow_type,
800                         NULL);
801
802   return shadow_type;
803 }
804
805 static gint
806 gtk_menu_bar_get_popup_delay (GtkMenuShell *menu_shell)
807 {
808   gint popup_delay;
809   
810   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
811                 "gtk-menu-bar-popup-delay", &popup_delay,
812                 NULL);
813
814   return popup_delay;
815 }
816
817 static void
818 gtk_menu_bar_move_current (GtkMenuShell         *menu_shell,
819                            GtkMenuDirectionType  direction)
820 {
821   GtkMenuBar *menubar = GTK_MENU_BAR (menu_shell);
822   GtkTextDirection text_dir;
823   GtkPackDirection pack_dir;
824
825   text_dir = gtk_widget_get_direction (GTK_WIDGET (menubar));
826   pack_dir = gtk_menu_bar_get_pack_direction (menubar);
827   
828   if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL)
829      {
830       if ((text_dir == GTK_TEXT_DIR_RTL) == (pack_dir == GTK_PACK_DIRECTION_LTR))
831         {
832           switch (direction) 
833             {      
834             case GTK_MENU_DIR_PREV:
835               direction = GTK_MENU_DIR_NEXT;
836               break;
837             case GTK_MENU_DIR_NEXT:
838               direction = GTK_MENU_DIR_PREV;
839               break;
840             default: ;
841             }
842         }
843     }
844   else
845     {
846       switch (direction) 
847         {
848         case GTK_MENU_DIR_PARENT:
849           if ((text_dir == GTK_TEXT_DIR_LTR) == (pack_dir == GTK_PACK_DIRECTION_TTB))
850             direction = GTK_MENU_DIR_PREV;
851           else
852             direction = GTK_MENU_DIR_NEXT;
853           break;
854         case GTK_MENU_DIR_CHILD:
855           if ((text_dir == GTK_TEXT_DIR_LTR) == (pack_dir == GTK_PACK_DIRECTION_TTB))
856             direction = GTK_MENU_DIR_NEXT;
857           else
858             direction = GTK_MENU_DIR_PREV;
859           break;
860         case GTK_MENU_DIR_PREV:
861           if (text_dir == GTK_TEXT_DIR_RTL)       
862             direction = GTK_MENU_DIR_CHILD;
863           else
864             direction = GTK_MENU_DIR_PARENT;
865           break;
866         case GTK_MENU_DIR_NEXT:
867           if (text_dir == GTK_TEXT_DIR_RTL)       
868             direction = GTK_MENU_DIR_PARENT;
869           else
870             direction = GTK_MENU_DIR_CHILD;
871           break;
872         default: ;
873         }
874     }
875   
876   GTK_MENU_SHELL_CLASS (gtk_menu_bar_parent_class)->move_current (menu_shell, direction);
877 }
878
879 /**
880  * gtk_menu_bar_get_pack_direction:
881  * @menubar: a #GtkMenuBar
882  * 
883  * Retrieves the current pack direction of the menubar. 
884  * See gtk_menu_bar_set_pack_direction().
885  *
886  * Return value: the pack direction
887  *
888  * Since: 2.8
889  */
890 GtkPackDirection
891 gtk_menu_bar_get_pack_direction (GtkMenuBar *menubar)
892 {
893   g_return_val_if_fail (GTK_IS_MENU_BAR (menubar), 
894                         GTK_PACK_DIRECTION_LTR);
895
896   return menubar->priv->pack_direction;
897 }
898
899 /**
900  * gtk_menu_bar_set_pack_direction:
901  * @menubar: a #GtkMenuBar
902  * @pack_dir: a new #GtkPackDirection
903  * 
904  * Sets how items should be packed inside a menubar.
905  * 
906  * Since: 2.8
907  */
908 void
909 gtk_menu_bar_set_pack_direction (GtkMenuBar       *menubar,
910                                  GtkPackDirection  pack_dir)
911 {
912   GtkMenuBarPrivate *priv;
913   GList *l;
914
915   g_return_if_fail (GTK_IS_MENU_BAR (menubar));
916
917   priv = menubar->priv;
918
919   if (priv->pack_direction != pack_dir)
920     {
921       priv->pack_direction = pack_dir;
922
923       gtk_widget_queue_resize (GTK_WIDGET (menubar));
924
925       for (l = GTK_MENU_SHELL (menubar)->priv->children; l; l = l->next)
926         gtk_widget_queue_resize (GTK_WIDGET (l->data));
927
928       g_object_notify (G_OBJECT (menubar), "pack-direction");
929     }
930 }
931
932 /**
933  * gtk_menu_bar_get_child_pack_direction:
934  * @menubar: a #GtkMenuBar
935  * 
936  * Retrieves the current child pack direction of the menubar.
937  * See gtk_menu_bar_set_child_pack_direction().
938  *
939  * Return value: the child pack direction
940  *
941  * Since: 2.8
942  */
943 GtkPackDirection
944 gtk_menu_bar_get_child_pack_direction (GtkMenuBar *menubar)
945 {
946   g_return_val_if_fail (GTK_IS_MENU_BAR (menubar), 
947                         GTK_PACK_DIRECTION_LTR);
948
949   return menubar->priv->child_pack_direction;
950 }
951
952 /**
953  * gtk_menu_bar_set_child_pack_direction:
954  * @menubar: a #GtkMenuBar
955  * @child_pack_dir: a new #GtkPackDirection
956  * 
957  * Sets how widgets should be packed inside the children of a menubar.
958  * 
959  * Since: 2.8
960  */
961 void
962 gtk_menu_bar_set_child_pack_direction (GtkMenuBar       *menubar,
963                                        GtkPackDirection  child_pack_dir)
964 {
965   GtkMenuBarPrivate *priv;
966   GList *l;
967
968   g_return_if_fail (GTK_IS_MENU_BAR (menubar));
969
970   priv = menubar->priv;
971
972   if (priv->child_pack_direction != child_pack_dir)
973     {
974       priv->child_pack_direction = child_pack_dir;
975
976       gtk_widget_queue_resize (GTK_WIDGET (menubar));
977
978       for (l = GTK_MENU_SHELL (menubar)->priv->children; l; l = l->next)
979         gtk_widget_queue_resize (GTK_WIDGET (l->data));
980
981       g_object_notify (G_OBJECT (menubar), "child-pack-direction");
982     }
983 }