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