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