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