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