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