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