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