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