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