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