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