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