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