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