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