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