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