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