]> Pileus Git - ~andy/gtk/blob - gtk/gtktoolbar.c
toolbar: don't leak the sibling path
[~andy/gtk] / gtk / gtktoolbar.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * GtkToolbar copyright (C) Federico Mena
4  *
5  * Copyright (C) 2002 Anders Carlsson <andersca@gnome.org>
6  * Copyright (C) 2002 James Henstridge <james@daa.com.au>
7  * Copyright (C) 2003, 2004 Soeren Sandmann <sandmann@daimi.au.dk>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /*
26  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
27  * file for a list of people on the GTK+ Team.  See the ChangeLog
28  * files for a list of changes.  These files are distributed with
29  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
30  */
31
32
33 #include "config.h"
34
35 #include <math.h>
36 #include <string.h>
37
38 #include "gtktoolbar.h"
39
40 #include "gtkarrow.h"
41 #include "gtkbindings.h"
42 #include "gtkhbox.h"
43 #include "gtkimage.h"
44 #include "gtklabel.h"
45 #include "gtkmainprivate.h"
46 #include "gtkmarshalers.h"
47 #include "gtkmenu.h"
48 #include "gtkorientable.h"
49 #include "gtkradiobutton.h"
50 #include "gtkradiotoolbutton.h"
51 #include "gtkseparatormenuitem.h"
52 #include "gtkseparatortoolitem.h"
53 #include "gtkstock.h"
54 #include "gtktoolshell.h"
55 #include "gtkvbox.h"
56 #include "gtkprivate.h"
57 #include "gtkintl.h"
58 #include "gtktypebuiltins.h"
59
60
61 /**
62  * SECTION:gtktoolbar
63  * @Short_description: Create bars of buttons and other widgets
64  * @Title: GtkToolbar
65  * @See_also: #GtkToolItem
66  *
67  * A toolbar is created with a call to gtk_toolbar_new().
68  *
69  * A toolbar can contain instances of a subclass of #GtkToolItem. To add
70  * a #GtkToolItem to the a toolbar, use gtk_toolbar_insert(). To remove
71  * an item from the toolbar use gtk_container_remove(). To add a button
72  * to the toolbar, add an instance of #GtkToolButton.
73  *
74  * Toolbar items can be visually grouped by adding instances of
75  * #GtkSeparatorToolItem to the toolbar. If the GtkToolbar child property
76  * "expand" is #TRUE and the property #GtkSeparatorToolItem:draw is set to
77  * #FALSE, the effect is to force all following items to the end of the toolbar.
78  *
79  * Creating a context menu for the toolbar can be done by connecting to
80  * the #GtkToolbar::popup-context-menu signal.
81  */
82
83
84 typedef struct _ToolbarContent ToolbarContent;
85
86 #define DEFAULT_IPADDING    0
87
88 #define DEFAULT_SPACE_SIZE  12
89 #define DEFAULT_SPACE_STYLE GTK_TOOLBAR_SPACE_LINE
90 #define SPACE_LINE_DIVISION 10.0
91 #define SPACE_LINE_START    2.0
92 #define SPACE_LINE_END      8.0
93
94 #define DEFAULT_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR
95 #define DEFAULT_TOOLBAR_STYLE GTK_TOOLBAR_BOTH
96 #define DEFAULT_ANIMATION_STATE TRUE
97
98 #define MAX_HOMOGENEOUS_N_CHARS 13 /* Items that are wider than this do not participate
99                                     * in the homogeneous game. In units of
100                                     * pango_font_get_estimated_char_width().
101                                     */
102 #define SLIDE_SPEED 600.0          /* How fast the items slide, in pixels per second */
103 #define ACCEL_THRESHOLD 0.18       /* After how much time in seconds will items start speeding up */
104
105
106 struct _GtkToolbarPrivate
107 {
108   GtkMenu         *menu;
109   GtkSettings     *settings;
110
111   GtkIconSize      icon_size;
112   GtkToolbarStyle  style;
113
114   GtkToolItem     *highlight_tool_item;
115   GtkWidget       *arrow;
116   GtkWidget       *arrow_button;
117
118   GdkWindow       *event_window;
119
120   GList           *content;
121
122   GTimer          *timer;
123
124   GtkWidgetPath   *sibling_path;
125
126   gulong           settings_connection;
127
128   gint             idle_id;
129   gint             button_maxw;         /* maximum width of homogeneous children */
130   gint             button_maxh;         /* maximum height of homogeneous children */
131   gint             max_homogeneous_pixels;
132   gint             num_children;
133
134   GtkOrientation   orientation;
135
136   guint            animation : 1;
137   guint            icon_size_set : 1;
138   guint            is_sliding : 1;
139   guint            need_rebuild : 1;  /* whether the overflow menu should be regenerated */
140   guint            need_sync : 1;
141   guint            show_arrow : 1;
142   guint            style_set     : 1;
143 };
144
145 /* Properties */
146 enum {
147   PROP_0,
148   PROP_ORIENTATION,
149   PROP_TOOLBAR_STYLE,
150   PROP_SHOW_ARROW,
151   PROP_TOOLTIPS,
152   PROP_ICON_SIZE,
153   PROP_ICON_SIZE_SET
154 };
155
156 /* Child properties */
157 enum {
158   CHILD_PROP_0,
159   CHILD_PROP_EXPAND,
160   CHILD_PROP_HOMOGENEOUS
161 };
162
163 /* Signals */
164 enum {
165   ORIENTATION_CHANGED,
166   STYLE_CHANGED,
167   POPUP_CONTEXT_MENU,
168   FOCUS_HOME_OR_END,
169   LAST_SIGNAL
170 };
171
172 typedef enum {
173   NOT_ALLOCATED,
174   NORMAL,
175   HIDDEN,
176   OVERFLOWN
177 } ItemState;
178
179
180 static void       gtk_toolbar_set_property         (GObject             *object,
181                                                     guint                prop_id,
182                                                     const GValue        *value,
183                                                     GParamSpec          *pspec);
184 static void       gtk_toolbar_get_property         (GObject             *object,
185                                                     guint                prop_id,
186                                                     GValue              *value,
187                                                     GParamSpec          *pspec);
188 static gint       gtk_toolbar_draw                 (GtkWidget           *widget,
189                                                     cairo_t             *cr);
190 static void       gtk_toolbar_realize              (GtkWidget           *widget);
191 static void       gtk_toolbar_unrealize            (GtkWidget           *widget);
192 static void       gtk_toolbar_get_preferred_width  (GtkWidget           *widget,
193                                                     gint                *minimum,
194                                                     gint                *natural);
195 static void       gtk_toolbar_get_preferred_height (GtkWidget           *widget,
196                                                     gint                *minimum,
197                                                     gint                *natural);
198
199 static void       gtk_toolbar_size_allocate        (GtkWidget           *widget,
200                                                     GtkAllocation       *allocation);
201 static void       gtk_toolbar_style_updated        (GtkWidget           *widget);
202 static gboolean   gtk_toolbar_focus                (GtkWidget           *widget,
203                                                     GtkDirectionType     dir);
204 static void       gtk_toolbar_move_focus           (GtkWidget           *widget,
205                                                     GtkDirectionType     dir);
206 static void       gtk_toolbar_screen_changed       (GtkWidget           *widget,
207                                                     GdkScreen           *previous_screen);
208 static void       gtk_toolbar_map                  (GtkWidget           *widget);
209 static void       gtk_toolbar_unmap                (GtkWidget           *widget);
210 static void       gtk_toolbar_set_child_property   (GtkContainer        *container,
211                                                     GtkWidget           *child,
212                                                     guint                property_id,
213                                                     const GValue        *value,
214                                                     GParamSpec          *pspec);
215 static void       gtk_toolbar_get_child_property   (GtkContainer        *container,
216                                                     GtkWidget           *child,
217                                                     guint                property_id,
218                                                     GValue              *value,
219                                                     GParamSpec          *pspec);
220 static void       gtk_toolbar_finalize             (GObject             *object);
221 static void       gtk_toolbar_show_all             (GtkWidget           *widget);
222 static void       gtk_toolbar_add                  (GtkContainer        *container,
223                                                     GtkWidget           *widget);
224 static void       gtk_toolbar_remove               (GtkContainer        *container,
225                                                     GtkWidget           *widget);
226 static void       gtk_toolbar_forall               (GtkContainer        *container,
227                                                     gboolean             include_internals,
228                                                     GtkCallback          callback,
229                                                     gpointer             callback_data);
230 static GType      gtk_toolbar_child_type           (GtkContainer        *container);
231 static GtkWidgetPath * gtk_toolbar_get_path_for_child
232                                                   (GtkContainer        *container,
233                                                    GtkWidget           *child);
234 static void       gtk_toolbar_invalidate_order    (GtkToolbar           *toolbar);
235
236 static void       gtk_toolbar_orientation_changed  (GtkToolbar          *toolbar,
237                                                     GtkOrientation       orientation);
238 static void       gtk_toolbar_real_style_changed   (GtkToolbar          *toolbar,
239                                                     GtkToolbarStyle      style);
240 static gboolean   gtk_toolbar_focus_home_or_end    (GtkToolbar          *toolbar,
241                                                     gboolean             focus_home);
242 static gboolean   gtk_toolbar_button_press         (GtkWidget           *toolbar,
243                                                     GdkEventButton      *event);
244 static gboolean   gtk_toolbar_arrow_button_press   (GtkWidget           *button,
245                                                     GdkEventButton      *event,
246                                                     GtkToolbar          *toolbar);
247 static void       gtk_toolbar_arrow_button_clicked (GtkWidget           *button,
248                                                     GtkToolbar          *toolbar);
249 static void       gtk_toolbar_update_button_relief (GtkToolbar          *toolbar);
250 static gboolean   gtk_toolbar_popup_menu           (GtkWidget           *toolbar);
251 static void       gtk_toolbar_reconfigured         (GtkToolbar          *toolbar);
252
253 static GtkReliefStyle       get_button_relief    (GtkToolbar *toolbar);
254 static gint                 get_internal_padding (GtkToolbar *toolbar);
255 static gint                 get_max_child_expand (GtkToolbar *toolbar);
256 static GtkShadowType        get_shadow_type      (GtkToolbar *toolbar);
257
258 /* methods on ToolbarContent 'class' */
259 static ToolbarContent *toolbar_content_new_tool_item        (GtkToolbar          *toolbar,
260                                                              GtkToolItem         *item,
261                                                              gboolean             is_placeholder,
262                                                              gint                 pos);
263 static void            toolbar_content_remove               (ToolbarContent      *content,
264                                                              GtkToolbar          *toolbar);
265 static void            toolbar_content_free                 (ToolbarContent      *content);
266 static void            toolbar_content_draw                 (ToolbarContent      *content,
267                                                              GtkContainer        *container,
268                                                              cairo_t             *cr);
269 static gboolean        toolbar_content_visible              (ToolbarContent      *content,
270                                                              GtkToolbar          *toolbar);
271 static void            toolbar_content_size_request         (ToolbarContent      *content,
272                                                              GtkToolbar          *toolbar,
273                                                              GtkRequisition      *requisition);
274 static gboolean        toolbar_content_is_homogeneous       (ToolbarContent      *content,
275                                                              GtkToolbar          *toolbar);
276 static gboolean        toolbar_content_is_placeholder       (ToolbarContent      *content);
277 static gboolean        toolbar_content_disappearing         (ToolbarContent      *content);
278 static ItemState       toolbar_content_get_state            (ToolbarContent      *content);
279 static gboolean        toolbar_content_child_visible        (ToolbarContent      *content);
280 static void            toolbar_content_get_goal_allocation  (ToolbarContent      *content,
281                                                              GtkAllocation       *allocation);
282 static void            toolbar_content_get_allocation       (ToolbarContent      *content,
283                                                              GtkAllocation       *allocation);
284 static void            toolbar_content_set_start_allocation (ToolbarContent      *content,
285                                                              GtkAllocation       *new_start_allocation);
286 static void            toolbar_content_get_start_allocation (ToolbarContent      *content,
287                                                              GtkAllocation       *start_allocation);
288 static gboolean        toolbar_content_get_expand           (ToolbarContent      *content);
289 static void            toolbar_content_set_goal_allocation  (ToolbarContent      *content,
290                                                              GtkAllocation       *allocation);
291 static void            toolbar_content_set_child_visible    (ToolbarContent      *content,
292                                                              GtkToolbar          *toolbar,
293                                                              gboolean             visible);
294 static void            toolbar_content_size_allocate        (ToolbarContent      *content,
295                                                              GtkAllocation       *allocation);
296 static void            toolbar_content_set_state            (ToolbarContent      *content,
297                                                              ItemState            new_state);
298 static GtkWidget *     toolbar_content_get_widget           (ToolbarContent      *content);
299 static void            toolbar_content_set_disappearing     (ToolbarContent      *content,
300                                                              gboolean             disappearing);
301 static void            toolbar_content_set_size_request     (ToolbarContent      *content,
302                                                              gint                 width,
303                                                              gint                 height);
304 static void            toolbar_content_toolbar_reconfigured (ToolbarContent      *content,
305                                                              GtkToolbar          *toolbar);
306 static GtkWidget *     toolbar_content_retrieve_menu_item   (ToolbarContent      *content);
307 static gboolean        toolbar_content_has_proxy_menu_item  (ToolbarContent      *content);
308 static gboolean        toolbar_content_is_separator         (ToolbarContent      *content);
309 static void            toolbar_content_show_all             (ToolbarContent      *content);
310 static void            toolbar_content_set_expand           (ToolbarContent      *content,
311                                                              gboolean             expand);
312
313 static void            toolbar_tool_shell_iface_init        (GtkToolShellIface   *iface);
314 static GtkIconSize     toolbar_get_icon_size                (GtkToolShell        *shell);
315 static GtkOrientation  toolbar_get_orientation              (GtkToolShell        *shell);
316 static GtkToolbarStyle toolbar_get_style                    (GtkToolShell        *shell);
317 static GtkReliefStyle  toolbar_get_relief_style             (GtkToolShell        *shell);
318 static void            toolbar_rebuild_menu                 (GtkToolShell        *shell);
319
320
321 G_DEFINE_TYPE_WITH_CODE (GtkToolbar, gtk_toolbar, GTK_TYPE_CONTAINER,
322                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TOOL_SHELL,
323                                                 toolbar_tool_shell_iface_init)
324                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
325                                                 NULL))
326
327 static guint toolbar_signals[LAST_SIGNAL] = { 0 };
328
329
330 static void
331 add_arrow_bindings (GtkBindingSet   *binding_set,
332                     guint            keysym,
333                     GtkDirectionType dir)
334 {
335   guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
336   
337   gtk_binding_entry_add_signal (binding_set, keysym, 0,
338                                 "move-focus", 1,
339                                 GTK_TYPE_DIRECTION_TYPE, dir);
340   gtk_binding_entry_add_signal (binding_set, keypad_keysym, 0,
341                                 "move-focus", 1,
342                                 GTK_TYPE_DIRECTION_TYPE, dir);
343 }
344
345 static void
346 add_ctrl_tab_bindings (GtkBindingSet    *binding_set,
347                        GdkModifierType   modifiers,
348                        GtkDirectionType  direction)
349 {
350   gtk_binding_entry_add_signal (binding_set,
351                                 GDK_KEY_Tab, GDK_CONTROL_MASK | modifiers,
352                                 "move-focus", 1,
353                                 GTK_TYPE_DIRECTION_TYPE, direction);
354   gtk_binding_entry_add_signal (binding_set,
355                                 GDK_KEY_KP_Tab, GDK_CONTROL_MASK | modifiers,
356                                 "move-focus", 1,
357                                 GTK_TYPE_DIRECTION_TYPE, direction);
358 }
359
360 static void
361 gtk_toolbar_class_init (GtkToolbarClass *klass)
362 {
363   GObjectClass *gobject_class;
364   GtkWidgetClass *widget_class;
365   GtkContainerClass *container_class;
366   GtkBindingSet *binding_set;
367   
368   gobject_class = (GObjectClass *)klass;
369   widget_class = (GtkWidgetClass *)klass;
370   container_class = (GtkContainerClass *)klass;
371   
372   gobject_class->set_property = gtk_toolbar_set_property;
373   gobject_class->get_property = gtk_toolbar_get_property;
374   gobject_class->finalize = gtk_toolbar_finalize;
375   
376   widget_class->button_press_event = gtk_toolbar_button_press;
377   widget_class->draw = gtk_toolbar_draw;
378   widget_class->get_preferred_width = gtk_toolbar_get_preferred_width;
379   widget_class->get_preferred_height = gtk_toolbar_get_preferred_height;
380   widget_class->size_allocate = gtk_toolbar_size_allocate;
381   widget_class->style_updated = gtk_toolbar_style_updated;
382   widget_class->focus = gtk_toolbar_focus;
383
384   /* need to override the base class function via override_class_handler,
385    * because the signal slot is not available in GtkWidgetClass
386    */
387   g_signal_override_class_handler ("move-focus",
388                                    GTK_TYPE_TOOLBAR,
389                                    G_CALLBACK (gtk_toolbar_move_focus));
390
391   widget_class->screen_changed = gtk_toolbar_screen_changed;
392   widget_class->realize = gtk_toolbar_realize;
393   widget_class->unrealize = gtk_toolbar_unrealize;
394   widget_class->map = gtk_toolbar_map;
395   widget_class->unmap = gtk_toolbar_unmap;
396   widget_class->popup_menu = gtk_toolbar_popup_menu;
397   widget_class->show_all = gtk_toolbar_show_all;
398   
399   container_class->add    = gtk_toolbar_add;
400   container_class->remove = gtk_toolbar_remove;
401   container_class->forall = gtk_toolbar_forall;
402   container_class->child_type = gtk_toolbar_child_type;
403   container_class->get_child_property = gtk_toolbar_get_child_property;
404   container_class->set_child_property = gtk_toolbar_set_child_property;
405   container_class->get_path_for_child = gtk_toolbar_get_path_for_child;
406
407   klass->orientation_changed = gtk_toolbar_orientation_changed;
408   klass->style_changed = gtk_toolbar_real_style_changed;
409   
410   /**
411    * GtkToolbar::orientation-changed:
412    * @toolbar: the object which emitted the signal
413    * @orientation: the new #GtkOrientation of the toolbar
414    *
415    * Emitted when the orientation of the toolbar changes.
416    */
417   toolbar_signals[ORIENTATION_CHANGED] =
418     g_signal_new (I_("orientation-changed"),
419                   G_OBJECT_CLASS_TYPE (klass),
420                   G_SIGNAL_RUN_FIRST,
421                   G_STRUCT_OFFSET (GtkToolbarClass, orientation_changed),
422                   NULL, NULL,
423                   g_cclosure_marshal_VOID__ENUM,
424                   G_TYPE_NONE, 1,
425                   GTK_TYPE_ORIENTATION);
426   /**
427    * GtkToolbar::style-changed:
428    * @toolbar: The #GtkToolbar which emitted the signal
429    * @style: the new #GtkToolbarStyle of the toolbar
430    *
431    * Emitted when the style of the toolbar changes. 
432    */
433   toolbar_signals[STYLE_CHANGED] =
434     g_signal_new (I_("style-changed"),
435                   G_OBJECT_CLASS_TYPE (klass),
436                   G_SIGNAL_RUN_FIRST,
437                   G_STRUCT_OFFSET (GtkToolbarClass, style_changed),
438                   NULL, NULL,
439                   g_cclosure_marshal_VOID__ENUM,
440                   G_TYPE_NONE, 1,
441                   GTK_TYPE_TOOLBAR_STYLE);
442   /**
443    * GtkToolbar::popup-context-menu:
444    * @toolbar: the #GtkToolbar which emitted the signal
445    * @x: the x coordinate of the point where the menu should appear
446    * @y: the y coordinate of the point where the menu should appear
447    * @button: the mouse button the user pressed, or -1
448    *
449    * Emitted when the user right-clicks the toolbar or uses the
450    * keybinding to display a popup menu.
451    *
452    * Application developers should handle this signal if they want
453    * to display a context menu on the toolbar. The context-menu should
454    * appear at the coordinates given by @x and @y. The mouse button
455    * number is given by the @button parameter. If the menu was popped
456    * up using the keybaord, @button is -1.
457    *
458    * Return value: return %TRUE if the signal was handled, %FALSE if not
459    */
460   toolbar_signals[POPUP_CONTEXT_MENU] =
461     g_signal_new (I_("popup-context-menu"),
462                   G_OBJECT_CLASS_TYPE (klass),
463                   G_SIGNAL_RUN_LAST,
464                   G_STRUCT_OFFSET (GtkToolbarClass, popup_context_menu),
465                   _gtk_boolean_handled_accumulator, NULL,
466                   _gtk_marshal_BOOLEAN__INT_INT_INT,
467                   G_TYPE_BOOLEAN, 3,
468                   G_TYPE_INT, G_TYPE_INT,
469                   G_TYPE_INT);
470
471   /**
472    * GtkToolbar::focus-home-or-end:
473    * @toolbar: the #GtkToolbar which emitted the signal
474    * @focus_home: %TRUE if the first item should be focused
475    *
476    * A keybinding signal used internally by GTK+. This signal can't
477    * be used in application code
478    *
479    * Return value: %TRUE if the signal was handled, %FALSE if not
480    */
481   toolbar_signals[FOCUS_HOME_OR_END] =
482     g_signal_new_class_handler (I_("focus-home-or-end"),
483                                 G_OBJECT_CLASS_TYPE (klass),
484                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
485                                 G_CALLBACK (gtk_toolbar_focus_home_or_end),
486                                 NULL, NULL,
487                                 _gtk_marshal_BOOLEAN__BOOLEAN,
488                                 G_TYPE_BOOLEAN, 1,
489                                 G_TYPE_BOOLEAN);
490
491   /* properties */
492   g_object_class_override_property (gobject_class,
493                                     PROP_ORIENTATION,
494                                     "orientation");
495
496   g_object_class_install_property (gobject_class,
497                                    PROP_TOOLBAR_STYLE,
498                                    g_param_spec_enum ("toolbar-style",
499                                                       P_("Toolbar Style"),
500                                                       P_("How to draw the toolbar"),
501                                                       GTK_TYPE_TOOLBAR_STYLE,
502                                                       DEFAULT_TOOLBAR_STYLE,
503                                                       GTK_PARAM_READWRITE));
504   g_object_class_install_property (gobject_class,
505                                    PROP_SHOW_ARROW,
506                                    g_param_spec_boolean ("show-arrow",
507                                                          P_("Show Arrow"),
508                                                          P_("If an arrow should be shown if the toolbar doesn't fit"),
509                                                          TRUE,
510                                                          GTK_PARAM_READWRITE));
511
512   /**
513    * GtkToolbar:icon-size:
514    *
515    * The size of the icons in a toolbar is normally determined by
516    * the toolbar-icon-size setting. When this property is set, it 
517    * overrides the setting. 
518    * 
519    * This should only be used for special-purpose toolbars, normal
520    * application toolbars should respect the user preferences for the
521    * size of icons.
522    *
523    * Since: 2.10
524    */
525   g_object_class_install_property (gobject_class,
526                                    PROP_ICON_SIZE,
527                                    g_param_spec_int ("icon-size",
528                                                      P_("Icon size"),
529                                                      P_("Size of icons in this toolbar"),
530                                                      0, G_MAXINT,
531                                                      DEFAULT_ICON_SIZE,
532                                                      GTK_PARAM_READWRITE));  
533
534   /**
535    * GtkToolbar:icon-size-set:
536    *
537    * Is %TRUE if the icon-size property has been set.
538    *
539    * Since: 2.10
540    */
541   g_object_class_install_property (gobject_class,
542                                    PROP_ICON_SIZE_SET,
543                                    g_param_spec_boolean ("icon-size-set",
544                                                          P_("Icon size set"),
545                                                          P_("Whether the icon-size property has been set"),
546                                                          FALSE,
547                                                          GTK_PARAM_READWRITE));  
548
549   /* child properties */
550   gtk_container_class_install_child_property (container_class,
551                                               CHILD_PROP_EXPAND,
552                                               g_param_spec_boolean ("expand", 
553                                                                     P_("Expand"), 
554                                                                     P_("Whether the item should receive extra space when the toolbar grows"),
555                                                                     FALSE,
556                                                                     GTK_PARAM_READWRITE));
557   
558   gtk_container_class_install_child_property (container_class,
559                                               CHILD_PROP_HOMOGENEOUS,
560                                               g_param_spec_boolean ("homogeneous", 
561                                                                     P_("Homogeneous"), 
562                                                                     P_("Whether the item should be the same size as other homogeneous items"),
563                                                                     FALSE,
564                                                                     GTK_PARAM_READWRITE));
565   
566   /* style properties */
567   gtk_widget_class_install_style_property (widget_class,
568                                            g_param_spec_int ("space-size",
569                                                              P_("Spacer size"),
570                                                              P_("Size of spacers"),
571                                                              0,
572                                                              G_MAXINT,
573                                                              DEFAULT_SPACE_SIZE,
574                                                              GTK_PARAM_READABLE));
575   
576   gtk_widget_class_install_style_property (widget_class,
577                                            g_param_spec_int ("internal-padding",
578                                                              P_("Internal padding"),
579                                                              P_("Amount of border space between the toolbar shadow and the buttons"),
580                                                              0,
581                                                              G_MAXINT,
582                                                              DEFAULT_IPADDING,
583                                                              GTK_PARAM_READABLE));
584
585   gtk_widget_class_install_style_property (widget_class,
586                                            g_param_spec_int ("max-child-expand",
587                                                              P_("Maximum child expand"),
588                                                              P_("Maximum amount of space an expandable item will be given"),
589                                                              0,
590                                                              G_MAXINT,
591                                                              G_MAXINT,
592                                                              GTK_PARAM_READABLE));
593
594   gtk_widget_class_install_style_property (widget_class,
595                                            g_param_spec_enum ("space-style",
596                                                               P_("Space style"),
597                                                               P_("Whether spacers are vertical lines or just blank"),
598                                                               GTK_TYPE_TOOLBAR_SPACE_STYLE,
599                                                               DEFAULT_SPACE_STYLE,
600                                                               GTK_PARAM_READABLE));
601   
602   gtk_widget_class_install_style_property (widget_class,
603                                            g_param_spec_enum ("button-relief",
604                                                               P_("Button relief"),
605                                                               P_("Type of bevel around toolbar buttons"),
606                                                               GTK_TYPE_RELIEF_STYLE,
607                                                               GTK_RELIEF_NONE,
608                                                               GTK_PARAM_READABLE));
609   gtk_widget_class_install_style_property (widget_class,
610                                            g_param_spec_enum ("shadow-type",
611                                                               P_("Shadow type"),
612                                                               P_("Style of bevel around the toolbar"),
613                                                               GTK_TYPE_SHADOW_TYPE,
614                                                               GTK_SHADOW_OUT,
615                                                               GTK_PARAM_READABLE));
616
617   binding_set = gtk_binding_set_by_class (klass);
618   
619   add_arrow_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT);
620   add_arrow_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT);
621   add_arrow_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP);
622   add_arrow_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN);
623   
624   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
625                                 "focus-home-or-end", 1,
626                                 G_TYPE_BOOLEAN, TRUE);
627   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
628                                 "focus-home-or-end", 1,
629                                 G_TYPE_BOOLEAN, TRUE);
630   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
631                                 "focus-home-or-end", 1,
632                                 G_TYPE_BOOLEAN, FALSE);
633   gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
634                                 "focus-home-or-end", 1,
635                                 G_TYPE_BOOLEAN, FALSE);
636   
637   add_ctrl_tab_bindings (binding_set, 0, GTK_DIR_TAB_FORWARD);
638   add_ctrl_tab_bindings (binding_set, GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
639
640   g_type_class_add_private (gobject_class, sizeof (GtkToolbarPrivate));
641 }
642
643 static void
644 toolbar_tool_shell_iface_init (GtkToolShellIface *iface)
645 {
646   iface->get_icon_size    = toolbar_get_icon_size;
647   iface->get_orientation  = toolbar_get_orientation;
648   iface->get_style        = toolbar_get_style;
649   iface->get_relief_style = toolbar_get_relief_style;
650   iface->rebuild_menu     = toolbar_rebuild_menu;
651 }
652
653 static void
654 gtk_toolbar_init (GtkToolbar *toolbar)
655 {
656   GtkToolbarPrivate *priv;
657   GtkStyleContext *context;
658
659   toolbar->priv = G_TYPE_INSTANCE_GET_PRIVATE (toolbar,
660                                                GTK_TYPE_TOOLBAR,
661                                                GtkToolbarPrivate);
662   priv = toolbar->priv;
663
664   gtk_widget_set_can_focus (GTK_WIDGET (toolbar), FALSE);
665   gtk_widget_set_has_window (GTK_WIDGET (toolbar), FALSE);
666
667   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
668   priv->style = DEFAULT_TOOLBAR_STYLE;
669   priv->icon_size = DEFAULT_ICON_SIZE;
670   priv->animation = DEFAULT_ANIMATION_STATE;
671
672   priv->arrow_button = gtk_toggle_button_new ();
673   g_signal_connect (priv->arrow_button, "button-press-event",
674                     G_CALLBACK (gtk_toolbar_arrow_button_press), toolbar);
675   g_signal_connect (priv->arrow_button, "clicked",
676                     G_CALLBACK (gtk_toolbar_arrow_button_clicked), toolbar);
677   gtk_button_set_relief (GTK_BUTTON (priv->arrow_button),
678                          get_button_relief (toolbar));
679
680   gtk_button_set_focus_on_click (GTK_BUTTON (priv->arrow_button), FALSE);
681
682   priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
683   gtk_widget_set_name (priv->arrow, "gtk-toolbar-arrow");
684   gtk_widget_show (priv->arrow);
685   gtk_container_add (GTK_CONTAINER (priv->arrow_button), priv->arrow);
686   
687   gtk_widget_set_parent (priv->arrow_button, GTK_WIDGET (toolbar));
688   
689   /* which child position a drop will occur at */
690   priv->menu = NULL;
691   priv->show_arrow = TRUE;
692   priv->settings = NULL;
693   
694   priv->max_homogeneous_pixels = -1;
695   
696   priv->timer = g_timer_new ();
697
698   context = gtk_widget_get_style_context (GTK_WIDGET (toolbar));
699   gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOOLBAR);
700 }
701
702 static void
703 gtk_toolbar_set_property (GObject      *object,
704                           guint         prop_id,
705                           const GValue *value,
706                           GParamSpec   *pspec)
707 {
708   GtkToolbar *toolbar = GTK_TOOLBAR (object);
709   GtkToolbarPrivate *priv = toolbar->priv;
710
711   switch (prop_id)
712     {
713     case PROP_ORIENTATION:
714       g_signal_emit (toolbar, toolbar_signals[ORIENTATION_CHANGED], 0,
715                      g_value_get_enum (value));
716       break;
717     case PROP_TOOLBAR_STYLE:
718       gtk_toolbar_set_style (toolbar, g_value_get_enum (value));
719       break;
720     case PROP_SHOW_ARROW:
721       gtk_toolbar_set_show_arrow (toolbar, g_value_get_boolean (value));
722       break;
723     case PROP_ICON_SIZE:
724       gtk_toolbar_set_icon_size (toolbar, g_value_get_int (value));
725       break;
726     case PROP_ICON_SIZE_SET:
727       if (g_value_get_boolean (value))
728         priv->icon_size_set = TRUE;
729       else
730         gtk_toolbar_unset_icon_size (toolbar);
731       break;
732     default:
733       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
734       break;
735     }
736 }
737
738 static void
739 gtk_toolbar_get_property (GObject    *object,
740                           guint       prop_id,
741                           GValue     *value,
742                           GParamSpec *pspec)
743 {
744   GtkToolbar *toolbar = GTK_TOOLBAR (object);
745   GtkToolbarPrivate *priv = toolbar->priv;
746
747   switch (prop_id)
748     {
749     case PROP_ORIENTATION:
750       g_value_set_enum (value, priv->orientation);
751       break;
752     case PROP_TOOLBAR_STYLE:
753       g_value_set_enum (value, priv->style);
754       break;
755     case PROP_SHOW_ARROW:
756       g_value_set_boolean (value, priv->show_arrow);
757       break;
758     case PROP_ICON_SIZE:
759       g_value_set_int (value, gtk_toolbar_get_icon_size (toolbar));
760       break;
761     case PROP_ICON_SIZE_SET:
762       g_value_set_boolean (value, priv->icon_size_set);
763       break;
764     default:
765       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
766       break;
767     }
768 }
769
770 static void
771 gtk_toolbar_map (GtkWidget *widget)
772 {
773   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
774   GtkToolbarPrivate *priv = toolbar->priv;
775
776   GTK_WIDGET_CLASS (gtk_toolbar_parent_class)->map (widget);
777
778   if (priv->event_window)
779     gdk_window_show_unraised (priv->event_window);
780 }
781
782 static void
783 gtk_toolbar_unmap (GtkWidget *widget)
784 {
785   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
786   GtkToolbarPrivate *priv = toolbar->priv;
787
788   if (priv->event_window)
789     gdk_window_hide (priv->event_window);
790   
791   GTK_WIDGET_CLASS (gtk_toolbar_parent_class)->unmap (widget);
792 }
793
794 static void
795 gtk_toolbar_realize (GtkWidget *widget)
796 {
797   GtkAllocation allocation;
798   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
799   GtkToolbarPrivate *priv = toolbar->priv;
800   GdkWindow *window;
801   GdkWindowAttr attributes;
802   gint attributes_mask;
803   guint border_width;
804
805   gtk_widget_set_realized (widget, TRUE);
806
807   gtk_widget_get_allocation (widget, &allocation);
808   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
809
810   attributes.wclass = GDK_INPUT_ONLY;
811   attributes.window_type = GDK_WINDOW_CHILD;
812   attributes.x = allocation.x + border_width;
813   attributes.y = allocation.y + border_width;
814   attributes.width = allocation.width - border_width * 2;
815   attributes.height = allocation.height - border_width * 2;
816   attributes.event_mask = gtk_widget_get_events (widget);
817   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
818                             GDK_BUTTON_RELEASE_MASK |
819                             GDK_ENTER_NOTIFY_MASK |
820                             GDK_LEAVE_NOTIFY_MASK);
821
822   attributes_mask = GDK_WA_X | GDK_WA_Y;
823
824   window = gtk_widget_get_parent_window (widget);
825   gtk_widget_set_window (widget, window);
826   g_object_ref (window);
827
828   priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
829                                        &attributes, attributes_mask);
830   gdk_window_set_user_data (priv->event_window, toolbar);
831 }
832
833 static void
834 gtk_toolbar_unrealize (GtkWidget *widget)
835 {
836   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
837   GtkToolbarPrivate *priv = toolbar->priv;
838
839   if (priv->event_window)
840     {
841       gdk_window_set_user_data (priv->event_window, NULL);
842       gdk_window_destroy (priv->event_window);
843       priv->event_window = NULL;
844     }
845
846   GTK_WIDGET_CLASS (gtk_toolbar_parent_class)->unrealize (widget);
847 }
848
849 static gint
850 gtk_toolbar_draw (GtkWidget *widget,
851                   cairo_t   *cr)
852 {
853   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
854   GtkToolbarPrivate *priv = toolbar->priv;
855   GtkStyleContext *context;
856   GtkStateFlags state;
857   GList *list;
858   guint border_width;
859
860   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
861   context = gtk_widget_get_style_context (widget);
862   state = gtk_widget_get_state_flags (widget);
863
864   gtk_style_context_save (context);
865   gtk_style_context_set_state (context, state);
866
867   gtk_render_background (context, cr, border_width, border_width,
868                          gtk_widget_get_allocated_width (widget) - 2 * border_width,
869                          gtk_widget_get_allocated_height (widget) - 2 * border_width);
870   gtk_render_frame (context, cr, border_width, border_width,
871                     gtk_widget_get_allocated_width (widget) - 2 * border_width,
872                     gtk_widget_get_allocated_height (widget) - 2 * border_width);
873
874   for (list = priv->content; list != NULL; list = list->next)
875     {
876       ToolbarContent *content = list->data;
877       
878       toolbar_content_draw (content, GTK_CONTAINER (widget), cr);
879     }
880   
881   gtk_container_propagate_draw (GTK_CONTAINER (widget),
882                                 priv->arrow_button,
883                                 cr);
884
885   gtk_style_context_restore (context);
886
887   return FALSE;
888 }
889
890 static void
891 gtk_toolbar_size_request (GtkWidget      *widget,
892                           GtkRequisition *requisition)
893 {
894   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
895   GtkToolbarPrivate *priv = toolbar->priv;
896   GList *list;
897   gint max_child_height;
898   gint max_child_width;
899   gint max_homogeneous_child_width;
900   gint max_homogeneous_child_height;
901   gint homogeneous_size;
902   gint long_req;
903   gint pack_front_size;
904   gint ipadding;
905   guint border_width;
906   GtkRequisition arrow_requisition;
907   
908   max_homogeneous_child_width = 0;
909   max_homogeneous_child_height = 0;
910   max_child_width = 0;
911   max_child_height = 0;
912   for (list = priv->content; list != NULL; list = list->next)
913     {
914       GtkRequisition requisition;
915       ToolbarContent *content = list->data;
916       
917       if (!toolbar_content_visible (content, toolbar))
918         continue;
919       
920       toolbar_content_size_request (content, toolbar, &requisition);
921
922       max_child_width = MAX (max_child_width, requisition.width);
923       max_child_height = MAX (max_child_height, requisition.height);
924       
925       if (toolbar_content_is_homogeneous (content, toolbar))
926         {
927           max_homogeneous_child_width = MAX (max_homogeneous_child_width, requisition.width);
928           max_homogeneous_child_height = MAX (max_homogeneous_child_height, requisition.height);
929         }
930     }
931   
932   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
933     homogeneous_size = max_homogeneous_child_width;
934   else
935     homogeneous_size = max_homogeneous_child_height;
936   
937   pack_front_size = 0;
938   for (list = priv->content; list != NULL; list = list->next)
939     {
940       ToolbarContent *content = list->data;
941       guint size;
942       
943       if (!toolbar_content_visible (content, toolbar))
944         continue;
945
946       if (toolbar_content_is_homogeneous (content, toolbar))
947         {
948           size = homogeneous_size;
949         }
950       else
951         {
952           GtkRequisition requisition;
953           
954           toolbar_content_size_request (content, toolbar, &requisition);
955           
956           if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
957             size = requisition.width;
958           else
959             size = requisition.height;
960         }
961
962       pack_front_size += size;
963     }
964   
965   if (priv->show_arrow)
966     {
967       gtk_widget_get_preferred_size (priv->arrow_button,
968                                      &arrow_requisition, NULL);
969
970       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
971         long_req = arrow_requisition.width;
972       else
973         long_req = arrow_requisition.height;
974       
975       /* There is no point requesting space for the arrow if that would take
976        * up more space than all the items combined
977        */
978       long_req = MIN (long_req, pack_front_size);
979     }
980   else
981     {
982       arrow_requisition.height = 0;
983       arrow_requisition.width = 0;
984       
985       long_req = pack_front_size;
986     }
987   
988   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
989     {
990       requisition->width = long_req;
991       requisition->height = MAX (max_child_height, arrow_requisition.height);
992     }
993   else
994     {
995       requisition->height = long_req;
996       requisition->width = MAX (max_child_width, arrow_requisition.width);
997     }
998   
999   /* Extra spacing */
1000   ipadding = get_internal_padding (toolbar);
1001
1002   border_width = gtk_container_get_border_width (GTK_CONTAINER (toolbar));
1003   requisition->width += 2 * (ipadding + border_width);
1004   requisition->height += 2 * (ipadding + border_width);
1005   
1006   if (get_shadow_type (toolbar) != GTK_SHADOW_NONE)
1007     {
1008       GtkStyleContext *context;
1009       GtkStateFlags state;
1010       GtkBorder padding;
1011
1012       context = gtk_widget_get_style_context (widget);
1013       state = gtk_widget_get_state_flags (widget);
1014       gtk_style_context_get_padding (context, state, &padding);
1015
1016       requisition->width += padding.left + padding.right;
1017       requisition->height += padding.top + padding.bottom;
1018     }
1019   
1020   priv->button_maxw = max_homogeneous_child_width;
1021   priv->button_maxh = max_homogeneous_child_height;
1022 }
1023
1024 static void
1025 gtk_toolbar_get_preferred_width (GtkWidget *widget,
1026                                  gint      *minimum,
1027                                  gint      *natural)
1028 {
1029   GtkRequisition requisition;
1030
1031   gtk_toolbar_size_request (widget, &requisition);
1032
1033   *minimum = *natural = requisition.width;
1034 }
1035
1036 static void
1037 gtk_toolbar_get_preferred_height (GtkWidget *widget,
1038                                   gint      *minimum,
1039                                   gint      *natural)
1040 {
1041   GtkRequisition requisition;
1042
1043   gtk_toolbar_size_request (widget, &requisition);
1044
1045   *minimum = *natural = requisition.height;
1046 }
1047
1048 static gint
1049 position (GtkToolbar *toolbar,
1050           gint        from,
1051           gint        to,
1052           gdouble     elapsed)
1053 {
1054   GtkToolbarPrivate *priv = toolbar->priv;
1055   gint n_pixels;
1056
1057   if (!priv->animation)
1058     return to;
1059
1060   if (elapsed <= ACCEL_THRESHOLD)
1061     {
1062       n_pixels = SLIDE_SPEED * elapsed;
1063     }
1064   else
1065     {
1066       /* The formula is a second degree polynomial in
1067        * @elapsed that has the line SLIDE_SPEED * @elapsed
1068        * as tangent for @elapsed == ACCEL_THRESHOLD.
1069        * This makes @n_pixels a smooth function of elapsed time.
1070        */
1071       n_pixels = (SLIDE_SPEED / ACCEL_THRESHOLD) * elapsed * elapsed -
1072         SLIDE_SPEED * elapsed + SLIDE_SPEED * ACCEL_THRESHOLD;
1073     }
1074
1075   if (to > from)
1076     return MIN (from + n_pixels, to);
1077   else
1078     return MAX (from - n_pixels, to);
1079 }
1080
1081 static void
1082 compute_intermediate_allocation (GtkToolbar          *toolbar,
1083                                  const GtkAllocation *start,
1084                                  const GtkAllocation *goal,
1085                                  GtkAllocation       *intermediate)
1086 {
1087   GtkToolbarPrivate *priv = toolbar->priv;
1088   gdouble elapsed = g_timer_elapsed (priv->timer, NULL);
1089
1090   intermediate->x      = position (toolbar, start->x, goal->x, elapsed);
1091   intermediate->y      = position (toolbar, start->y, goal->y, elapsed);
1092   intermediate->width  = position (toolbar, start->x + start->width,
1093                                    goal->x + goal->width,
1094                                    elapsed) - intermediate->x;
1095   intermediate->height = position (toolbar, start->y + start->height,
1096                                    goal->y + goal->height,
1097                                    elapsed) - intermediate->y;
1098 }
1099
1100 static void
1101 fixup_allocation_for_rtl (gint           total_size,
1102                           GtkAllocation *allocation)
1103 {
1104   allocation->x += (total_size - (2 * allocation->x + allocation->width));
1105 }
1106
1107 static void
1108 fixup_allocation_for_vertical (GtkAllocation *allocation)
1109 {
1110   gint tmp;
1111   
1112   tmp = allocation->x;
1113   allocation->x = allocation->y;
1114   allocation->y = tmp;
1115   
1116   tmp = allocation->width;
1117   allocation->width = allocation->height;
1118   allocation->height = tmp;
1119 }
1120
1121 static gint
1122 get_item_size (GtkToolbar     *toolbar,
1123                ToolbarContent *content)
1124 {
1125   GtkToolbarPrivate *priv = toolbar->priv;
1126   GtkRequisition requisition;
1127   
1128   toolbar_content_size_request (content, toolbar, &requisition);
1129
1130   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1131     {
1132       if (toolbar_content_is_homogeneous (content, toolbar))
1133         return priv->button_maxw;
1134       else
1135         return requisition.width;
1136     }
1137   else
1138     {
1139       if (toolbar_content_is_homogeneous (content, toolbar))
1140         return priv->button_maxh;
1141       else
1142         return requisition.height;
1143     }
1144 }
1145
1146 static gboolean
1147 slide_idle_handler (gpointer data)
1148 {
1149   GtkToolbar *toolbar = GTK_TOOLBAR (data);
1150   GtkToolbarPrivate *priv = toolbar->priv;
1151   GList *list;
1152
1153   if (priv->need_sync)
1154     {
1155       gdk_flush ();
1156       priv->need_sync = FALSE;
1157     }
1158   
1159   for (list = priv->content; list != NULL; list = list->next)
1160     {
1161       ToolbarContent *content = list->data;
1162       ItemState state;
1163       GtkAllocation goal_allocation;
1164       GtkAllocation allocation;
1165       gboolean cont;
1166
1167       state = toolbar_content_get_state (content);
1168       toolbar_content_get_goal_allocation (content, &goal_allocation);
1169       toolbar_content_get_allocation (content, &allocation);
1170       
1171       cont = FALSE;
1172       
1173       if (state == NOT_ALLOCATED)
1174         {
1175           /* an unallocated item means that size allocate has to
1176            * called at least once more
1177            */
1178           cont = TRUE;
1179         }
1180
1181       /* An invisible item with a goal allocation of
1182        * 0 is already at its goal.
1183        */
1184       if ((state == NORMAL || state == OVERFLOWN) &&
1185           ((goal_allocation.width != 0 &&
1186             goal_allocation.height != 0) ||
1187            toolbar_content_child_visible (content)))
1188         {
1189           if ((goal_allocation.x != allocation.x ||
1190                goal_allocation.y != allocation.y ||
1191                goal_allocation.width != allocation.width ||
1192                goal_allocation.height != allocation.height))
1193             {
1194               /* An item is not in its right position yet. Note
1195                * that OVERFLOWN items do get an allocation in
1196                * gtk_toolbar_size_allocate(). This way you can see
1197                * them slide back in when you drag an item off the
1198                * toolbar.
1199                */
1200               cont = TRUE;
1201             }
1202         }
1203
1204       if (toolbar_content_is_placeholder (content) &&
1205           toolbar_content_disappearing (content) &&
1206           toolbar_content_child_visible (content))
1207         {
1208           /* A disappearing placeholder is still visible.
1209            */
1210              
1211           cont = TRUE;
1212         }
1213       
1214       if (cont)
1215         {
1216           gtk_widget_queue_resize_no_redraw (GTK_WIDGET (toolbar));
1217           
1218           return TRUE;
1219         }
1220     }
1221   
1222   gtk_widget_queue_resize_no_redraw (GTK_WIDGET (toolbar));
1223
1224   priv->is_sliding = FALSE;
1225   priv->idle_id = 0;
1226
1227   return FALSE;
1228 }
1229
1230 static gboolean
1231 rect_within (GtkAllocation *a1,
1232              GtkAllocation *a2)
1233 {
1234   return (a1->x >= a2->x                         &&
1235           a1->x + a1->width <= a2->x + a2->width &&
1236           a1->y >= a2->y                         &&
1237           a1->y + a1->height <= a2->y + a2->height);
1238 }
1239
1240 static void
1241 gtk_toolbar_begin_sliding (GtkToolbar *toolbar)
1242 {
1243   GtkAllocation allocation;
1244   GtkWidget *widget = GTK_WIDGET (toolbar);
1245   GtkToolbarPrivate *priv = toolbar->priv;
1246   GtkStyleContext *context;
1247   GtkStateFlags state;
1248   GtkBorder padding;
1249   GList *list;
1250   gint cur_x;
1251   gint cur_y;
1252   gint border_width;
1253   gboolean rtl;
1254   gboolean vertical;
1255   
1256   /* Start the sliding. This function copies the allocation of every
1257    * item into content->start_allocation. For items that haven't
1258    * been allocated yet, we calculate their position and save that
1259    * in start_allocatino along with zero width and zero height.
1260    *
1261    * FIXME: It would be nice if we could share this code with
1262    * the equivalent in gtk_widget_size_allocate().
1263    */
1264   priv->is_sliding = TRUE;
1265   
1266   if (!priv->idle_id)
1267     priv->idle_id = gdk_threads_add_idle (slide_idle_handler, toolbar);
1268
1269   gtk_widget_get_allocation (widget, &allocation);
1270   context = gtk_widget_get_style_context (widget);
1271   state = gtk_widget_get_state_flags (widget);
1272   gtk_style_context_get_padding (context, state, &padding);
1273
1274   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1275   vertical = (priv->orientation == GTK_ORIENTATION_VERTICAL);
1276   border_width = get_internal_padding (toolbar) + gtk_container_get_border_width (GTK_CONTAINER (toolbar));
1277   
1278   if (rtl)
1279     {
1280       cur_x = allocation.width - border_width - padding.right;
1281       cur_y = allocation.height - border_width - padding.top;
1282     }
1283   else
1284     {
1285       cur_x = border_width + padding.left;
1286       cur_y = border_width + padding.top;
1287     }
1288
1289   cur_x += allocation.x;
1290   cur_y += allocation.y;
1291
1292   for (list = priv->content; list != NULL; list = list->next)
1293     {
1294       ToolbarContent *content = list->data;
1295       GtkAllocation new_start_allocation;
1296       GtkAllocation item_allocation;
1297       ItemState state;
1298       
1299       state = toolbar_content_get_state (content);
1300       toolbar_content_get_allocation (content, &item_allocation);
1301       
1302       if ((state == NORMAL &&
1303            rect_within (&item_allocation, &allocation)) ||
1304           state == OVERFLOWN)
1305         {
1306           new_start_allocation = item_allocation;
1307         }
1308       else
1309         {
1310           new_start_allocation.x = cur_x;
1311           new_start_allocation.y = cur_y;
1312           
1313           if (vertical)
1314             {
1315               new_start_allocation.width = allocation.width -
1316                                            2 * border_width -
1317                                            padding.left - padding.right;
1318               new_start_allocation.height = 0;
1319             }
1320           else
1321             {
1322               new_start_allocation.width = 0;
1323               new_start_allocation.height = allocation.height -
1324                                             2 * border_width -
1325                                             padding.top - padding.bottom;
1326             }
1327         }
1328       
1329       if (vertical)
1330         cur_y = new_start_allocation.y + new_start_allocation.height;
1331       else if (rtl)
1332         cur_x = new_start_allocation.x;
1333       else
1334         cur_x = new_start_allocation.x + new_start_allocation.width;
1335       
1336       toolbar_content_set_start_allocation (content, &new_start_allocation);
1337     }
1338
1339   /* This resize will run before the first idle handler. This
1340    * will make sure that items get the right goal allocation
1341    * so that the idle handler will not immediately return
1342    * FALSE
1343    */
1344   gtk_widget_queue_resize_no_redraw (GTK_WIDGET (toolbar));
1345   g_timer_reset (priv->timer);
1346 }
1347
1348 static void
1349 gtk_toolbar_stop_sliding (GtkToolbar *toolbar)
1350 {
1351   GtkToolbarPrivate *priv = toolbar->priv;
1352
1353   if (priv->is_sliding)
1354     {
1355       GList *list;
1356       
1357       priv->is_sliding = FALSE;
1358       
1359       if (priv->idle_id)
1360         {
1361           g_source_remove (priv->idle_id);
1362           priv->idle_id = 0;
1363         }
1364       
1365       list = priv->content;
1366       while (list)
1367         {
1368           ToolbarContent *content = list->data;
1369           list = list->next;
1370
1371           if (toolbar_content_is_placeholder (content))
1372             {
1373               toolbar_content_remove (content, toolbar);
1374               toolbar_content_free (content);
1375             }
1376         }
1377       
1378       gtk_widget_queue_resize_no_redraw (GTK_WIDGET (toolbar));
1379     }
1380 }
1381
1382 static void
1383 remove_item (GtkWidget *menu_item,
1384              gpointer   data)
1385 {
1386   gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (menu_item)),
1387                         menu_item);
1388 }
1389
1390 static void
1391 menu_deactivated (GtkWidget  *menu,
1392                   GtkToolbar *toolbar)
1393 {
1394   GtkToolbarPrivate *priv = toolbar->priv;
1395
1396   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->arrow_button), FALSE);
1397 }
1398
1399 static void
1400 menu_detached (GtkWidget  *widget,
1401                GtkMenu    *menu)
1402 {
1403   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
1404   GtkToolbarPrivate *priv = toolbar->priv;
1405
1406   priv->menu = NULL;
1407 }
1408
1409 static void
1410 rebuild_menu (GtkToolbar *toolbar)
1411 {
1412   GtkToolbarPrivate *priv = toolbar->priv;
1413   GList *list, *children;
1414
1415   if (!priv->menu)
1416     {
1417       priv->menu = GTK_MENU (gtk_menu_new());
1418       gtk_menu_attach_to_widget (priv->menu,
1419                                  GTK_WIDGET (toolbar),
1420                                  menu_detached);
1421
1422       g_signal_connect (priv->menu, "deactivate",
1423                         G_CALLBACK (menu_deactivated), toolbar);
1424     }
1425
1426   gtk_container_foreach (GTK_CONTAINER (priv->menu), remove_item, NULL);
1427   
1428   for (list = priv->content; list != NULL; list = list->next)
1429     {
1430       ToolbarContent *content = list->data;
1431       
1432       if (toolbar_content_get_state (content) == OVERFLOWN &&
1433           !toolbar_content_is_placeholder (content))
1434         {
1435           GtkWidget *menu_item = toolbar_content_retrieve_menu_item (content);
1436           
1437           if (menu_item)
1438             {
1439               g_assert (GTK_IS_MENU_ITEM (menu_item));
1440               gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), menu_item);
1441             }
1442         }
1443     }
1444
1445   /* Remove leading and trailing separator items */
1446   children = gtk_container_get_children (GTK_CONTAINER (priv->menu));
1447   
1448   list = children;
1449   while (list && GTK_IS_SEPARATOR_MENU_ITEM (list->data))
1450     {
1451       GtkWidget *child = list->data;
1452       
1453       gtk_container_remove (GTK_CONTAINER (priv->menu), child);
1454       list = list->next;
1455     }
1456   g_list_free (children);
1457
1458   /* Regenerate the list of children so we don't try to remove items twice */
1459   children = gtk_container_get_children (GTK_CONTAINER (priv->menu));
1460
1461   list = g_list_last (children);
1462   while (list && GTK_IS_SEPARATOR_MENU_ITEM (list->data))
1463     {
1464       GtkWidget *child = list->data;
1465
1466       gtk_container_remove (GTK_CONTAINER (priv->menu), child);
1467       list = list->prev;
1468     }
1469   g_list_free (children);
1470
1471   priv->need_rebuild = FALSE;
1472 }
1473
1474 static void
1475 gtk_toolbar_size_allocate (GtkWidget     *widget,
1476                            GtkAllocation *allocation)
1477 {
1478   GtkAllocation widget_allocation;
1479   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
1480   GtkToolbarPrivate *priv = toolbar->priv;
1481   GtkAllocation *allocations;
1482   ItemState *new_states;
1483   GtkAllocation arrow_allocation;
1484   GtkStyleContext *context;
1485   GtkStateFlags state;
1486   GtkBorder padding;
1487   gint arrow_size;
1488   gint size, pos, short_size;
1489   GList *list;
1490   gint i;
1491   gboolean need_arrow;
1492   gint n_expand_items;
1493   gint border_width;
1494   gint available_size;
1495   gint n_items;
1496   gint needed_size;
1497   GtkRequisition arrow_requisition;
1498   gboolean overflowing;
1499   gboolean size_changed;
1500   GtkAllocation item_area;
1501   GtkShadowType shadow_type;
1502
1503   context = gtk_widget_get_style_context (widget);
1504   state = gtk_widget_get_state_flags (widget);
1505   gtk_style_context_get_padding (context, state, &padding);
1506
1507   gtk_widget_get_allocation (widget, &widget_allocation);
1508   size_changed = FALSE;
1509   if (widget_allocation.x != allocation->x ||
1510       widget_allocation.y != allocation->y ||
1511       widget_allocation.width != allocation->width ||
1512       widget_allocation.height != allocation->height)
1513     {
1514       size_changed = TRUE;
1515     }
1516
1517   if (size_changed)
1518     gtk_toolbar_stop_sliding (toolbar);
1519
1520   gtk_widget_set_allocation (widget, allocation);
1521
1522   border_width = gtk_container_get_border_width (GTK_CONTAINER (toolbar));
1523
1524   if (gtk_widget_get_realized (widget))
1525     gdk_window_move_resize (priv->event_window,
1526                             allocation->x + border_width,
1527                             allocation->y + border_width,
1528                             allocation->width - border_width * 2,
1529                             allocation->height - border_width * 2);
1530
1531   border_width += get_internal_padding (toolbar);
1532
1533   gtk_widget_get_preferred_size (priv->arrow_button,
1534                                  &arrow_requisition, NULL);
1535
1536   shadow_type = get_shadow_type (toolbar);
1537
1538   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1539     {
1540       available_size = size = allocation->width - 2 * border_width;
1541       short_size = allocation->height - 2 * border_width;
1542       arrow_size = arrow_requisition.width;
1543
1544       if (shadow_type != GTK_SHADOW_NONE)
1545         {
1546           available_size -= padding.left + padding.right;
1547           short_size -= padding.top + padding.bottom;
1548         }
1549     }
1550   else
1551     {
1552       available_size = size = allocation->height - 2 * border_width;
1553       short_size = allocation->width - 2 * border_width;
1554       arrow_size = arrow_requisition.height;
1555
1556       if (shadow_type != GTK_SHADOW_NONE)
1557         {
1558           available_size -= padding.top + padding.bottom;
1559           short_size -= padding.left + padding.right;
1560         }
1561     }
1562
1563   n_items = g_list_length (priv->content);
1564   allocations = g_new0 (GtkAllocation, n_items);
1565   new_states = g_new0 (ItemState, n_items);
1566
1567   needed_size = 0;
1568   need_arrow = FALSE;
1569   for (list = priv->content; list != NULL; list = list->next)
1570     {
1571       ToolbarContent *content = list->data;
1572
1573       if (toolbar_content_visible (content, toolbar))
1574         {
1575           needed_size += get_item_size (toolbar, content);
1576
1577           /* Do we need an arrow?
1578            *
1579            * Assume we don't, and see if any non-separator item
1580            * with a proxy menu item is then going to overflow.
1581            */
1582           if (needed_size > available_size &&
1583               !need_arrow &&
1584               priv->show_arrow &&
1585               toolbar_content_has_proxy_menu_item (content) &&
1586               !toolbar_content_is_separator (content))
1587             {
1588               need_arrow = TRUE;
1589             }
1590         }
1591     }
1592
1593   if (need_arrow)
1594     size = available_size - arrow_size;
1595   else
1596     size = available_size;
1597
1598   /* calculate widths and states of items */
1599   overflowing = FALSE;
1600   for (list = priv->content, i = 0; list != NULL; list = list->next, ++i)
1601     {
1602       ToolbarContent *content = list->data;
1603       gint item_size;
1604
1605       if (!toolbar_content_visible (content, toolbar))
1606         {
1607           new_states[i] = HIDDEN;
1608           continue;
1609         }
1610
1611       item_size = get_item_size (toolbar, content);
1612       if (item_size <= size && !overflowing)
1613         {
1614           size -= item_size;
1615           allocations[i].width = item_size;
1616           new_states[i] = NORMAL;
1617         }
1618       else
1619         {
1620           overflowing = TRUE;
1621           new_states[i] = OVERFLOWN;
1622           allocations[i].width = item_size;
1623         }
1624     }
1625
1626   /* calculate width of arrow */
1627   if (need_arrow)
1628     {
1629       arrow_allocation.width = arrow_size;
1630       arrow_allocation.height = MAX (short_size, 1);
1631     }
1632
1633   /* expand expandable items */
1634
1635   /* We don't expand when there is an overflow menu,
1636    * because that leads to weird jumps when items get
1637    * moved to the overflow menu and the expanding
1638    * items suddenly get a lot of extra space
1639    */
1640   if (!overflowing)
1641     {
1642       gint max_child_expand;
1643       n_expand_items = 0;
1644
1645       for (i = 0, list = priv->content; list != NULL; list = list->next, ++i)
1646         {
1647           ToolbarContent *content = list->data;
1648
1649           if (toolbar_content_get_expand (content) && new_states[i] == NORMAL)
1650             n_expand_items++;
1651         }
1652
1653       max_child_expand = get_max_child_expand (toolbar);
1654       for (list = priv->content, i = 0; list != NULL; list = list->next, ++i)
1655         {
1656           ToolbarContent *content = list->data;
1657
1658           if (toolbar_content_get_expand (content) && new_states[i] == NORMAL)
1659             {
1660               gint extra = size / n_expand_items;
1661               if (size % n_expand_items != 0)
1662                 extra++;
1663
1664               if (extra > max_child_expand)
1665                 extra = max_child_expand;
1666
1667               allocations[i].width += extra;
1668               size -= extra;
1669               n_expand_items--;
1670             }
1671         }
1672
1673       g_assert (n_expand_items == 0);
1674     }
1675
1676   /* position items */
1677   pos = border_width;
1678   for (list = priv->content, i = 0; list != NULL; list = list->next, ++i)
1679     {
1680       /* Both NORMAL and OVERFLOWN items get a position.
1681        * This ensures that sliding will work for OVERFLOWN items too.
1682        */
1683       if (new_states[i] == NORMAL || new_states[i] == OVERFLOWN)
1684         {
1685           allocations[i].x = pos;
1686           allocations[i].y = border_width;
1687           allocations[i].height = short_size;
1688
1689           pos += allocations[i].width;
1690         }
1691     }
1692
1693   /* position arrow */
1694   if (need_arrow)
1695     {
1696       arrow_allocation.x = available_size - border_width - arrow_allocation.width;
1697       arrow_allocation.y = border_width;
1698     }
1699
1700   item_area.x = border_width;
1701   item_area.y = border_width;
1702   item_area.width = available_size - (need_arrow? arrow_size : 0);
1703   item_area.height = short_size;
1704
1705   /* fix up allocations in the vertical or RTL cases */
1706   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
1707     {
1708       for (i = 0; i < n_items; ++i)
1709         fixup_allocation_for_vertical (&(allocations[i]));
1710
1711       if (need_arrow)
1712         fixup_allocation_for_vertical (&arrow_allocation);
1713
1714       fixup_allocation_for_vertical (&item_area);
1715     }
1716   else if (gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_RTL)
1717     {
1718       for (i = 0; i < n_items; ++i)
1719         fixup_allocation_for_rtl (available_size, &(allocations[i]));
1720
1721       if (need_arrow)
1722         fixup_allocation_for_rtl (available_size, &arrow_allocation);
1723
1724       fixup_allocation_for_rtl (available_size, &item_area);
1725     }
1726
1727   /* translate the items by allocation->(x,y) */
1728   for (i = 0; i < n_items; ++i)
1729     {
1730       allocations[i].x += allocation->x;
1731       allocations[i].y += allocation->y;
1732
1733       if (shadow_type != GTK_SHADOW_NONE)
1734         {
1735           allocations[i].x += padding.left;
1736           allocations[i].y += padding.top;
1737         }
1738     }
1739
1740   if (need_arrow)
1741     {
1742       arrow_allocation.x += allocation->x;
1743       arrow_allocation.y += allocation->y;
1744
1745       if (shadow_type != GTK_SHADOW_NONE)
1746         {
1747           arrow_allocation.x += padding.left;
1748           arrow_allocation.y += padding.top;
1749         }
1750     }
1751
1752   item_area.x += allocation->x;
1753   item_area.y += allocation->y;
1754   if (shadow_type != GTK_SHADOW_NONE)
1755     {
1756       item_area.x += padding.left;
1757       item_area.y += padding.top;
1758     }
1759
1760   /* did anything change? */
1761   for (list = priv->content, i = 0; list != NULL; list = list->next, i++)
1762     {
1763       ToolbarContent *content = list->data;
1764
1765       if (toolbar_content_get_state (content) == NORMAL &&
1766           new_states[i] != NORMAL)
1767         {
1768           /* an item disappeared and we didn't change size, so begin sliding */
1769           if (!size_changed)
1770             gtk_toolbar_begin_sliding (toolbar);
1771         }
1772     }
1773
1774   /* finally allocate the items */
1775   if (priv->is_sliding)
1776     {
1777       for (list = priv->content, i = 0; list != NULL; list = list->next, i++)
1778         {
1779           ToolbarContent *content = list->data;
1780
1781           toolbar_content_set_goal_allocation (content, &(allocations[i]));
1782         }
1783     }
1784
1785   for (list = priv->content, i = 0; list != NULL; list = list->next, ++i)
1786     {
1787       ToolbarContent *content = list->data;
1788
1789       if (new_states[i] == OVERFLOWN || new_states[i] == NORMAL)
1790         {
1791           GtkAllocation alloc;
1792           GtkAllocation start_allocation = { 0, };
1793           GtkAllocation goal_allocation;
1794
1795           if (priv->is_sliding)
1796             {
1797               toolbar_content_get_start_allocation (content, &start_allocation);
1798               toolbar_content_get_goal_allocation (content, &goal_allocation);
1799
1800               compute_intermediate_allocation (toolbar,
1801                                                &start_allocation,
1802                                                &goal_allocation,
1803                                                &alloc);
1804
1805               priv->need_sync = TRUE;
1806             }
1807           else
1808             {
1809               alloc = allocations[i];
1810             }
1811
1812           if (alloc.width <= 0 || alloc.height <= 0)
1813             {
1814               toolbar_content_set_child_visible (content, toolbar, FALSE);
1815             }
1816           else
1817             {
1818               if (!rect_within (&alloc, &item_area))
1819                 {
1820                   toolbar_content_set_child_visible (content, toolbar, FALSE);
1821                   toolbar_content_size_allocate (content, &alloc);
1822                 }
1823               else
1824                 {
1825                   toolbar_content_set_child_visible (content, toolbar, TRUE);
1826                   toolbar_content_size_allocate (content, &alloc);
1827                 }
1828             }
1829         }
1830       else
1831         {
1832           toolbar_content_set_child_visible (content, toolbar, FALSE);
1833         }
1834
1835       toolbar_content_set_state (content, new_states[i]);
1836     }
1837
1838   if (priv->menu && priv->need_rebuild)
1839     rebuild_menu (toolbar);
1840
1841   if (need_arrow)
1842     {
1843       gtk_widget_size_allocate (GTK_WIDGET (priv->arrow_button),
1844                                 &arrow_allocation);
1845       gtk_widget_show (GTK_WIDGET (priv->arrow_button));
1846     }
1847   else
1848     {
1849       gtk_widget_hide (GTK_WIDGET (priv->arrow_button));
1850
1851       if (priv->menu && gtk_widget_get_visible (GTK_WIDGET (priv->menu)))
1852         gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->menu));
1853     }
1854
1855   g_free (allocations);
1856   g_free (new_states);
1857 }
1858
1859 static void
1860 gtk_toolbar_update_button_relief (GtkToolbar *toolbar)
1861 {
1862   GtkToolbarPrivate *priv = toolbar->priv;
1863   GtkReliefStyle relief;
1864
1865   relief = get_button_relief (toolbar);
1866
1867   if (relief != gtk_button_get_relief (GTK_BUTTON (priv->arrow_button)))
1868     {
1869       gtk_toolbar_reconfigured (toolbar);
1870   
1871       gtk_button_set_relief (GTK_BUTTON (priv->arrow_button), relief);
1872     }
1873 }
1874
1875 static void
1876 gtk_toolbar_style_updated (GtkWidget *widget)
1877 {
1878   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
1879   GtkToolbarPrivate *priv = toolbar->priv;
1880
1881   GTK_WIDGET_CLASS (gtk_toolbar_parent_class)->style_updated (widget);
1882
1883   priv->max_homogeneous_pixels = -1;
1884
1885   if (gtk_widget_get_realized (widget))
1886     gtk_style_context_set_background (gtk_widget_get_style_context (widget),
1887                                       gtk_widget_get_window (widget));
1888
1889   gtk_toolbar_update_button_relief (GTK_TOOLBAR (widget));
1890 }
1891
1892 static GList *
1893 gtk_toolbar_list_children_in_focus_order (GtkToolbar       *toolbar,
1894                                           GtkDirectionType  dir)
1895 {
1896   GtkToolbarPrivate *priv = toolbar->priv;
1897   GList *result = NULL;
1898   GList *list;
1899   gboolean rtl;
1900   
1901   /* generate list of children in reverse logical order */
1902   
1903   for (list = priv->content; list != NULL; list = list->next)
1904     {
1905       ToolbarContent *content = list->data;
1906       GtkWidget *widget;
1907       
1908       widget = toolbar_content_get_widget (content);
1909       
1910       if (widget)
1911         result = g_list_prepend (result, widget);
1912     }
1913   
1914   result = g_list_prepend (result, priv->arrow_button);
1915   
1916   rtl = (gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_RTL);
1917   
1918   /* move in logical order when
1919    *
1920    *    - dir is TAB_FORWARD
1921    *
1922    *    - in RTL mode and moving left or up
1923    *
1924    *    - in LTR mode and moving right or down
1925    */
1926   if (dir == GTK_DIR_TAB_FORWARD                                        ||
1927       (rtl  && (dir == GTK_DIR_UP   || dir == GTK_DIR_LEFT))            ||
1928       (!rtl && (dir == GTK_DIR_DOWN || dir == GTK_DIR_RIGHT)))
1929     {
1930       result = g_list_reverse (result);
1931     }
1932   
1933   return result;
1934 }
1935
1936 static gboolean
1937 gtk_toolbar_focus_home_or_end (GtkToolbar *toolbar,
1938                                gboolean    focus_home)
1939 {
1940   GList *children, *list;
1941   GtkDirectionType dir = focus_home? GTK_DIR_RIGHT : GTK_DIR_LEFT;
1942   
1943   children = gtk_toolbar_list_children_in_focus_order (toolbar, dir);
1944   
1945   if (gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_RTL)
1946     {
1947       children = g_list_reverse (children);
1948       
1949       dir = (dir == GTK_DIR_RIGHT)? GTK_DIR_LEFT : GTK_DIR_RIGHT;
1950     }
1951   
1952   for (list = children; list != NULL; list = list->next)
1953     {
1954       GtkWidget *child = list->data;
1955
1956       if (gtk_container_get_focus_child (GTK_CONTAINER (toolbar)) == child)
1957         break;
1958       
1959       if (gtk_widget_get_mapped (child) && gtk_widget_child_focus (child, dir))
1960         break;
1961     }
1962   
1963   g_list_free (children);
1964   
1965   return TRUE;
1966 }   
1967
1968 /* Keybinding handler. This function is called when the user presses
1969  * Ctrl TAB or an arrow key.
1970  */
1971 static void
1972 gtk_toolbar_move_focus (GtkWidget        *widget,
1973                         GtkDirectionType  dir)
1974 {
1975   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
1976   GtkContainer *container = GTK_CONTAINER (toolbar);
1977   GtkWidget *focus_child;
1978   GList *list;
1979   gboolean try_focus = FALSE;
1980   GList *children;
1981
1982   focus_child = gtk_container_get_focus_child (container);
1983
1984   if (focus_child && gtk_widget_child_focus (focus_child, dir))
1985     return;
1986   
1987   children = gtk_toolbar_list_children_in_focus_order (toolbar, dir);
1988   
1989   for (list = children; list != NULL; list = list->next)
1990     {
1991       GtkWidget *child = list->data;
1992       
1993       if (try_focus && gtk_widget_get_mapped (child) && gtk_widget_child_focus (child, dir))
1994         break;
1995       
1996       if (child == focus_child)
1997         try_focus = TRUE;
1998     }
1999   
2000   g_list_free (children);
2001 }
2002
2003 /* The focus handler for the toolbar. It called when the user presses
2004  * TAB or otherwise tries to focus the toolbar.
2005  */
2006 static gboolean
2007 gtk_toolbar_focus (GtkWidget        *widget,
2008                    GtkDirectionType  dir)
2009 {
2010   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
2011   GList *children, *list;
2012   gboolean result = FALSE;
2013
2014   /* if focus is already somewhere inside the toolbar then return FALSE.
2015    * The only way focus can stay inside the toolbar is when the user presses
2016    * arrow keys or Ctrl TAB (both of which are handled by the
2017    * gtk_toolbar_move_focus() keybinding function.
2018    */
2019   if (gtk_container_get_focus_child (GTK_CONTAINER (widget)))
2020     return FALSE;
2021
2022   children = gtk_toolbar_list_children_in_focus_order (toolbar, dir);
2023
2024   for (list = children; list != NULL; list = list->next)
2025     {
2026       GtkWidget *child = list->data;
2027       
2028       if (gtk_widget_get_mapped (child) && gtk_widget_child_focus (child, dir))
2029         {
2030           result = TRUE;
2031           break;
2032         }
2033     }
2034
2035   g_list_free (children);
2036
2037   return result;
2038 }
2039
2040 static GtkSettings *
2041 toolbar_get_settings (GtkToolbar *toolbar)
2042 {
2043   return toolbar->priv->settings;
2044 }
2045
2046 static void
2047 style_change_notify (GtkToolbar *toolbar)
2048 {
2049   GtkToolbarPrivate *priv = toolbar->priv;
2050
2051   if (!priv->style_set)
2052     {
2053       /* pretend it was set, then unset, thus reverting to new default */
2054       priv->style_set = TRUE;
2055       gtk_toolbar_unset_style (toolbar);
2056     }
2057 }
2058
2059 static void
2060 icon_size_change_notify (GtkToolbar *toolbar)
2061 {
2062   GtkToolbarPrivate *priv = toolbar->priv;
2063
2064   if (!priv->icon_size_set)
2065     {
2066       /* pretend it was set, then unset, thus reverting to new default */
2067       priv->icon_size_set = TRUE;
2068       gtk_toolbar_unset_icon_size (toolbar);
2069     }
2070 }
2071
2072 static void
2073 animation_change_notify (GtkToolbar *toolbar)
2074 {
2075   GtkToolbarPrivate *priv = toolbar->priv;
2076   GtkSettings *settings = toolbar_get_settings (toolbar);
2077   gboolean animation;
2078
2079   if (settings)
2080     g_object_get (settings,
2081                   "gtk-enable-animations", &animation,
2082                   NULL);
2083   else
2084     animation = DEFAULT_ANIMATION_STATE;
2085
2086   priv->animation = animation;
2087 }
2088
2089 static void
2090 settings_change_notify (GtkSettings      *settings,
2091                         const GParamSpec *pspec,
2092                         GtkToolbar       *toolbar)
2093 {
2094   if (! strcmp (pspec->name, "gtk-toolbar-style"))
2095     style_change_notify (toolbar);
2096   else if (! strcmp (pspec->name, "gtk-toolbar-icon-size"))
2097     icon_size_change_notify (toolbar);
2098   else if (! strcmp (pspec->name, "gtk-enable-animations"))
2099     animation_change_notify (toolbar);
2100 }
2101
2102 static void
2103 gtk_toolbar_screen_changed (GtkWidget *widget,
2104                             GdkScreen *previous_screen)
2105 {
2106   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
2107   GtkToolbarPrivate *priv = toolbar->priv;
2108   GtkSettings *old_settings = toolbar_get_settings (toolbar);
2109   GtkSettings *settings;
2110   
2111   if (gtk_widget_has_screen (GTK_WIDGET (toolbar)))
2112     settings = gtk_widget_get_settings (GTK_WIDGET (toolbar));
2113   else
2114     settings = NULL;
2115   
2116   if (settings == old_settings)
2117     return;
2118   
2119   if (old_settings)
2120     {
2121       g_signal_handler_disconnect (old_settings, priv->settings_connection);
2122
2123       g_object_unref (old_settings);
2124     }
2125
2126   if (settings)
2127     {
2128       priv->settings_connection =
2129         g_signal_connect (settings, "notify",
2130                           G_CALLBACK (settings_change_notify),
2131                           toolbar);
2132
2133       priv->settings = g_object_ref (settings);
2134     }
2135   else
2136     priv->settings = NULL;
2137
2138   style_change_notify (toolbar);
2139   icon_size_change_notify (toolbar);
2140   animation_change_notify (toolbar);
2141 }
2142
2143 static int
2144 find_drop_index (GtkToolbar *toolbar,
2145                  gint        x,
2146                  gint        y)
2147 {
2148   GtkToolbarPrivate *priv = toolbar->priv;
2149   GList *interesting_content;
2150   GList *list;
2151   GtkOrientation orientation;
2152   GtkTextDirection direction;
2153   gint best_distance = G_MAXINT;
2154   gint distance;
2155   gint cursor;
2156   gint pos;
2157   ToolbarContent *best_content;
2158   GtkAllocation allocation;
2159   
2160   /* list items we care about wrt. drag and drop */
2161   interesting_content = NULL;
2162   for (list = priv->content; list != NULL; list = list->next)
2163     {
2164       ToolbarContent *content = list->data;
2165       
2166       if (toolbar_content_get_state (content) == NORMAL)
2167         interesting_content = g_list_prepend (interesting_content, content);
2168     }
2169   interesting_content = g_list_reverse (interesting_content);
2170   
2171   if (!interesting_content)
2172     return 0;
2173   
2174   orientation = priv->orientation;
2175   direction = gtk_widget_get_direction (GTK_WIDGET (toolbar));
2176   
2177   /* distance to first interesting item */
2178   best_content = interesting_content->data;
2179   toolbar_content_get_allocation (best_content, &allocation);
2180   
2181   if (orientation == GTK_ORIENTATION_HORIZONTAL)
2182     {
2183       cursor = x;
2184       
2185       if (direction == GTK_TEXT_DIR_LTR)
2186         pos = allocation.x;
2187       else
2188         pos = allocation.x + allocation.width;
2189     }
2190   else
2191     {
2192       cursor = y;
2193       pos = allocation.y;
2194     }
2195   
2196   best_content = NULL;
2197   best_distance = ABS (pos - cursor);
2198   
2199   /* distance to far end of each item */
2200   for (list = interesting_content; list != NULL; list = list->next)
2201     {
2202       ToolbarContent *content = list->data;
2203       
2204       toolbar_content_get_allocation (content, &allocation);
2205       
2206       if (orientation == GTK_ORIENTATION_HORIZONTAL)
2207         {
2208           if (direction == GTK_TEXT_DIR_LTR)
2209             pos = allocation.x + allocation.width;
2210           else
2211             pos = allocation.x;
2212         }
2213       else
2214         {
2215           pos = allocation.y + allocation.height;
2216         }
2217       
2218       distance = ABS (pos - cursor);
2219       
2220       if (distance < best_distance)
2221         {
2222           best_distance = distance;
2223           best_content = content;
2224         }
2225     }
2226   
2227   g_list_free (interesting_content);
2228   
2229   if (!best_content)
2230     return 0;
2231   else
2232     return g_list_index (priv->content, best_content) + 1;
2233 }
2234
2235 static void
2236 reset_all_placeholders (GtkToolbar *toolbar)
2237 {
2238   GtkToolbarPrivate *priv = toolbar->priv;
2239   GList *list;
2240   
2241   for (list = priv->content; list != NULL; list = list->next)
2242     {
2243       ToolbarContent *content = list->data;
2244       if (toolbar_content_is_placeholder (content))
2245         toolbar_content_set_disappearing (content, TRUE);
2246     }
2247 }
2248
2249 static gint
2250 physical_to_logical (GtkToolbar *toolbar,
2251                      gint        physical)
2252 {
2253   GtkToolbarPrivate *priv = toolbar->priv;
2254   GList *list;
2255   int logical;
2256   
2257   g_assert (physical >= 0);
2258   
2259   logical = 0;
2260   for (list = priv->content; list && physical > 0; list = list->next)
2261     {
2262       ToolbarContent *content = list->data;
2263       
2264       if (!toolbar_content_is_placeholder (content))
2265         logical++;
2266       physical--;
2267     }
2268   
2269   g_assert (physical == 0);
2270   
2271   return logical;
2272 }
2273
2274 static gint
2275 logical_to_physical (GtkToolbar *toolbar,
2276                      gint        logical)
2277 {
2278   GtkToolbarPrivate *priv = toolbar->priv;
2279   GList *list;
2280   gint physical;
2281   
2282   g_assert (logical >= 0);
2283   
2284   physical = 0;
2285   for (list = priv->content; list; list = list->next)
2286     {
2287       ToolbarContent *content = list->data;
2288       
2289       if (!toolbar_content_is_placeholder (content))
2290         {
2291           if (logical == 0)
2292             break;
2293           logical--;
2294         }
2295       
2296       physical++;
2297     }
2298   
2299   g_assert (logical == 0);
2300   
2301   return physical;
2302 }
2303
2304 /**
2305  * gtk_toolbar_set_drop_highlight_item:
2306  * @toolbar: a #GtkToolbar
2307  * @tool_item: (allow-none): a #GtkToolItem, or %NULL to turn of highlighting
2308  * @index_: a position on @toolbar
2309  *
2310  * Highlights @toolbar to give an idea of what it would look like
2311  * if @item was added to @toolbar at the position indicated by @index_.
2312  * If @item is %NULL, highlighting is turned off. In that case @index_ 
2313  * is ignored.
2314  *
2315  * The @tool_item passed to this function must not be part of any widget
2316  * hierarchy. When an item is set as drop highlight item it can not
2317  * added to any widget hierarchy or used as highlight item for another
2318  * toolbar.
2319  * 
2320  * Since: 2.4
2321  **/
2322 void
2323 gtk_toolbar_set_drop_highlight_item (GtkToolbar  *toolbar,
2324                                      GtkToolItem *tool_item,
2325                                      gint         index_)
2326 {
2327   ToolbarContent *content;
2328   GtkToolbarPrivate *priv;
2329   gint n_items;
2330   GtkRequisition requisition;
2331   GtkRequisition old_requisition;
2332   gboolean restart_sliding;
2333   
2334   g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
2335   g_return_if_fail (tool_item == NULL || GTK_IS_TOOL_ITEM (tool_item));
2336
2337   priv = toolbar->priv;
2338
2339   if (!tool_item)
2340     {
2341       if (priv->highlight_tool_item)
2342         {
2343           gtk_widget_unparent (GTK_WIDGET (priv->highlight_tool_item));
2344           g_object_unref (priv->highlight_tool_item);
2345           priv->highlight_tool_item = NULL;
2346         }
2347       
2348       reset_all_placeholders (toolbar);
2349       gtk_toolbar_begin_sliding (toolbar);
2350       return;
2351     }
2352   
2353   n_items = gtk_toolbar_get_n_items (toolbar);
2354   if (index_ < 0 || index_ > n_items)
2355     index_ = n_items;
2356   
2357   if (tool_item != priv->highlight_tool_item)
2358     {
2359       if (priv->highlight_tool_item)
2360         g_object_unref (priv->highlight_tool_item);
2361       
2362       g_object_ref_sink (tool_item);
2363       
2364       priv->highlight_tool_item = tool_item;
2365       
2366       gtk_widget_set_parent (GTK_WIDGET (priv->highlight_tool_item),
2367                              GTK_WIDGET (toolbar));
2368     }
2369   
2370   index_ = logical_to_physical (toolbar, index_);
2371   
2372   content = g_list_nth_data (priv->content, index_);
2373   
2374   if (index_ > 0)
2375     {
2376       ToolbarContent *prev_content;
2377       
2378       prev_content = g_list_nth_data (priv->content, index_ - 1);
2379       
2380       if (prev_content && toolbar_content_is_placeholder (prev_content))
2381         content = prev_content;
2382     }
2383   
2384   if (!content || !toolbar_content_is_placeholder (content))
2385     {
2386       GtkWidget *placeholder;
2387       
2388       placeholder = GTK_WIDGET (gtk_separator_tool_item_new ());
2389
2390       content = toolbar_content_new_tool_item (toolbar,
2391                                                GTK_TOOL_ITEM (placeholder),
2392                                                TRUE, index_);
2393       gtk_widget_show (placeholder);
2394     }
2395   
2396   g_assert (content);
2397   g_assert (toolbar_content_is_placeholder (content));
2398
2399   gtk_widget_get_preferred_size (GTK_WIDGET (priv->highlight_tool_item),
2400                                  &requisition, NULL);
2401
2402   toolbar_content_set_expand (content, gtk_tool_item_get_expand (tool_item));
2403   
2404   restart_sliding = FALSE;
2405   toolbar_content_size_request (content, toolbar, &old_requisition);
2406   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2407     {
2408       requisition.height = -1;
2409       if (requisition.width != old_requisition.width)
2410         restart_sliding = TRUE;
2411     }
2412   else
2413     {
2414       requisition.width = -1;
2415       if (requisition.height != old_requisition.height)
2416         restart_sliding = TRUE;
2417     }
2418
2419   if (toolbar_content_disappearing (content))
2420     restart_sliding = TRUE;
2421   
2422   reset_all_placeholders (toolbar);
2423   toolbar_content_set_disappearing (content, FALSE);
2424   
2425   toolbar_content_set_size_request (content,
2426                                     requisition.width, requisition.height);
2427   
2428   if (restart_sliding)
2429     gtk_toolbar_begin_sliding (toolbar);
2430 }
2431
2432 static void
2433 gtk_toolbar_get_child_property (GtkContainer *container,
2434                                 GtkWidget    *child,
2435                                 guint         property_id,
2436                                 GValue       *value,
2437                                 GParamSpec   *pspec)
2438 {
2439   GtkToolItem *item = GTK_TOOL_ITEM (child);
2440   
2441   switch (property_id)
2442     {
2443     case CHILD_PROP_HOMOGENEOUS:
2444       g_value_set_boolean (value, gtk_tool_item_get_homogeneous (item));
2445       break;
2446       
2447     case CHILD_PROP_EXPAND:
2448       g_value_set_boolean (value, gtk_tool_item_get_expand (item));
2449       break;
2450       
2451     default:
2452       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
2453       break;
2454     }
2455 }
2456
2457 static void
2458 gtk_toolbar_set_child_property (GtkContainer *container,
2459                                 GtkWidget    *child,
2460                                 guint         property_id,
2461                                 const GValue *value,
2462                                 GParamSpec   *pspec)
2463 {
2464   switch (property_id)
2465     {
2466     case CHILD_PROP_HOMOGENEOUS:
2467       gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (child), g_value_get_boolean (value));
2468       break;
2469       
2470     case CHILD_PROP_EXPAND:
2471       gtk_tool_item_set_expand (GTK_TOOL_ITEM (child), g_value_get_boolean (value));
2472       break;
2473       
2474     default:
2475       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
2476       break;
2477     }
2478 }
2479
2480 static void
2481 gtk_toolbar_show_all (GtkWidget *widget)
2482 {
2483   GtkToolbar *toolbar = GTK_TOOLBAR (widget);
2484   GtkToolbarPrivate *priv = toolbar->priv;
2485   GList *list;
2486
2487   for (list = priv->content; list != NULL; list = list->next)
2488     {
2489       ToolbarContent *content = list->data;
2490       
2491       toolbar_content_show_all (content);
2492     }
2493   
2494   gtk_widget_show (widget);
2495 }
2496
2497 static void
2498 gtk_toolbar_add (GtkContainer *container,
2499                  GtkWidget    *widget)
2500 {
2501   GtkToolbar *toolbar = GTK_TOOLBAR (container);
2502
2503   gtk_toolbar_insert (toolbar, GTK_TOOL_ITEM (widget), -1);
2504 }
2505
2506 static void
2507 gtk_toolbar_remove (GtkContainer *container,
2508                     GtkWidget    *widget)
2509 {
2510   GtkToolbar *toolbar = GTK_TOOLBAR (container);
2511   GtkToolbarPrivate *priv = toolbar->priv;
2512   ToolbarContent *content_to_remove;
2513   GList *list;
2514
2515   content_to_remove = NULL;
2516   for (list = priv->content; list != NULL; list = list->next)
2517     {
2518       ToolbarContent *content = list->data;
2519       GtkWidget *child;
2520       
2521       child = toolbar_content_get_widget (content);
2522       if (child && child == widget)
2523         {
2524           content_to_remove = content;
2525           break;
2526         }
2527     }
2528   
2529   g_return_if_fail (content_to_remove != NULL);
2530   
2531   toolbar_content_remove (content_to_remove, toolbar);
2532   toolbar_content_free (content_to_remove);
2533 }
2534
2535 static void
2536 gtk_toolbar_forall (GtkContainer *container,
2537                     gboolean      include_internals,
2538                     GtkCallback   callback,
2539                     gpointer      callback_data)
2540 {
2541   GtkToolbar *toolbar = GTK_TOOLBAR (container);
2542   GtkToolbarPrivate *priv = toolbar->priv;
2543   GList *list;
2544   
2545   g_return_if_fail (callback != NULL);
2546   
2547   list = priv->content;
2548   while (list)
2549     {
2550       ToolbarContent *content = list->data;
2551       GList *next = list->next;
2552       
2553       if (include_internals || !toolbar_content_is_placeholder (content))
2554         {
2555           GtkWidget *child = toolbar_content_get_widget (content);
2556           
2557           if (child)
2558             callback (child, callback_data);
2559         }
2560       
2561       list = next;
2562     }
2563   
2564   if (include_internals)
2565     callback (priv->arrow_button, callback_data);
2566 }
2567
2568 static GType
2569 gtk_toolbar_child_type (GtkContainer *container)
2570 {
2571   return GTK_TYPE_TOOL_ITEM;
2572 }
2573
2574 static void
2575 gtk_toolbar_reconfigured (GtkToolbar *toolbar)
2576 {
2577   GtkToolbarPrivate *priv = toolbar->priv;
2578   GList *list;
2579   
2580   list = priv->content;
2581   while (list)
2582     {
2583       ToolbarContent *content = list->data;
2584       GList *next = list->next;
2585       
2586       toolbar_content_toolbar_reconfigured (content, toolbar);
2587       
2588       list = next;
2589     }
2590 }
2591
2592 static void
2593 gtk_toolbar_orientation_changed (GtkToolbar    *toolbar,
2594                                  GtkOrientation orientation)
2595 {
2596   GtkToolbarPrivate *priv = toolbar->priv;
2597
2598   if (priv->orientation != orientation)
2599     {
2600       priv->orientation = orientation;
2601       
2602       if (orientation == GTK_ORIENTATION_HORIZONTAL)
2603         gtk_arrow_set (GTK_ARROW (priv->arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2604       else
2605         gtk_arrow_set (GTK_ARROW (priv->arrow), GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2606       
2607       gtk_toolbar_reconfigured (toolbar);
2608       
2609       gtk_widget_queue_resize (GTK_WIDGET (toolbar));
2610       g_object_notify (G_OBJECT (toolbar), "orientation");
2611     }
2612 }
2613
2614 static void
2615 gtk_toolbar_real_style_changed (GtkToolbar     *toolbar,
2616                                 GtkToolbarStyle style)
2617 {
2618   GtkToolbarPrivate *priv = toolbar->priv;
2619
2620   if (priv->style != style)
2621     {
2622       priv->style = style;
2623
2624       gtk_toolbar_reconfigured (toolbar);
2625       
2626       gtk_widget_queue_resize (GTK_WIDGET (toolbar));
2627       g_object_notify (G_OBJECT (toolbar), "toolbar-style");
2628     }
2629 }
2630
2631 static void
2632 menu_position_func (GtkMenu  *menu,
2633                     gint     *x,
2634                     gint     *y,
2635                     gboolean *push_in,
2636                     gpointer  user_data)
2637 {
2638   GtkAllocation allocation;
2639   GtkToolbar *toolbar = GTK_TOOLBAR (user_data);
2640   GtkToolbarPrivate *priv = toolbar->priv;
2641   GtkRequisition req;
2642   GtkRequisition menu_req;
2643   GdkRectangle monitor;
2644   gint monitor_num;
2645   GdkScreen *screen;
2646
2647   gtk_widget_get_preferred_size (priv->arrow_button,
2648                                  &req, NULL);
2649   gtk_widget_get_preferred_size (GTK_WIDGET (menu),
2650                                  &menu_req, NULL);
2651
2652   screen = gtk_widget_get_screen (GTK_WIDGET (menu));
2653   monitor_num = gdk_screen_get_monitor_at_window (screen,
2654                                                   gtk_widget_get_window (priv->arrow_button));
2655   if (monitor_num < 0)
2656     monitor_num = 0;
2657   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
2658
2659   gtk_widget_get_allocation (priv->arrow_button, &allocation);
2660
2661   gdk_window_get_origin (gtk_button_get_event_window (GTK_BUTTON (priv->arrow_button)), x, y);
2662   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2663     {
2664       if (gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_LTR) 
2665         *x += allocation.width - req.width;
2666       else 
2667         *x += req.width - menu_req.width;
2668
2669       if ((*y + allocation.height + menu_req.height) <= monitor.y + monitor.height)
2670         *y += allocation.height;
2671       else if ((*y - menu_req.height) >= monitor.y)
2672         *y -= menu_req.height;
2673       else if (monitor.y + monitor.height - (*y + allocation.height) > *y)
2674         *y += allocation.height;
2675       else
2676         *y -= menu_req.height;
2677     }
2678   else 
2679     {
2680       if (gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_LTR) 
2681         *x += allocation.width;
2682       else 
2683         *x -= menu_req.width;
2684
2685       if (*y + menu_req.height > monitor.y + monitor.height &&
2686           *y + allocation.height - monitor.y > monitor.y + monitor.height - *y)
2687         *y += allocation.height - menu_req.height;
2688     }
2689
2690   *push_in = FALSE;
2691 }
2692
2693 static void
2694 show_menu (GtkToolbar     *toolbar,
2695            GdkEventButton *event)
2696 {
2697   GtkToolbarPrivate *priv = toolbar->priv;
2698
2699   rebuild_menu (toolbar);
2700
2701   gtk_widget_show_all (GTK_WIDGET (priv->menu));
2702
2703   gtk_menu_popup (priv->menu, NULL, NULL,
2704                   menu_position_func, toolbar,
2705                   event? event->button : 0,
2706                   event? event->time : gtk_get_current_event_time());
2707 }
2708
2709 static void
2710 gtk_toolbar_arrow_button_clicked (GtkWidget  *button,
2711                                   GtkToolbar *toolbar)
2712 {
2713   GtkToolbarPrivate *priv = toolbar->priv;
2714
2715   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->arrow_button)) &&
2716       (!priv->menu || !gtk_widget_get_visible (GTK_WIDGET (priv->menu))))
2717     {
2718       /* We only get here when the button is clicked with the keyboard,
2719        * because mouse button presses result in the menu being shown so
2720        * that priv->menu would be non-NULL and visible.
2721        */
2722       show_menu (toolbar, NULL);
2723       gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
2724     }
2725 }
2726
2727 static gboolean
2728 gtk_toolbar_arrow_button_press (GtkWidget      *button,
2729                                 GdkEventButton *event,
2730                                 GtkToolbar     *toolbar)
2731 {
2732   show_menu (toolbar, event);
2733   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
2734   
2735   return TRUE;
2736 }
2737
2738 static gboolean
2739 gtk_toolbar_button_press (GtkWidget      *toolbar,
2740                           GdkEventButton *event)
2741 {
2742   GtkWidget *window;
2743
2744   if (event->button == 3)
2745     {
2746       gboolean return_value;
2747
2748       g_signal_emit (toolbar, toolbar_signals[POPUP_CONTEXT_MENU], 0,
2749                      (int)event->x_root, (int)event->y_root, event->button,
2750                      &return_value);
2751
2752       return return_value;
2753     }
2754
2755   window = gtk_widget_get_toplevel (toolbar);
2756
2757   if (window)
2758     {
2759       gboolean window_drag = FALSE;
2760
2761       gtk_widget_style_get (toolbar,
2762                             "window-dragging", &window_drag,
2763                             NULL);
2764
2765       if (window_drag)
2766         {
2767           gtk_window_begin_move_drag (GTK_WINDOW (window),
2768                                       event->button,
2769                                       event->x_root,
2770                                       event->y_root,
2771                                       event->time);
2772
2773           return TRUE;
2774         }
2775     }
2776
2777   return FALSE;
2778 }
2779
2780 static gboolean
2781 gtk_toolbar_popup_menu (GtkWidget *toolbar)
2782 {
2783   gboolean return_value;
2784   /* This function is the handler for the "popup menu" keybinding,
2785    * ie., it is called when the user presses Shift F10
2786    */
2787   g_signal_emit (toolbar, toolbar_signals[POPUP_CONTEXT_MENU], 0,
2788                  -1, -1, -1, &return_value);
2789   
2790   return return_value;
2791 }
2792
2793 /**
2794  * gtk_toolbar_new:
2795  * 
2796  * Creates a new toolbar. 
2797  
2798  * Return Value: the newly-created toolbar.
2799  **/
2800 GtkWidget *
2801 gtk_toolbar_new (void)
2802 {
2803   GtkToolbar *toolbar;
2804   
2805   toolbar = g_object_new (GTK_TYPE_TOOLBAR, NULL);
2806   
2807   return GTK_WIDGET (toolbar);
2808 }
2809
2810 /**
2811  * gtk_toolbar_insert:
2812  * @toolbar: a #GtkToolbar
2813  * @item: a #GtkToolItem
2814  * @pos: the position of the new item
2815  *
2816  * Insert a #GtkToolItem into the toolbar at position @pos. If @pos is
2817  * 0 the item is prepended to the start of the toolbar. If @pos is
2818  * negative, the item is appended to the end of the toolbar.
2819  *
2820  * Since: 2.4
2821  **/
2822 void
2823 gtk_toolbar_insert (GtkToolbar  *toolbar,
2824                     GtkToolItem *item,
2825                     gint         pos)
2826 {
2827   g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
2828   g_return_if_fail (GTK_IS_TOOL_ITEM (item));
2829   
2830   if (pos >= 0)
2831     pos = logical_to_physical (toolbar, pos);
2832
2833   toolbar_content_new_tool_item (toolbar, item, FALSE, pos);
2834 }
2835
2836 /**
2837  * gtk_toolbar_get_item_index:
2838  * @toolbar: a #GtkToolbar
2839  * @item: a #GtkToolItem that is a child of @toolbar
2840  * 
2841  * Returns the position of @item on the toolbar, starting from 0.
2842  * It is an error if @item is not a child of the toolbar.
2843  * 
2844  * Return value: the position of item on the toolbar.
2845  * 
2846  * Since: 2.4
2847  **/
2848 gint
2849 gtk_toolbar_get_item_index (GtkToolbar  *toolbar,
2850                             GtkToolItem *item)
2851 {
2852   GtkToolbarPrivate *priv;
2853   GList *list;
2854   int n;
2855   
2856   g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), -1);
2857   g_return_val_if_fail (GTK_IS_TOOL_ITEM (item), -1);
2858   g_return_val_if_fail (gtk_widget_get_parent (GTK_WIDGET (item)) == GTK_WIDGET (toolbar), -1);
2859
2860   priv = toolbar->priv;
2861
2862   n = 0;
2863   for (list = priv->content; list != NULL; list = list->next)
2864     {
2865       ToolbarContent *content = list->data;
2866       GtkWidget *widget;
2867       
2868       widget = toolbar_content_get_widget (content);
2869       
2870       if (item == GTK_TOOL_ITEM (widget))
2871         break;
2872       
2873       ++n;
2874     }
2875   
2876   return physical_to_logical (toolbar, n);
2877 }
2878
2879 /**
2880  * gtk_toolbar_set_style:
2881  * @toolbar: a #GtkToolbar.
2882  * @style: the new style for @toolbar.
2883  * 
2884  * Alters the view of @toolbar to display either icons only, text only, or both.
2885  **/
2886 void
2887 gtk_toolbar_set_style (GtkToolbar      *toolbar,
2888                        GtkToolbarStyle  style)
2889 {
2890   GtkToolbarPrivate *priv;
2891
2892   g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
2893
2894   priv = toolbar->priv;
2895
2896   priv->style_set = TRUE;
2897   g_signal_emit (toolbar, toolbar_signals[STYLE_CHANGED], 0, style);
2898 }
2899
2900 /**
2901  * gtk_toolbar_get_style:
2902  * @toolbar: a #GtkToolbar
2903  *
2904  * Retrieves whether the toolbar has text, icons, or both . See
2905  * gtk_toolbar_set_style().
2906  
2907  * Return value: the current style of @toolbar
2908  **/
2909 GtkToolbarStyle
2910 gtk_toolbar_get_style (GtkToolbar *toolbar)
2911 {
2912   g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), DEFAULT_TOOLBAR_STYLE);
2913
2914   return toolbar->priv->style;
2915 }
2916
2917 /**
2918  * gtk_toolbar_unset_style:
2919  * @toolbar: a #GtkToolbar
2920  * 
2921  * Unsets a toolbar style set with gtk_toolbar_set_style(), so that
2922  * user preferences will be used to determine the toolbar style.
2923  **/
2924 void
2925 gtk_toolbar_unset_style (GtkToolbar *toolbar)
2926 {
2927   GtkToolbarPrivate *priv;
2928   GtkToolbarStyle style;
2929   
2930   g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
2931
2932   priv = toolbar->priv;
2933
2934   if (priv->style_set)
2935     {
2936       GtkSettings *settings = toolbar_get_settings (toolbar);
2937       
2938       if (settings)
2939         g_object_get (settings,
2940                       "gtk-toolbar-style", &style,
2941                       NULL);
2942       else
2943         style = DEFAULT_TOOLBAR_STYLE;
2944
2945       if (style != priv->style)
2946         g_signal_emit (toolbar, toolbar_signals[STYLE_CHANGED], 0, style);
2947
2948       priv->style_set = FALSE;
2949     }
2950 }
2951
2952 /**
2953  * gtk_toolbar_get_n_items:
2954  * @toolbar: a #GtkToolbar
2955  * 
2956  * Returns the number of items on the toolbar.
2957  * 
2958  * Return value: the number of items on the toolbar
2959  * 
2960  * Since: 2.4
2961  **/
2962 gint
2963 gtk_toolbar_get_n_items (GtkToolbar *toolbar)
2964 {
2965   GtkToolbarPrivate *priv;
2966
2967   g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), -1);
2968
2969   priv = toolbar->priv;
2970
2971   return physical_to_logical (toolbar, g_list_length (priv->content));
2972 }
2973
2974 /**
2975  * gtk_toolbar_get_nth_item:
2976  * @toolbar: a #GtkToolbar
2977  * @n: A position on the toolbar
2978  *
2979  * Returns the @n<!-- -->'th item on @toolbar, or %NULL if the
2980  * toolbar does not contain an @n<!-- -->'th item.
2981  *
2982  * Return value: (transfer none): The @n<!-- -->'th #GtkToolItem on @toolbar,
2983  *     or %NULL if there isn't an @n<!-- -->'th item.
2984  *
2985  * Since: 2.4
2986  **/
2987 GtkToolItem *
2988 gtk_toolbar_get_nth_item (GtkToolbar *toolbar,
2989                           gint        n)
2990 {
2991   GtkToolbarPrivate *priv;
2992   ToolbarContent *content;
2993   gint n_items;
2994   
2995   g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), NULL);
2996
2997   priv = toolbar->priv;
2998
2999   n_items = gtk_toolbar_get_n_items (toolbar);
3000   
3001   if (n < 0 || n >= n_items)
3002     return NULL;
3003
3004   content = g_list_nth_data (priv->content, logical_to_physical (toolbar, n));
3005   
3006   g_assert (content);
3007   g_assert (!toolbar_content_is_placeholder (content));
3008   
3009   return GTK_TOOL_ITEM (toolbar_content_get_widget (content));
3010 }
3011
3012 /**
3013  * gtk_toolbar_get_icon_size:
3014  * @toolbar: a #GtkToolbar
3015  *
3016  * Retrieves the icon size for the toolbar. See gtk_toolbar_set_icon_size().
3017  *
3018  * Return value: (type int): the current icon size for the icons on
3019  * the toolbar.
3020  **/
3021 GtkIconSize
3022 gtk_toolbar_get_icon_size (GtkToolbar *toolbar)
3023 {
3024   g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), DEFAULT_ICON_SIZE);
3025
3026   return toolbar->priv->icon_size;
3027 }
3028
3029 /**
3030  * gtk_toolbar_get_relief_style:
3031  * @toolbar: a #GtkToolbar
3032  * 
3033  * Returns the relief style of buttons on @toolbar. See
3034  * gtk_button_set_relief().
3035  * 
3036  * Return value: The relief style of buttons on @toolbar.
3037  * 
3038  * Since: 2.4
3039  **/
3040 GtkReliefStyle
3041 gtk_toolbar_get_relief_style (GtkToolbar *toolbar)
3042 {
3043   g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), GTK_RELIEF_NONE);
3044   
3045   return get_button_relief (toolbar);
3046 }
3047
3048 /**
3049  * gtk_toolbar_set_show_arrow:
3050  * @toolbar: a #GtkToolbar
3051  * @show_arrow: Whether to show an overflow menu
3052  * 
3053  * Sets whether to show an overflow menu when
3054  * @toolbar doesn't have room for all items on it. If %TRUE,
3055  * items that there are not room are available through an
3056  * overflow menu.
3057  * 
3058  * Since: 2.4
3059  **/
3060 void
3061 gtk_toolbar_set_show_arrow (GtkToolbar *toolbar,
3062                             gboolean    show_arrow)
3063 {
3064   GtkToolbarPrivate *priv;
3065
3066   g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
3067
3068   priv = toolbar->priv;
3069
3070   show_arrow = show_arrow != FALSE;
3071   
3072   if (priv->show_arrow != show_arrow)
3073     {
3074       priv->show_arrow = show_arrow;
3075       
3076       if (!priv->show_arrow)
3077         gtk_widget_hide (priv->arrow_button);
3078       
3079       gtk_widget_queue_resize (GTK_WIDGET (toolbar));      
3080       g_object_notify (G_OBJECT (toolbar), "show-arrow");
3081     }
3082 }
3083
3084 /**
3085  * gtk_toolbar_get_show_arrow:
3086  * @toolbar: a #GtkToolbar
3087  * 
3088  * Returns whether the toolbar has an overflow menu.
3089  * See gtk_toolbar_set_show_arrow().
3090  * 
3091  * Return value: %TRUE if the toolbar has an overflow menu.
3092  * 
3093  * Since: 2.4
3094  **/
3095 gboolean
3096 gtk_toolbar_get_show_arrow (GtkToolbar *toolbar)
3097 {
3098   g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), FALSE);
3099
3100   return toolbar->priv->show_arrow;
3101 }
3102
3103 /**
3104  * gtk_toolbar_get_drop_index:
3105  * @toolbar: a #GtkToolbar
3106  * @x: x coordinate of a point on the toolbar
3107  * @y: y coordinate of a point on the toolbar
3108  *
3109  * Returns the position corresponding to the indicated point on
3110  * @toolbar. This is useful when dragging items to the toolbar:
3111  * this function returns the position a new item should be
3112  * inserted.
3113  *
3114  * @x and @y are in @toolbar coordinates.
3115  * 
3116  * Return value: The position corresponding to the point (@x, @y) on the toolbar.
3117  * 
3118  * Since: 2.4
3119  **/
3120 gint
3121 gtk_toolbar_get_drop_index (GtkToolbar *toolbar,
3122                             gint        x,
3123                             gint        y)
3124 {
3125   g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), -1);
3126   
3127   return physical_to_logical (toolbar, find_drop_index (toolbar, x, y));
3128 }
3129
3130 static void
3131 gtk_toolbar_finalize (GObject *object)
3132 {
3133   GList *list;
3134   GtkToolbar *toolbar = GTK_TOOLBAR (object);
3135   GtkToolbarPrivate *priv = toolbar->priv;
3136
3137   if (priv->arrow_button)
3138     gtk_widget_unparent (priv->arrow_button);
3139
3140   if (priv->sibling_path != NULL)
3141     {
3142       gtk_widget_path_unref (priv->sibling_path);
3143       priv->sibling_path = NULL;
3144     }
3145
3146   for (list = priv->content; list != NULL; list = list->next)
3147     {
3148       ToolbarContent *content = list->data;
3149
3150       toolbar_content_free (content);
3151     }
3152   
3153   g_list_free (priv->content);
3154   
3155   g_timer_destroy (priv->timer);
3156   
3157   if (priv->menu)
3158     {
3159       g_signal_handlers_disconnect_by_func (priv->menu,
3160                                             menu_deactivated, toolbar);
3161       gtk_widget_destroy (GTK_WIDGET (priv->menu));
3162     }
3163
3164   if (priv->idle_id)
3165     g_source_remove (priv->idle_id);
3166
3167   G_OBJECT_CLASS (gtk_toolbar_parent_class)->finalize (object);
3168 }
3169
3170 /**
3171  * gtk_toolbar_set_icon_size:
3172  * @toolbar: A #GtkToolbar
3173  * @icon_size: (type int): The #GtkIconSize that stock icons in the
3174  *     toolbar shall have.
3175  *
3176  * This function sets the size of stock icons in the toolbar. You
3177  * can call it both before you add the icons and after they've been
3178  * added. The size you set will override user preferences for the default
3179  * icon size.
3180  * 
3181  * This should only be used for special-purpose toolbars, normal
3182  * application toolbars should respect the user preferences for the
3183  * size of icons.
3184  **/
3185 void
3186 gtk_toolbar_set_icon_size (GtkToolbar  *toolbar,
3187                            GtkIconSize  icon_size)
3188 {
3189   GtkToolbarPrivate *priv;
3190
3191   g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
3192   g_return_if_fail (icon_size != GTK_ICON_SIZE_INVALID);
3193
3194   priv = toolbar->priv;
3195
3196   if (!priv->icon_size_set)
3197     {
3198       priv->icon_size_set = TRUE;
3199       g_object_notify (G_OBJECT (toolbar), "icon-size-set");
3200     }
3201
3202   if (priv->icon_size == icon_size)
3203     return;
3204
3205   priv->icon_size = icon_size;
3206   g_object_notify (G_OBJECT (toolbar), "icon-size");
3207   
3208   gtk_toolbar_reconfigured (toolbar);
3209   
3210   gtk_widget_queue_resize (GTK_WIDGET (toolbar));
3211 }
3212
3213 /**
3214  * gtk_toolbar_unset_icon_size:
3215  * @toolbar: a #GtkToolbar
3216  * 
3217  * Unsets toolbar icon size set with gtk_toolbar_set_icon_size(), so that
3218  * user preferences will be used to determine the icon size.
3219  **/
3220 void
3221 gtk_toolbar_unset_icon_size (GtkToolbar *toolbar)
3222 {
3223   GtkToolbarPrivate *priv;
3224   GtkIconSize size;
3225
3226   g_return_if_fail (GTK_IS_TOOLBAR (toolbar));
3227
3228   priv = toolbar->priv;
3229
3230   if (priv->icon_size_set)
3231     {
3232       GtkSettings *settings = toolbar_get_settings (toolbar);
3233       
3234       if (settings)
3235         {
3236           g_object_get (settings,
3237                         "gtk-toolbar-icon-size", &size,
3238                         NULL);
3239         }
3240       else
3241         size = DEFAULT_ICON_SIZE;
3242
3243       if (size != priv->icon_size)
3244         {
3245           gtk_toolbar_set_icon_size (toolbar, size);
3246           g_object_notify (G_OBJECT (toolbar), "icon-size");      
3247         }
3248
3249       priv->icon_size_set = FALSE;
3250       g_object_notify (G_OBJECT (toolbar), "icon-size-set");      
3251     }
3252 }
3253
3254 /*
3255  * ToolbarContent methods
3256  */
3257 typedef enum {
3258   UNKNOWN,
3259   YES,
3260   NO
3261 } TriState;
3262
3263 struct _ToolbarContent
3264 {
3265   ItemState      state;
3266
3267   GtkToolItem   *item;
3268   GtkAllocation  allocation;
3269   GtkAllocation  start_allocation;
3270   GtkAllocation  goal_allocation;
3271   guint          is_placeholder : 1;
3272   guint          disappearing : 1;
3273   guint          has_menu : 2;
3274 };
3275
3276 static void
3277 toolbar_item_visiblity_notify_cb (GObject *obj,
3278                                   GParamSpec *pspec,
3279                                   gpointer user_data)
3280 {
3281   GtkToolbar *toolbar = user_data;
3282
3283   gtk_toolbar_invalidate_order (toolbar);
3284 }
3285
3286 static ToolbarContent *
3287 toolbar_content_new_tool_item (GtkToolbar  *toolbar,
3288                                GtkToolItem *item,
3289                                gboolean     is_placeholder,
3290                                gint         pos)
3291 {
3292   GtkToolbarPrivate *priv = toolbar->priv;
3293   ToolbarContent *content;
3294
3295   content = g_slice_new0 (ToolbarContent);
3296   
3297   content->state = NOT_ALLOCATED;
3298   content->item = item;
3299   content->is_placeholder = is_placeholder;
3300
3301   priv->content = g_list_insert (priv->content, content, pos);
3302   
3303   gtk_widget_set_parent (GTK_WIDGET (item), GTK_WIDGET (toolbar));
3304   gtk_toolbar_invalidate_order (toolbar);
3305
3306   g_signal_connect (content->item, "notify::visible",
3307                     G_CALLBACK (toolbar_item_visiblity_notify_cb), toolbar);
3308
3309   if (!is_placeholder)
3310     {
3311       priv->num_children++;
3312
3313       gtk_toolbar_stop_sliding (toolbar);
3314     }
3315
3316   gtk_widget_queue_resize (GTK_WIDGET (toolbar));
3317   priv->need_rebuild = TRUE;
3318   
3319   return content;
3320 }
3321
3322 static void
3323 toolbar_content_remove (ToolbarContent *content,
3324                         GtkToolbar     *toolbar)
3325 {
3326   GtkToolbarPrivate *priv = toolbar->priv;
3327
3328   gtk_toolbar_invalidate_order (toolbar);
3329   gtk_widget_unparent (GTK_WIDGET (content->item));
3330
3331   g_signal_handlers_disconnect_by_func (content->item,
3332                                         toolbar_item_visiblity_notify_cb,
3333                                         toolbar);
3334
3335   priv->content = g_list_remove (priv->content, content);
3336
3337   if (!toolbar_content_is_placeholder (content))
3338     priv->num_children--;
3339
3340   gtk_widget_queue_resize (GTK_WIDGET (toolbar));
3341   priv->need_rebuild = TRUE;
3342 }
3343
3344 static void
3345 toolbar_content_free (ToolbarContent *content)
3346 {
3347   g_slice_free (ToolbarContent, content);
3348 }
3349
3350 static gint
3351 calculate_max_homogeneous_pixels (GtkWidget *widget)
3352 {
3353   PangoContext *context;
3354   PangoFontMetrics *metrics;
3355   const PangoFontDescription *font_desc;
3356   GtkStyleContext *style_context;
3357   GtkStateFlags state;
3358   gint char_width;
3359   
3360   context = gtk_widget_get_pango_context (widget);
3361   style_context = gtk_widget_get_style_context (widget);
3362   state = gtk_widget_get_state_flags (widget);
3363
3364   font_desc = gtk_style_context_get_font (style_context, state);
3365
3366   metrics = pango_context_get_metrics (context, font_desc,
3367                                        pango_context_get_language (context));
3368   char_width = pango_font_metrics_get_approximate_char_width (metrics);
3369   pango_font_metrics_unref (metrics);
3370   
3371   return PANGO_PIXELS (MAX_HOMOGENEOUS_N_CHARS * char_width);
3372 }
3373
3374 static void
3375 toolbar_content_draw (ToolbarContent *content,
3376                       GtkContainer   *container,
3377                       cairo_t        *cr)
3378 {
3379   GtkWidget *widget;
3380
3381   if (content->is_placeholder)
3382     return;
3383   
3384   widget = GTK_WIDGET (content->item);
3385
3386   if (widget)
3387     gtk_container_propagate_draw (container, widget, cr);
3388 }
3389
3390 static gboolean
3391 toolbar_content_visible (ToolbarContent *content,
3392                          GtkToolbar     *toolbar)
3393 {
3394   GtkToolbarPrivate *priv = toolbar->priv;
3395   GtkToolItem *item;
3396
3397   item = content->item;
3398
3399   if (!gtk_widget_get_visible (GTK_WIDGET (item)))
3400     return FALSE;
3401
3402   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
3403       gtk_tool_item_get_visible_horizontal (item))
3404     return TRUE;
3405
3406   if (priv->orientation == GTK_ORIENTATION_VERTICAL &&
3407       gtk_tool_item_get_visible_vertical (item))
3408     return TRUE;
3409       
3410   return FALSE;
3411 }
3412
3413 static void
3414 toolbar_content_size_request (ToolbarContent *content,
3415                               GtkToolbar     *toolbar,
3416                               GtkRequisition *requisition)
3417 {
3418   gtk_widget_get_preferred_size (GTK_WIDGET (content->item),
3419                                  requisition, NULL);
3420   if (content->is_placeholder &&
3421       content->disappearing)
3422     {
3423       requisition->width = 0;
3424       requisition->height = 0;
3425     }
3426 }
3427
3428 static gboolean
3429 toolbar_content_is_homogeneous (ToolbarContent *content,
3430                                 GtkToolbar     *toolbar)
3431 {
3432   GtkToolbarPrivate *priv = toolbar->priv;
3433   GtkRequisition requisition;
3434   gboolean result;
3435   
3436   if (priv->max_homogeneous_pixels < 0)
3437     {
3438       priv->max_homogeneous_pixels =
3439         calculate_max_homogeneous_pixels (GTK_WIDGET (toolbar));
3440     }
3441   
3442   toolbar_content_size_request (content, toolbar, &requisition);
3443   
3444   if (requisition.width > priv->max_homogeneous_pixels)
3445     return FALSE;
3446
3447   result = gtk_tool_item_get_homogeneous (content->item) &&
3448            !GTK_IS_SEPARATOR_TOOL_ITEM (content->item);
3449
3450   if (gtk_tool_item_get_is_important (content->item) &&
3451       priv->style == GTK_TOOLBAR_BOTH_HORIZ &&
3452       priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3453     {
3454       result = FALSE;
3455     }
3456
3457   return result;
3458 }
3459
3460 static gboolean
3461 toolbar_content_is_placeholder (ToolbarContent *content)
3462 {
3463   if (content->is_placeholder)
3464     return TRUE;
3465   
3466   return FALSE;
3467 }
3468
3469 static gboolean
3470 toolbar_content_disappearing (ToolbarContent *content)
3471 {
3472   if (content->disappearing)
3473     return TRUE;
3474   
3475   return FALSE;
3476 }
3477
3478 static ItemState
3479 toolbar_content_get_state (ToolbarContent *content)
3480 {
3481   return content->state;
3482 }
3483
3484 static gboolean
3485 toolbar_content_child_visible (ToolbarContent *content)
3486 {
3487   return gtk_widget_get_child_visible (GTK_WIDGET (content->item));
3488 }
3489
3490 static void
3491 toolbar_content_get_goal_allocation (ToolbarContent *content,
3492                                      GtkAllocation  *allocation)
3493 {
3494   *allocation = content->goal_allocation;
3495 }
3496
3497 static void
3498 toolbar_content_get_allocation (ToolbarContent *content,
3499                                 GtkAllocation  *allocation)
3500 {
3501   *allocation = content->allocation;
3502 }
3503
3504 static void
3505 toolbar_content_set_start_allocation (ToolbarContent *content,
3506                                       GtkAllocation  *allocation)
3507 {
3508   content->start_allocation = *allocation;
3509 }
3510
3511 static gboolean
3512 toolbar_content_get_expand (ToolbarContent *content)
3513 {
3514   if (!content->disappearing &&
3515       gtk_tool_item_get_expand (content->item))
3516     return TRUE;
3517
3518   return FALSE;
3519 }
3520
3521 static void
3522 toolbar_content_set_goal_allocation (ToolbarContent *content,
3523                                      GtkAllocation  *allocation)
3524 {
3525   content->goal_allocation = *allocation;
3526 }
3527
3528 static void
3529 toolbar_content_set_child_visible (ToolbarContent *content,
3530                                    GtkToolbar     *toolbar,
3531                                    gboolean        visible)
3532 {
3533   gtk_widget_set_child_visible (GTK_WIDGET (content->item),
3534                                 visible);
3535 }
3536
3537 static void
3538 toolbar_content_get_start_allocation (ToolbarContent *content,
3539                                       GtkAllocation  *start_allocation)
3540 {
3541   *start_allocation = content->start_allocation;
3542 }
3543
3544 static void
3545 toolbar_content_size_allocate (ToolbarContent *content,
3546                                GtkAllocation  *allocation)
3547 {
3548   content->allocation = *allocation;
3549   gtk_widget_size_allocate (GTK_WIDGET (content->item),
3550                             allocation);
3551 }
3552
3553 static void
3554 toolbar_content_set_state (ToolbarContent *content,
3555                            ItemState       state)
3556 {
3557   content->state = state;
3558 }
3559
3560 static GtkWidget *
3561 toolbar_content_get_widget (ToolbarContent *content)
3562 {
3563   return GTK_WIDGET (content->item);
3564 }
3565
3566
3567 static void
3568 toolbar_content_set_disappearing (ToolbarContent *content,
3569                                   gboolean        disappearing)
3570 {
3571   content->disappearing = disappearing;
3572 }
3573
3574 static void
3575 toolbar_content_set_size_request (ToolbarContent *content,
3576                                   gint            width,
3577                                   gint            height)
3578 {
3579   gtk_widget_set_size_request (GTK_WIDGET (content->item),
3580                                width, height);
3581 }
3582
3583 static void
3584 toolbar_content_toolbar_reconfigured (ToolbarContent *content,
3585                                       GtkToolbar     *toolbar)
3586 {
3587   gtk_tool_item_toolbar_reconfigured (content->item);
3588 }
3589
3590 static GtkWidget *
3591 toolbar_content_retrieve_menu_item (ToolbarContent *content)
3592 {
3593   return gtk_tool_item_retrieve_proxy_menu_item (content->item);
3594 }
3595
3596 static gboolean
3597 toolbar_content_has_proxy_menu_item (ToolbarContent *content)
3598 {
3599   GtkWidget *menu_item;
3600
3601   if (content->has_menu == YES)
3602     return TRUE;
3603   else if (content->has_menu == NO)
3604     return FALSE;
3605
3606   menu_item = toolbar_content_retrieve_menu_item (content);
3607
3608   content->has_menu = menu_item? YES : NO;
3609
3610   return menu_item != NULL;
3611 }
3612
3613 static void
3614 toolbar_content_set_unknown_menu_status (ToolbarContent *content)
3615 {
3616   content->has_menu = UNKNOWN;
3617 }
3618
3619 static gboolean
3620 toolbar_content_is_separator (ToolbarContent *content)
3621 {
3622   return GTK_IS_SEPARATOR_TOOL_ITEM (content->item);
3623 }
3624
3625 static void
3626 toolbar_content_set_expand (ToolbarContent *content,
3627                             gboolean        expand)
3628 {
3629   gtk_tool_item_set_expand (content->item, expand);
3630 }
3631
3632 static void
3633 toolbar_content_show_all (ToolbarContent  *content)
3634 {
3635   GtkWidget *widget;
3636   
3637   widget = toolbar_content_get_widget (content);
3638   if (widget)
3639     gtk_widget_show_all (widget);
3640 }
3641
3642 /*
3643  * Getters
3644  */
3645 static GtkReliefStyle
3646 get_button_relief (GtkToolbar *toolbar)
3647 {
3648   GtkReliefStyle button_relief = GTK_RELIEF_NORMAL;
3649
3650   gtk_widget_style_get (GTK_WIDGET (toolbar),
3651                         "button-relief", &button_relief,
3652                         NULL);
3653   
3654   return button_relief;
3655 }
3656
3657 static gint
3658 get_internal_padding (GtkToolbar *toolbar)
3659 {
3660   gint ipadding = 0;
3661   
3662   gtk_widget_style_get (GTK_WIDGET (toolbar),
3663                         "internal-padding", &ipadding,
3664                         NULL);
3665   
3666   return ipadding;
3667 }
3668
3669 static gint
3670 get_max_child_expand (GtkToolbar *toolbar)
3671 {
3672   gint mexpand = G_MAXINT;
3673
3674   gtk_widget_style_get (GTK_WIDGET (toolbar),
3675                         "max-child-expand", &mexpand,
3676                         NULL);
3677   return mexpand;
3678 }
3679
3680 static GtkShadowType
3681 get_shadow_type (GtkToolbar *toolbar)
3682 {
3683   GtkShadowType shadow_type;
3684   
3685   gtk_widget_style_get (GTK_WIDGET (toolbar),
3686                         "shadow-type", &shadow_type,
3687                         NULL);
3688   
3689   return shadow_type;
3690 }
3691
3692 /* GTK+ internal methods */
3693
3694 gint
3695 _gtk_toolbar_get_default_space_size (void)
3696 {
3697   return DEFAULT_SPACE_SIZE;
3698 }
3699
3700 void
3701 _gtk_toolbar_paint_space_line (GtkWidget           *widget,
3702                                GtkToolbar          *toolbar,
3703                                cairo_t             *cr)
3704 {
3705   GtkOrientation orientation;
3706   GtkStyleContext *context;
3707   GtkStateFlags state;
3708   GtkBorder padding;
3709   gint width, height;
3710   const gdouble start_fraction = (SPACE_LINE_START / SPACE_LINE_DIVISION);
3711   const gdouble end_fraction = (SPACE_LINE_END / SPACE_LINE_DIVISION);
3712
3713   g_return_if_fail (GTK_IS_WIDGET (widget));
3714
3715   orientation = toolbar ? toolbar->priv->orientation : GTK_ORIENTATION_HORIZONTAL;
3716
3717   context = gtk_widget_get_style_context (widget);
3718   state = gtk_widget_get_state_flags (widget);
3719   width = gtk_widget_get_allocated_width (widget);
3720   height = gtk_widget_get_allocated_height (widget);
3721   gtk_style_context_get_padding (context, state, &padding);
3722
3723   if (orientation == GTK_ORIENTATION_HORIZONTAL)
3724     {
3725       gboolean wide_separators;
3726       gint     separator_width;
3727
3728       gtk_widget_style_get (widget,
3729                             "wide-separators", &wide_separators,
3730                             "separator-width", &separator_width,
3731                             NULL);
3732
3733       if (wide_separators)
3734         gtk_render_frame (context, cr,
3735                           (width - separator_width) / 2,
3736                           height * start_fraction,
3737                           separator_width,
3738                           height * (end_fraction - start_fraction));
3739       else
3740         gtk_render_line (context, cr,
3741                          (width - padding.left) / 2,
3742                          height * start_fraction,
3743                          (width - padding.left) / 2,
3744                          height * end_fraction);
3745     }
3746   else
3747     {
3748       gboolean wide_separators;
3749       gint     separator_height;
3750
3751       gtk_widget_style_get (widget,
3752                             "wide-separators",  &wide_separators,
3753                             "separator-height", &separator_height,
3754                             NULL);
3755
3756       if (wide_separators)
3757         gtk_render_frame (context, cr,
3758                           width * start_fraction,
3759                           (height - separator_height) / 2,
3760                           width * (end_fraction - start_fraction),
3761                           separator_height);
3762       else
3763         gtk_render_line (context, cr,
3764                          width * start_fraction,
3765                          (height - padding.top) / 2,
3766                          width * end_fraction,
3767                          (height - padding.top) / 2);
3768     }
3769 }
3770
3771 gchar *
3772 _gtk_toolbar_elide_underscores (const gchar *original)
3773 {
3774   gchar *q, *result;
3775   const gchar *p, *end;
3776   gsize len;
3777   gboolean last_underscore;
3778   
3779   if (!original)
3780     return NULL;
3781
3782   len = strlen (original);
3783   q = result = g_malloc (len + 1);
3784   last_underscore = FALSE;
3785   
3786   end = original + len;
3787   for (p = original; p < end; p++)
3788     {
3789       if (!last_underscore && *p == '_')
3790         last_underscore = TRUE;
3791       else
3792         {
3793           last_underscore = FALSE;
3794           if (original + 2 <= p && p + 1 <= end && 
3795               p[-2] == '(' && p[-1] == '_' && p[0] != '_' && p[1] == ')')
3796             {
3797               q--;
3798               *q = '\0';
3799               p++;
3800             }
3801           else
3802             *q++ = *p;
3803         }
3804     }
3805
3806   if (last_underscore)
3807     *q++ = '_';
3808   
3809   *q = '\0';
3810   
3811   return result;
3812 }
3813
3814 static GtkIconSize
3815 toolbar_get_icon_size (GtkToolShell *shell)
3816 {
3817   GtkToolbar *toolbar = GTK_TOOLBAR (shell);
3818   GtkToolbarPrivate *priv = toolbar->priv;
3819
3820   return priv->icon_size;
3821 }
3822
3823 static GtkOrientation
3824 toolbar_get_orientation (GtkToolShell *shell)
3825 {
3826   GtkToolbar *toolbar = GTK_TOOLBAR (shell);
3827   GtkToolbarPrivate *priv = toolbar->priv;
3828
3829   return priv->orientation;
3830 }
3831
3832 static GtkToolbarStyle
3833 toolbar_get_style (GtkToolShell *shell)
3834 {
3835   GtkToolbar *toolbar = GTK_TOOLBAR (shell);
3836   GtkToolbarPrivate *priv = toolbar->priv;
3837
3838   return priv->style;
3839 }
3840
3841 static GtkReliefStyle
3842 toolbar_get_relief_style (GtkToolShell *shell)
3843 {
3844   return get_button_relief (GTK_TOOLBAR (shell));
3845 }
3846
3847 static void
3848 toolbar_rebuild_menu (GtkToolShell *shell)
3849 {
3850   GtkToolbar *toolbar = GTK_TOOLBAR (shell);
3851   GtkToolbarPrivate *priv = toolbar->priv;
3852   GList *list;
3853
3854   priv->need_rebuild = TRUE;
3855
3856   for (list = priv->content; list != NULL; list = list->next)
3857     {
3858       ToolbarContent *content = list->data;
3859
3860       toolbar_content_set_unknown_menu_status (content);
3861     }
3862   
3863   gtk_widget_queue_resize (GTK_WIDGET (shell));
3864 }
3865
3866 typedef struct _CountingData CountingData;
3867 struct _CountingData {
3868   GtkWidget *widget;
3869   gboolean found;
3870   guint before;
3871   guint after;
3872 };
3873
3874 static void
3875 count_widget_position (GtkWidget *widget,
3876                        gpointer   data)
3877 {
3878   CountingData *count = data;
3879
3880   if (!gtk_widget_get_visible (widget))
3881     return;
3882
3883   if (count->widget == widget)
3884     count->found = TRUE;
3885   else if (count->found)
3886     count->after++;
3887   else
3888     count->before++;
3889 }
3890
3891 static guint
3892 gtk_toolbar_get_visible_position (GtkToolbar *toolbar,
3893                                   GtkWidget  *child)
3894 {
3895   CountingData count = { child, FALSE, 0, 0 };
3896
3897   /* foreach iterates in visible order */
3898   gtk_container_forall (GTK_CONTAINER (toolbar),
3899                         count_widget_position,
3900                         &count);
3901
3902   g_assert (count.found);
3903
3904   if (toolbar->priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
3905       gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_RTL)
3906     return count.after;
3907
3908   return count.before;
3909 }
3910
3911 static void
3912 add_widget_to_path (gpointer data,
3913                     gpointer user_data)
3914 {
3915   GtkWidget *widget = data;
3916   GtkWidgetPath *path = user_data;
3917
3918   if (gtk_widget_get_visible (widget))
3919     gtk_widget_path_append_for_widget (path, widget);
3920 }
3921
3922 static GtkWidgetPath *
3923 gtk_toolbar_get_path_for_child (GtkContainer *container,
3924                                 GtkWidget    *child)
3925 {
3926   GtkWidgetPath *path;
3927   GtkToolbar *toolbar;
3928   GtkToolbarPrivate *priv;
3929   gint vis_index;
3930
3931   toolbar = GTK_TOOLBAR (container);
3932   priv = toolbar->priv;
3933
3934   if (priv->sibling_path == NULL)
3935     {
3936       GList *children;
3937
3938       /* build a path for all the visible children;
3939        * get_children works in visible order
3940        */
3941       priv->sibling_path = gtk_widget_path_new ();
3942       children = gtk_container_get_children (container);
3943
3944       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
3945           gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_RTL)
3946         children = g_list_reverse (children);
3947
3948       g_list_foreach (children, add_widget_to_path, priv->sibling_path);
3949       g_list_free (children);
3950     }
3951
3952   path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (container)));
3953   if (gtk_widget_get_visible (child))
3954     {
3955       vis_index = gtk_toolbar_get_visible_position (toolbar, child);
3956
3957       if (vis_index < gtk_widget_path_length (priv->sibling_path))
3958         gtk_widget_path_append_with_siblings (path,
3959                                               priv->sibling_path,
3960                                               vis_index);
3961       else
3962         gtk_widget_path_append_for_widget (path, child);
3963     }
3964   else
3965     gtk_widget_path_append_for_widget (path, child);
3966
3967   return path;
3968 }
3969
3970 static void
3971 gtk_toolbar_invalidate_order (GtkToolbar *toolbar)
3972 {
3973   GtkToolbarPrivate *priv = toolbar->priv;
3974
3975   if (priv->sibling_path != NULL)
3976     {
3977       gtk_widget_path_unref (priv->sibling_path);
3978       priv->sibling_path = NULL;
3979
3980       gtk_container_foreach (GTK_CONTAINER (toolbar),
3981                              (GtkCallback) gtk_widget_reset_style,
3982                              NULL);
3983     }
3984 }
3985