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