1 /* GtkToolPalette -- A tool palette with categories and DnD support
2 * Copyright (C) 2008 Openismus GmbH
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "gtktoolpaletteprivate.h"
29 #define P_(msgid) (msgid)
31 #define ANIMATION_TIMEOUT 50
32 #define ANIMATION_DURATION (ANIMATION_TIMEOUT * 4)
33 #define DEFAULT_ANIMATION_STATE TRUE
34 #define DEFAULT_EXPANDER_SIZE 16
35 #define DEFAULT_HEADER_SPACING 2
37 #define DEFAULT_NAME NULL
38 #define DEFAULT_COLLAPSED FALSE
39 #define DEFAULT_ELLIPSIZE PANGO_ELLIPSIZE_NONE
42 * SECTION:GtkToolItemGroup
43 * @short_description: A sub container used in a tool palette
44 * @include: gtktoolitemgroup.h
46 * An #GtkToolItemGroup is used together with #GtkToolPalette to add #GtkToolItem<!-- -->s to a palette like container
47 * with different categories and drag and drop support.
64 CHILD_PROP_HOMOGENEOUS,
71 typedef struct _GtkToolItemGroupChild GtkToolItemGroupChild;
73 struct _GtkToolItemGroupPrivate
80 gint64 animation_start;
81 GSource *animation_timeout;
82 GtkExpanderStyle expander_style;
85 PangoEllipsizeMode ellipsize;
90 GtkSettings *settings;
91 gulong settings_connection;
96 struct _GtkToolItemGroupChild
100 guint homogeneous : 1;
106 static void gtk_tool_item_group_tool_shell_init (GtkToolShellIface *iface);
108 G_DEFINE_TYPE_WITH_CODE (GtkToolItemGroup, gtk_tool_item_group, GTK_TYPE_CONTAINER,
109 G_IMPLEMENT_INTERFACE (GTK_TYPE_TOOL_SHELL, gtk_tool_item_group_tool_shell_init));
112 gtk_tool_item_group_get_alignment (GtkToolItemGroup *group)
114 return gtk_bin_get_child (GTK_BIN (group->priv->header));
118 gtk_tool_item_group_get_label (GtkToolItemGroup *group)
120 GtkWidget *alignment = gtk_tool_item_group_get_alignment (group);
121 return gtk_bin_get_child (GTK_BIN (alignment));
124 static GtkOrientation
125 gtk_tool_item_group_get_orientation (GtkToolShell *shell)
127 GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
129 if (GTK_IS_TOOL_PALETTE (parent))
130 return gtk_orientable_get_orientation (GTK_ORIENTABLE (parent));
132 return GTK_ORIENTATION_VERTICAL;
135 static GtkToolbarStyle
136 gtk_tool_item_group_get_style (GtkToolShell *shell)
138 GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
140 if (GTK_IS_TOOL_PALETTE (parent))
141 return gtk_tool_palette_get_style (GTK_TOOL_PALETTE (parent));
143 return GTK_TOOLBAR_ICONS;
147 gtk_tool_item_group_get_icon_size (GtkToolShell *shell)
149 GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
151 if (GTK_IS_TOOL_PALETTE (parent))
152 return gtk_tool_palette_get_icon_size (GTK_TOOL_PALETTE (parent));
154 return GTK_ICON_SIZE_SMALL_TOOLBAR;
157 static PangoEllipsizeMode
158 gtk_tool_item_group_get_ellipsize_mode (GtkToolShell *shell)
160 return GTK_TOOL_ITEM_GROUP (shell)->priv->ellipsize;
164 gtk_tool_item_group_get_text_alignment (GtkToolShell *shell)
166 if (GTK_TOOLBAR_TEXT == gtk_tool_item_group_get_style (shell) ||
167 GTK_TOOLBAR_BOTH_HORIZ == gtk_tool_item_group_get_style (shell))
173 static GtkOrientation
174 gtk_tool_item_group_get_text_orientation (GtkToolShell *shell)
176 GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
178 if (GTK_IS_TOOL_PALETTE (parent))
180 GtkOrientation orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (parent));
181 if (GTK_ORIENTATION_HORIZONTAL == orientation &&
182 (GTK_TOOLBAR_TEXT == gtk_tool_item_group_get_style (shell)/* ||
183 GTK_TOOLBAR_BOTH_HORIZ == gtk_tool_item_group_get_style (shell)*/))
184 return GTK_ORIENTATION_VERTICAL;
187 return GTK_ORIENTATION_HORIZONTAL;
190 static GtkSizeGroup *
191 gtk_tool_item_group_get_text_size_group (GtkToolShell *shell)
193 GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
195 if (GTK_IS_TOOL_PALETTE (parent))
196 return _gtk_tool_palette_get_size_group (GTK_TOOL_PALETTE (parent));
202 animation_change_notify (GtkToolItemGroup *group)
204 GtkSettings *settings = group->priv->settings;
208 g_object_get (settings,
209 "gtk-enable-animations", &animation,
212 animation = DEFAULT_ANIMATION_STATE;
214 group->priv->animation = animation;
218 gtk_tool_item_group_settings_change_notify (GtkSettings *settings,
219 const GParamSpec *pspec,
220 GtkToolItemGroup *group)
222 if (! strcmp (pspec->name, "gtk-enable-animations"))
224 animation_change_notify (group);
229 gtk_tool_item_group_screen_changed (GtkWidget *widget,
230 GdkScreen *previous_screen)
232 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget);
233 GtkToolItemGroupPrivate* priv = group->priv;
234 GtkSettings *old_settings = priv->settings;
235 GtkSettings *settings;
237 if (gtk_widget_has_screen (GTK_WIDGET (group)))
238 settings = gtk_widget_get_settings (GTK_WIDGET (group));
242 if (settings == old_settings)
247 g_signal_handler_disconnect (old_settings, priv->settings_connection);
248 g_object_unref (old_settings);
253 priv->settings_connection =
254 g_signal_connect (settings, "notify",
255 G_CALLBACK (gtk_tool_item_group_settings_change_notify),
257 priv->settings = g_object_ref (settings);
260 priv->settings = NULL;
262 animation_change_notify (group);
266 gtk_tool_item_group_tool_shell_init (GtkToolShellIface *iface)
268 iface->get_icon_size = gtk_tool_item_group_get_icon_size;
269 iface->get_orientation = gtk_tool_item_group_get_orientation;
270 iface->get_style = gtk_tool_item_group_get_style;
271 iface->get_text_alignment = gtk_tool_item_group_get_text_alignment;
272 iface->get_text_orientation = gtk_tool_item_group_get_text_orientation;
273 iface->get_text_size_group = gtk_tool_item_group_get_text_size_group;
274 iface->get_ellipsize_mode = gtk_tool_item_group_get_ellipsize_mode;
278 gtk_tool_item_group_header_expose_event_cb (GtkWidget *widget,
279 GdkEventExpose *event,
282 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data);
283 GtkExpanderStyle expander_style;
284 GtkOrientation orientation;
286 GtkTextDirection direction;
288 orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
289 expander_style = group->priv->expander_style;
290 direction = gtk_widget_get_direction (widget);
292 if (GTK_ORIENTATION_VERTICAL == orientation)
294 if (GTK_TEXT_DIR_RTL == direction)
295 x = widget->allocation.x + widget->allocation.width - group->priv->expander_size / 2;
297 x = widget->allocation.x + group->priv->expander_size / 2;
298 y = widget->allocation.y + widget->allocation.height / 2;
302 x = widget->allocation.x + widget->allocation.width / 2;
303 y = widget->allocation.y + group->priv->expander_size / 2;
305 /* Unfortunatly gtk_paint_expander() doesn't support rotated drawing
306 * modes. Luckily the following shady arithmetics produce the desired
308 expander_style = GTK_EXPANDER_EXPANDED - expander_style; /* XXX */
311 gtk_paint_expander (widget->style, widget->window,
312 group->priv->header->state,
313 &event->area, GTK_WIDGET (group),
314 "tool-palette-header", x, y,
321 gtk_tool_item_group_header_size_request_cb (GtkWidget *widget G_GNUC_UNUSED,
322 GtkRequisition *requisition,
325 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data);
326 requisition->height = MAX (requisition->height, group->priv->expander_size);
330 gtk_tool_item_group_header_clicked_cb (GtkButton *button G_GNUC_UNUSED,
333 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data);
334 GtkWidget *parent = gtk_widget_get_parent (data);
336 if (group->priv->collapsed ||
337 !GTK_IS_TOOL_PALETTE (parent) ||
338 !gtk_tool_palette_get_exclusive (GTK_TOOL_PALETTE (parent), data))
339 gtk_tool_item_group_set_collapsed (group, !group->priv->collapsed);
343 gtk_tool_item_group_header_adjust_style (GtkToolItemGroup *group)
345 GtkWidget *alignment = gtk_tool_item_group_get_alignment (group);
346 GtkWidget *label = gtk_bin_get_child (GTK_BIN (alignment));
347 GtkWidget *widget = GTK_WIDGET (group);
349 GtkTextDirection direction = gtk_widget_get_direction (widget);
351 gtk_widget_style_get (widget,
352 "header-spacing", &group->priv->header_spacing,
353 "expander-size", &group->priv->expander_size,
356 switch (gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)))
358 case GTK_ORIENTATION_HORIZONTAL:
359 dy = group->priv->header_spacing + group->priv->expander_size;
360 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_NONE);
361 if (GTK_TEXT_DIR_RTL == direction)
362 gtk_label_set_angle (GTK_LABEL (label), -90);
364 gtk_label_set_angle (GTK_LABEL (label), 90);
367 case GTK_ORIENTATION_VERTICAL:
368 dx = group->priv->header_spacing + group->priv->expander_size;
369 gtk_label_set_ellipsize (GTK_LABEL (label), group->priv->ellipsize);
370 gtk_label_set_angle (GTK_LABEL (label), 0);
374 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), dy, 0, dx, 0);
378 gtk_tool_item_group_init (GtkToolItemGroup *group)
380 GtkWidget *alignment;
383 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (group), FALSE);
385 group->priv = G_TYPE_INSTANCE_GET_PRIVATE (group,
386 GTK_TYPE_TOOL_ITEM_GROUP,
387 GtkToolItemGroupPrivate);
389 group->priv->children = NULL;
390 group->priv->header_spacing = DEFAULT_HEADER_SPACING;
391 group->priv->expander_size = DEFAULT_EXPANDER_SIZE;
392 group->priv->expander_style = GTK_EXPANDER_EXPANDED;
394 label = gtk_label_new (NULL);
395 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
396 alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
397 gtk_container_add (GTK_CONTAINER (alignment), label);
398 gtk_widget_show_all (alignment);
400 gtk_widget_push_composite_child ();
401 group->priv->header = gtk_button_new ();
402 gtk_widget_set_composite_name (group->priv->header, "header");
403 gtk_widget_pop_composite_child ();
405 g_object_ref_sink (group->priv->header);
406 gtk_button_set_focus_on_click (GTK_BUTTON (group->priv->header), FALSE);
407 gtk_container_add (GTK_CONTAINER (group->priv->header), alignment);
408 gtk_widget_set_parent (group->priv->header, GTK_WIDGET (group));
410 gtk_tool_item_group_header_adjust_style (group);
412 g_signal_connect_after (alignment, "expose-event",
413 G_CALLBACK (gtk_tool_item_group_header_expose_event_cb),
415 g_signal_connect_after (alignment, "size-request",
416 G_CALLBACK (gtk_tool_item_group_header_size_request_cb),
419 g_signal_connect (group->priv->header, "clicked",
420 G_CALLBACK (gtk_tool_item_group_header_clicked_cb),
425 gtk_tool_item_group_set_property (GObject *object,
430 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object);
435 gtk_tool_item_group_set_name (group, g_value_get_string (value));
439 gtk_tool_item_group_set_collapsed (group, g_value_get_boolean (value));
443 gtk_tool_item_group_set_ellipsize (group, g_value_get_enum (value));
446 gtk_tool_item_group_set_header_relief (group, g_value_get_enum(value));
449 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
455 gtk_tool_item_group_get_property (GObject *object,
460 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object);
465 g_value_set_string (value, gtk_tool_item_group_get_name (group));
469 g_value_set_boolean (value, gtk_tool_item_group_get_collapsed (group));
473 g_value_set_enum (value, gtk_tool_item_group_get_ellipsize (group));
477 g_value_set_enum (value, gtk_tool_item_group_get_header_relief (group));
481 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
487 gtk_tool_item_group_finalize (GObject *object)
489 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object);
491 if (group->priv->children)
493 g_list_free (group->priv->children);
494 group->priv->children = NULL;
497 G_OBJECT_CLASS (gtk_tool_item_group_parent_class)->finalize (object);
501 gtk_tool_item_group_dispose (GObject *object)
503 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object);
505 if (group->priv->toplevel)
507 /* disconnect focus tracking handler */
508 g_signal_handler_disconnect (group->priv->toplevel,
509 group->priv->focus_set_id);
511 group->priv->focus_set_id = 0;
512 group->priv->toplevel = NULL;
515 G_OBJECT_CLASS (gtk_tool_item_group_parent_class)->dispose (object);
519 gtk_tool_item_group_get_item_size (GtkToolItemGroup *group,
520 GtkRequisition *item_size,
521 gboolean homogeneous_only,
522 gint *requested_rows)
524 GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (group));
526 if (GTK_IS_TOOL_PALETTE (parent))
527 _gtk_tool_palette_get_item_size (GTK_TOOL_PALETTE (parent), item_size, homogeneous_only, requested_rows);
529 _gtk_tool_item_group_item_size_request (group, item_size, homogeneous_only, requested_rows);
533 gtk_tool_item_group_size_request (GtkWidget *widget,
534 GtkRequisition *requisition)
536 const gint border_width = GTK_CONTAINER (widget)->border_width;
537 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget);
538 GtkOrientation orientation;
539 GtkRequisition item_size;
542 if (group->priv->children && gtk_tool_item_group_get_name (group))
544 gtk_widget_size_request (group->priv->header, requisition);
545 gtk_widget_show (group->priv->header);
549 requisition->width = requisition->height = 0;
550 gtk_widget_hide (group->priv->header);
553 gtk_tool_item_group_get_item_size (group, &item_size, FALSE, &requested_rows);
555 orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
557 if (GTK_ORIENTATION_VERTICAL == orientation)
558 requisition->width = MAX (requisition->width, item_size.width);
560 requisition->height = MAX (requisition->height, item_size.height * requested_rows);
562 requisition->width += border_width * 2;
563 requisition->height += border_width * 2;
567 gtk_tool_item_group_is_item_visible (GtkToolItemGroup *group,
568 GtkToolItemGroupChild *child)
570 GtkToolbarStyle style;
571 GtkOrientation orientation;
573 orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
574 style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group));
576 /* horizontal tool palettes with text style support only homogeneous items */
577 if (!child->homogeneous &&
578 GTK_ORIENTATION_HORIZONTAL == orientation &&
579 GTK_TOOLBAR_TEXT == style)
583 (GTK_WIDGET_VISIBLE (child->item)) &&
584 (GTK_ORIENTATION_VERTICAL == orientation ?
585 gtk_tool_item_get_visible_vertical (child->item) :
586 gtk_tool_item_get_visible_horizontal (child->item));
589 static inline unsigned
593 return (x + y - 1) / y;
597 gtk_tool_item_group_real_size_query (GtkWidget *widget,
598 GtkAllocation *allocation,
599 GtkRequisition *inquery)
601 const gint border_width = GTK_CONTAINER (widget)->border_width;
602 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget);
604 GtkRequisition item_size;
605 GtkAllocation item_area;
607 GtkOrientation orientation;
608 GtkToolbarStyle style;
612 orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
613 style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group));
615 /* figure out the size of homogeneous items */
616 gtk_tool_item_group_get_item_size (group, &item_size, TRUE, &min_rows);
618 if (GTK_ORIENTATION_VERTICAL == orientation)
619 item_size.width = MIN (item_size.width, allocation->width);
621 item_size.height = MIN (item_size.height, allocation->height);
624 item_area.height = 0;
626 /* figure out the required columns (n_columns) and rows (n_rows) to place all items */
627 if (!group->priv->collapsed || !group->priv->animation || group->priv->animation_timeout)
633 if (GTK_ORIENTATION_VERTICAL == orientation)
635 gboolean new_row = FALSE;
639 item_area.width = allocation->width - 2 * border_width;
640 n_columns = MAX (item_area.width / item_size.width, 1);
642 /* calculate required rows for n_columns columns */
643 for (it = group->priv->children; it != NULL; it = it->next)
645 GtkToolItemGroupChild *child = it->data;
647 if (!gtk_tool_item_group_is_item_visible (group, child))
650 if (new_row || child->new_row)
660 if (child->homogeneous)
663 if (col >= n_columns)
668 GtkRequisition req = {0, 0};
671 gtk_widget_size_request (GTK_WIDGET (child->item), &req);
673 width = udiv (req.width, item_size.width);
681 if (col >= n_columns)
689 guint *row_min_width;
691 gboolean new_row = TRUE;
692 guint col = 0, min_col, max_col = 0, all_items = 0;
695 item_area.height = allocation->height - 2 * border_width;
696 n_rows = MAX (item_area.height / item_size.height, min_rows);
698 row_min_width = g_new0 (guint, n_rows);
700 /* calculate minimal and maximal required cols and minimal required rows */
701 for (it = group->priv->children; it != NULL; it = it->next)
703 GtkToolItemGroupChild *child = it->data;
705 if (!gtk_tool_item_group_is_item_visible (group, child))
708 if (new_row || child->new_row)
713 row_min_width[row] = 1;
719 if (child->homogeneous)
726 GtkRequisition req = {0, 0};
729 gtk_widget_size_request (GTK_WIDGET (child->item), &req);
731 width = udiv (req.width, item_size.width);
736 row_min_width[row] = MAX (row_min_width[row], width);
739 max_col = MAX (max_col, col);
742 /* calculate minimal required cols */
743 min_col = udiv (all_items, n_rows);
745 for (i = 0; i <= row; i++)
747 min_col = MAX (min_col, row_min_width[i]);
750 /* simple linear search for minimal required columns for the given maximal number of rows (n_rows) */
751 for (n_columns = min_col; n_columns < max_col; n_columns ++)
755 /* calculate required rows for n_columns columns */
756 for (it = group->priv->children; it != NULL; it = it->next)
758 GtkToolItemGroupChild *child = it->data;
760 if (!gtk_tool_item_group_is_item_visible (group, child))
763 if (new_row || child->new_row)
773 if (child->homogeneous)
776 if (col >= n_columns)
781 GtkRequisition req = {0, 0};
784 gtk_widget_size_request (GTK_WIDGET (child->item), &req);
786 width = udiv (req.width, item_size.width);
794 if (col >= n_columns)
804 item_area.width = item_size.width * n_columns;
805 item_area.height = item_size.height * n_rows;
811 /* figure out header widget size */
812 if (GTK_WIDGET_VISIBLE (group->priv->header))
814 GtkRequisition child_requisition;
816 gtk_widget_size_request (group->priv->header, &child_requisition);
818 if (GTK_ORIENTATION_VERTICAL == orientation)
819 inquery->height += child_requisition.height;
821 inquery->width += child_requisition.width;
824 /* report effective widget size */
825 inquery->width += item_area.width + 2 * border_width;
826 inquery->height += item_area.height + 2 * border_width;
830 gtk_tool_item_group_real_size_allocate (GtkWidget *widget,
831 GtkAllocation *allocation)
833 const gint border_width = GTK_CONTAINER (widget)->border_width;
834 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget);
835 GtkRequisition child_requisition;
836 GtkAllocation child_allocation;
838 GtkRequisition item_size;
839 GtkAllocation item_area;
841 GtkOrientation orientation;
842 GtkToolbarStyle style;
846 gint n_columns, n_rows = 1;
849 GtkTextDirection direction = gtk_widget_get_direction (widget);
851 orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
852 style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group));
855 GTK_WIDGET_CLASS (gtk_tool_item_group_parent_class)->size_allocate (widget, allocation);
857 child_allocation.x = border_width;
858 child_allocation.y = border_width;
860 /* place the header widget */
861 if (GTK_WIDGET_VISIBLE (group->priv->header))
863 gtk_widget_size_request (group->priv->header, &child_requisition);
865 if (GTK_ORIENTATION_VERTICAL == orientation)
867 child_allocation.width = allocation->width;
868 child_allocation.height = child_requisition.height;
872 child_allocation.width = child_requisition.width;
873 child_allocation.height = allocation->height;
875 if (GTK_TEXT_DIR_RTL == direction)
876 child_allocation.x = allocation->width - border_width - child_allocation.width;
879 gtk_widget_size_allocate (group->priv->header, &child_allocation);
881 if (GTK_ORIENTATION_VERTICAL == orientation)
882 child_allocation.y += child_allocation.height;
883 else if (GTK_TEXT_DIR_RTL != direction)
884 child_allocation.x += child_allocation.width;
886 child_allocation.x = border_width;
889 child_requisition.width = child_requisition.height = 0;
891 /* figure out the size of homogeneous items */
892 gtk_tool_item_group_get_item_size (group, &item_size, TRUE, &min_rows);
894 /* figure out the available columns and size of item_area */
895 if (GTK_ORIENTATION_VERTICAL == orientation)
897 item_size.width = MIN (item_size.width, allocation->width);
899 item_area.width = allocation->width - 2 * border_width;
900 item_area.height = allocation->height - 2 * border_width - child_requisition.height;
902 n_columns = MAX (item_area.width / item_size.width, 1);
904 item_size.width = item_area.width / n_columns;
908 item_size.height = MIN (item_size.height, allocation->height);
910 item_area.width = allocation->width - 2 * border_width - child_requisition.width;
911 item_area.height = allocation->height - 2 * border_width;
913 n_columns = MAX (item_area.width / item_size.width, 1);
914 n_rows = MAX (item_area.height / item_size.height, min_rows);
916 item_size.height = item_area.height / n_rows;
919 item_area.x = child_allocation.x;
920 item_area.y = child_allocation.y;
922 /* when expanded or in transition, place the tool items in a grid like layout */
923 if (!group->priv->collapsed || !group->priv->animation || group->priv->animation_timeout)
925 gint col = 0, row = 0;
927 for (it = group->priv->children; it != NULL; it = it->next)
929 GtkToolItemGroupChild *child = it->data;
932 if (!gtk_tool_item_group_is_item_visible (group, child))
934 gtk_widget_set_child_visible (GTK_WIDGET (child->item), FALSE);
939 /* for non homogeneous widgets request the required size */
940 child_requisition.width = 0;
942 if (!child->homogeneous)
944 gtk_widget_size_request (GTK_WIDGET (child->item), &child_requisition);
945 child_requisition.width = MIN (child_requisition.width, item_area.width);
948 /* select next row if at end of row */
949 if (col > 0 && (child->new_row || (col * item_size.width) + MAX (child_requisition.width, item_size.width) > item_area.width))
953 child_allocation.y += child_allocation.height;
958 /* calculate the position and size of the item */
959 if (!child->homogeneous)
965 col_width = udiv (child_requisition.width, item_size.width);
967 col_width = n_columns - col;
969 width = col_width * item_size.width;
971 if (GTK_TEXT_DIR_RTL == direction)
972 col_child = (n_columns - col - col_width);
976 child_allocation.x = item_area.x + col_child * item_size.width;
977 child_allocation.width = width;
982 (item_area.x + col_child * item_size.width +
983 (width - child_requisition.width) / 2);
984 child_allocation.width = child_requisition.width;
991 if (GTK_TEXT_DIR_RTL == direction)
992 col_child = (n_columns - col - 1);
994 child_allocation.x = item_area.x + col_child * item_size.width;
995 child_allocation.width = item_size.width;
1000 child_allocation.height = item_size.height;
1002 gtk_widget_size_allocate (GTK_WIDGET (child->item), &child_allocation);
1003 gtk_widget_set_child_visible (GTK_WIDGET (child->item), TRUE);
1006 child_allocation.y += item_size.height;
1009 /* or just hide all items, when collapsed */
1013 for (it = group->priv->children; it != NULL; it = it->next)
1015 GtkToolItemGroupChild *child = it->data;
1017 gtk_widget_set_child_visible (GTK_WIDGET (child->item), FALSE);
1023 gtk_tool_item_group_size_allocate (GtkWidget *widget,
1024 GtkAllocation *allocation)
1026 gtk_tool_item_group_real_size_allocate (widget, allocation);
1028 if (GTK_WIDGET_MAPPED (widget))
1029 gdk_window_invalidate_rect (widget->window, NULL, FALSE);
1033 gtk_tool_item_group_set_focus_cb (GtkWidget *window G_GNUC_UNUSED,
1037 GtkAdjustment *adjustment;
1040 /* Find this group's parent widget in the focused widget's anchestry. */
1041 for (p = widget; p; p = gtk_widget_get_parent (p))
1044 p = gtk_widget_get_parent (p);
1048 if (GTK_IS_TOOL_PALETTE (p))
1050 /* Check that the focused widgets is fully visible within
1051 * the group's parent widget and make it visible otherwise. */
1053 adjustment = gtk_tool_palette_get_hadjustment (GTK_TOOL_PALETTE (p));
1054 adjustment = gtk_tool_palette_get_vadjustment (GTK_TOOL_PALETTE (p));
1060 /* Handle vertical adjustment. */
1061 if (gtk_widget_translate_coordinates
1062 (widget, p, 0, 0, NULL, &y) && y < 0)
1064 y += adjustment->value;
1065 gtk_adjustment_clamp_page (adjustment, y, y + widget->allocation.height);
1067 else if (gtk_widget_translate_coordinates
1068 (widget, p, 0, widget->allocation.height, NULL, &y) &&
1069 y > p->allocation.height)
1071 y += adjustment->value;
1072 gtk_adjustment_clamp_page (adjustment, y - widget->allocation.height, y);
1076 adjustment = gtk_tool_palette_get_hadjustment (GTK_TOOL_PALETTE (p));
1082 /* Handle horizontal adjustment. */
1083 if (gtk_widget_translate_coordinates
1084 (widget, p, 0, 0, &x, NULL) && x < 0)
1086 x += adjustment->value;
1087 gtk_adjustment_clamp_page (adjustment, x, x + widget->allocation.width);
1089 else if (gtk_widget_translate_coordinates
1090 (widget, p, widget->allocation.width, 0, &x, NULL) &&
1091 x > p->allocation.width)
1093 x += adjustment->value;
1094 gtk_adjustment_clamp_page (adjustment, x - widget->allocation.width, x);
1103 gtk_tool_item_group_set_toplevel_window (GtkToolItemGroup *group,
1104 GtkWidget *toplevel)
1106 if (toplevel != group->priv->toplevel)
1108 if (group->priv->toplevel)
1110 /* Disconnect focus tracking handler. */
1111 g_signal_handler_disconnect (group->priv->toplevel,
1112 group->priv->focus_set_id);
1114 group->priv->focus_set_id = 0;
1115 group->priv->toplevel = NULL;
1120 /* Install focus tracking handler. We connect to the window's
1121 * set-focus signal instead of connecting to the focus signal of
1124 * 1) Reduce the number of signal handlers used.
1125 * 2) Avoid special handling for group headers.
1126 * 3) Catch focus grabs not only for direct children,
1127 * but also for nested widgets.
1129 group->priv->focus_set_id =
1130 g_signal_connect (toplevel, "set-focus",
1131 G_CALLBACK (gtk_tool_item_group_set_focus_cb),
1134 group->priv->toplevel = toplevel;
1140 gtk_tool_item_group_realize (GtkWidget *widget)
1142 GtkWidget *toplevel_window;
1143 const gint border_width = GTK_CONTAINER (widget)->border_width;
1144 gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1145 GdkWindowAttr attributes;
1146 GdkDisplay *display;
1148 attributes.window_type = GDK_WINDOW_CHILD;
1149 attributes.x = widget->allocation.x + border_width;
1150 attributes.y = widget->allocation.y + border_width;
1151 attributes.width = widget->allocation.width - border_width * 2;
1152 attributes.height = widget->allocation.height - border_width * 2;
1153 attributes.wclass = GDK_INPUT_OUTPUT;
1154 attributes.visual = gtk_widget_get_visual (widget);
1155 attributes.colormap = gtk_widget_get_colormap (widget);
1156 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK
1157 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1158 | GDK_BUTTON_MOTION_MASK;
1160 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1161 &attributes, attributes_mask);
1163 display = gdk_drawable_get_display (widget->window);
1165 if (gdk_display_supports_composite (display))
1166 gdk_window_set_composited (widget->window, TRUE);
1168 gdk_window_set_user_data (widget->window, widget);
1169 widget->style = gtk_style_attach (widget->style, widget->window);
1170 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1171 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1173 gtk_container_forall (GTK_CONTAINER (widget),
1174 (GtkCallback) gtk_widget_set_parent_window,
1177 gtk_widget_queue_resize_no_redraw (widget);
1179 toplevel_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
1180 gtk_tool_item_group_set_toplevel_window (GTK_TOOL_ITEM_GROUP (widget),
1185 gtk_tool_item_group_unrealize (GtkWidget *widget)
1187 gtk_tool_item_group_set_toplevel_window (GTK_TOOL_ITEM_GROUP (widget), NULL);
1188 GTK_WIDGET_CLASS (gtk_tool_item_group_parent_class)->unrealize (widget);
1192 gtk_tool_item_group_style_set (GtkWidget *widget,
1193 GtkStyle *previous_style)
1195 gtk_tool_item_group_header_adjust_style (GTK_TOOL_ITEM_GROUP (widget));
1196 GTK_WIDGET_CLASS (gtk_tool_item_group_parent_class)->style_set (widget, previous_style);
1200 gtk_tool_item_group_add (GtkContainer *container,
1203 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (container));
1204 g_return_if_fail (GTK_IS_TOOL_ITEM (widget));
1206 gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (container),
1207 GTK_TOOL_ITEM (widget), -1);
1211 gtk_tool_item_group_remove (GtkContainer *container,
1214 GtkToolItemGroup *group;
1217 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (container));
1218 group = GTK_TOOL_ITEM_GROUP (container);
1220 for (it = group->priv->children; it != NULL; it = it->next)
1222 GtkToolItemGroupChild *child_info = it->data;
1224 if ((GtkWidget *)child_info->item == child)
1226 g_object_unref (child);
1227 gtk_widget_unparent (child);
1229 g_free (child_info);
1230 group->priv->children = g_list_delete_link (group->priv->children, it);
1232 gtk_widget_queue_resize (GTK_WIDGET (container));
1239 gtk_tool_item_group_forall (GtkContainer *container,
1241 GtkCallback callback,
1242 gpointer callback_data)
1244 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (container);
1247 if (internals && group->priv->header)
1248 callback (group->priv->header, callback_data);
1250 children = group->priv->children;
1253 GtkToolItemGroupChild *child = children->data;
1254 children = children->next; /* store pointer before call to callback
1255 because the child pointer is invalid if the
1256 child->item is removed from the item group
1259 callback (GTK_WIDGET (child->item), callback_data);
1264 gtk_tool_item_group_child_type (GtkContainer *container G_GNUC_UNUSED)
1266 return GTK_TYPE_TOOL_ITEM;
1269 static GtkToolItemGroupChild *
1270 gtk_tool_item_group_get_child (GtkToolItemGroup *group,
1278 g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL);
1279 g_return_val_if_fail (GTK_IS_TOOL_ITEM (item), NULL);
1281 for (it = group->priv->children, i = 0; it != NULL; it = it->next, ++i)
1283 GtkToolItemGroupChild *child = it->data;
1285 if (child->item == item)
1301 gtk_tool_item_group_get_item_packing (GtkToolItemGroup *group,
1303 gboolean *homogeneous,
1308 GtkToolItemGroupChild *child;
1310 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1311 g_return_if_fail (GTK_IS_TOOL_ITEM (item));
1313 child = gtk_tool_item_group_get_child (group, item, NULL, NULL);
1318 *expand = child->expand;
1321 *homogeneous = child->homogeneous;
1324 *fill = child->fill;
1327 *new_row = child->new_row;
1331 gtk_tool_item_group_set_item_packing (GtkToolItemGroup *group,
1333 gboolean homogeneous,
1338 GtkToolItemGroupChild *child;
1339 gboolean changed = FALSE;
1341 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1342 g_return_if_fail (GTK_IS_TOOL_ITEM (item));
1344 child = gtk_tool_item_group_get_child (group, item, NULL, NULL);
1348 gtk_widget_freeze_child_notify (GTK_WIDGET (item));
1350 if (child->homogeneous != homogeneous)
1352 child->homogeneous = homogeneous;
1354 gtk_widget_child_notify (GTK_WIDGET (item), "homogeneous");
1356 if (child->expand != expand)
1358 child->expand = expand;
1360 gtk_widget_child_notify (GTK_WIDGET (item), "expand");
1362 if (child->fill != fill)
1366 gtk_widget_child_notify (GTK_WIDGET (item), "fill");
1368 if (child->new_row != new_row)
1370 child->new_row = new_row;
1372 gtk_widget_child_notify (GTK_WIDGET (item), "new-row");
1375 gtk_widget_thaw_child_notify (GTK_WIDGET (item));
1377 if (changed && GTK_WIDGET_VISIBLE (group) && GTK_WIDGET_VISIBLE (item))
1378 gtk_widget_queue_resize (GTK_WIDGET (group));
1382 gtk_tool_item_group_set_child_property (GtkContainer *container,
1385 const GValue *value,
1388 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (container);
1389 GtkToolItem *item = GTK_TOOL_ITEM (child);
1390 gboolean homogeneous, expand, fill, new_row;
1392 if (prop_id != CHILD_PROP_POSITION)
1393 gtk_tool_item_group_get_item_packing (group, item,
1401 case CHILD_PROP_HOMOGENEOUS:
1402 gtk_tool_item_group_set_item_packing (group, item,
1403 g_value_get_boolean (value),
1409 case CHILD_PROP_EXPAND:
1410 gtk_tool_item_group_set_item_packing (group, item,
1412 g_value_get_boolean (value),
1417 case CHILD_PROP_FILL:
1418 gtk_tool_item_group_set_item_packing (group, item,
1421 g_value_get_boolean (value),
1425 case CHILD_PROP_NEW_ROW:
1426 gtk_tool_item_group_set_item_packing (group, item,
1430 g_value_get_boolean (value));
1433 case CHILD_PROP_POSITION:
1434 gtk_tool_item_group_set_item_position (group, item, g_value_get_int (value));
1438 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
1444 gtk_tool_item_group_get_child_property (GtkContainer *container,
1450 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (container);
1451 GtkToolItem *item = GTK_TOOL_ITEM (child);
1452 gboolean homogeneous, expand, fill, new_row;
1454 if (prop_id != CHILD_PROP_POSITION)
1455 gtk_tool_item_group_get_item_packing (group, item,
1463 case CHILD_PROP_HOMOGENEOUS:
1464 g_value_set_boolean (value, homogeneous);
1467 case CHILD_PROP_EXPAND:
1468 g_value_set_boolean (value, expand);
1471 case CHILD_PROP_FILL:
1472 g_value_set_boolean (value, fill);
1475 case CHILD_PROP_NEW_ROW:
1476 g_value_set_boolean (value, new_row);
1479 case CHILD_PROP_POSITION:
1480 g_value_set_int (value, gtk_tool_item_group_get_item_position (group, item));
1484 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
1490 gtk_tool_item_group_class_init (GtkToolItemGroupClass *cls)
1492 GObjectClass *oclass = G_OBJECT_CLASS (cls);
1493 GtkWidgetClass *wclass = GTK_WIDGET_CLASS (cls);
1494 GtkContainerClass *cclass = GTK_CONTAINER_CLASS (cls);
1496 oclass->set_property = gtk_tool_item_group_set_property;
1497 oclass->get_property = gtk_tool_item_group_get_property;
1498 oclass->finalize = gtk_tool_item_group_finalize;
1499 oclass->dispose = gtk_tool_item_group_dispose;
1501 wclass->size_request = gtk_tool_item_group_size_request;
1502 wclass->size_allocate = gtk_tool_item_group_size_allocate;
1503 wclass->realize = gtk_tool_item_group_realize;
1504 wclass->unrealize = gtk_tool_item_group_unrealize;
1505 wclass->style_set = gtk_tool_item_group_style_set;
1506 wclass->screen_changed = gtk_tool_item_group_screen_changed;
1508 cclass->add = gtk_tool_item_group_add;
1509 cclass->remove = gtk_tool_item_group_remove;
1510 cclass->forall = gtk_tool_item_group_forall;
1511 cclass->child_type = gtk_tool_item_group_child_type;
1512 cclass->set_child_property = gtk_tool_item_group_set_child_property;
1513 cclass->get_child_property = gtk_tool_item_group_get_child_property;
1515 g_object_class_install_property (oclass, PROP_NAME,
1516 g_param_spec_string ("name",
1518 P_("The name of this item group"),
1520 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1521 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1523 g_object_class_install_property (oclass, PROP_COLLAPSED,
1524 g_param_spec_boolean ("collapsed",
1526 P_("Wether the group has been collapsed and items are hidden"),
1528 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1529 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1531 g_object_class_install_property (oclass, PROP_ELLIPSIZE,
1532 g_param_spec_enum ("ellipsize",
1534 P_("Ellipsize for item group headers"),
1535 PANGO_TYPE_ELLIPSIZE_MODE, DEFAULT_ELLIPSIZE,
1536 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1537 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1539 g_object_class_install_property (oclass, PROP_ELLIPSIZE,
1540 g_param_spec_enum ("header-relief",
1542 P_("Relief of the group header button"),
1543 GTK_TYPE_RELIEF_STYLE, GTK_RELIEF_NORMAL,
1544 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1545 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1547 gtk_widget_class_install_style_property (wclass,
1548 g_param_spec_int ("expander-size",
1549 P_("Expander Size"),
1550 P_("Size of the expander arrow"),
1553 DEFAULT_EXPANDER_SIZE,
1554 G_PARAM_READABLE | G_PARAM_STATIC_NAME |
1555 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1557 gtk_widget_class_install_style_property (wclass,
1558 g_param_spec_int ("header-spacing",
1559 P_("Header Spacing"),
1560 P_("Spacing between expander arrow and caption"),
1563 DEFAULT_HEADER_SPACING,
1564 G_PARAM_READABLE | G_PARAM_STATIC_NAME |
1565 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1567 gtk_container_class_install_child_property (cclass, CHILD_PROP_HOMOGENEOUS,
1568 g_param_spec_boolean ("homogeneous",
1570 P_("Whether the item should be the same size as other homogeneous items"),
1572 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1573 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1575 gtk_container_class_install_child_property (cclass, CHILD_PROP_EXPAND,
1576 g_param_spec_boolean ("expand",
1578 P_("Whether the item should receive extra space when the toolbar grows"),
1580 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1581 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1583 gtk_container_class_install_child_property (cclass, CHILD_PROP_FILL,
1584 g_param_spec_boolean ("fill",
1586 P_("Whether the item should fill the avaiable space"),
1588 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1589 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1591 gtk_container_class_install_child_property (cclass, CHILD_PROP_NEW_ROW,
1592 g_param_spec_boolean ("new-row",
1594 P_("Whether the item should start a new row"),
1596 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1597 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1599 gtk_container_class_install_child_property (cclass, CHILD_PROP_POSITION,
1600 g_param_spec_int ("position",
1602 P_("Position of the item within this group"),
1606 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1607 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1609 g_type_class_add_private (cls, sizeof (GtkToolItemGroupPrivate));
1613 * gtk_tool_item_group_new:
1614 * @name: the name of the new group.
1616 * Creates a new tool item group with name @name.
1618 * Returns: a new #GtkToolItemGroup.
1623 gtk_tool_item_group_new (const gchar *name)
1625 return g_object_new (GTK_TYPE_TOOL_ITEM_GROUP, "name", name, NULL);
1629 * gtk_tool_item_group_set_name:
1630 * @group: an #GtkToolItemGroup.
1631 * @name: the new name of of the group.
1633 * Sets the name of the tool item group. The name is displayed in the header
1639 gtk_tool_item_group_set_name (GtkToolItemGroup *group,
1642 const gchar *current_name;
1645 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1646 current_name = gtk_tool_item_group_get_name (group);
1648 if (current_name != name && (!current_name || !name || strcmp (current_name, name)))
1650 label = gtk_tool_item_group_get_label (group);
1651 gtk_label_set_text (GTK_LABEL (label), name);
1653 if (name && group->priv->children)
1654 gtk_widget_show (group->priv->header);
1656 gtk_widget_hide (group->priv->header);
1658 g_object_notify (G_OBJECT (group), "name");
1663 * gtk_tool_item_group_header_relief:
1664 * @group: an #GtkToolItemGroup.
1665 * @style: The GtkReliefStyle
1667 * Set the button relief of the group header. See #gtk_button_set_relief for
1673 gtk_tool_item_group_set_header_relief (GtkToolItemGroup *group,
1674 GtkReliefStyle style)
1676 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1677 gtk_button_set_relief (GTK_BUTTON(group->priv->header), style);
1681 gtk_tool_item_group_get_animation_timestamp (GtkToolItemGroup *group)
1684 g_source_get_current_time (group->priv->animation_timeout, &now);
1685 return (now.tv_sec * G_USEC_PER_SEC + now.tv_usec - group->priv->animation_start) / 1000;
1689 gtk_tool_item_group_force_expose (GtkToolItemGroup *group)
1691 if (GTK_WIDGET_REALIZED (group->priv->header))
1693 GtkWidget *alignment = gtk_tool_item_group_get_alignment (group);
1696 /* Find the header button's arrow area... */
1697 area.x = alignment->allocation.x;
1698 area.y = alignment->allocation.y + (alignment->allocation.height - group->priv->expander_size) / 2;
1699 area.height = group->priv->expander_size;
1700 area.width = group->priv->expander_size;
1702 /* ... and invalidated it to get it animated. */
1703 gdk_window_invalidate_rect (group->priv->header->window, &area, TRUE);
1706 if (GTK_WIDGET_REALIZED (group))
1708 GtkWidget *widget = GTK_WIDGET (group);
1709 GtkWidget *parent = gtk_widget_get_parent (widget);
1710 int x, y, width, height;
1712 /* Find the tool item area button's arrow area... */
1713 width = widget->allocation.width;
1714 height = widget->allocation.height;
1716 gtk_widget_translate_coordinates (widget, parent, 0, 0, &x, &y);
1718 if (GTK_WIDGET_VISIBLE (group->priv->header))
1720 height -= group->priv->header->allocation.height;
1721 y += group->priv->header->allocation.height;
1724 /* ... and invalidated it to get it animated. */
1725 gtk_widget_queue_draw_area (parent, x, y, width, height);
1730 gtk_tool_item_group_animation_cb (gpointer data)
1732 GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data);
1733 gint64 timestamp = gtk_tool_item_group_get_animation_timestamp (group);
1735 /* Enque this early to reduce number of expose events. */
1736 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (group));
1738 /* Figure out current style of the expander arrow. */
1739 if (group->priv->collapsed)
1741 if (group->priv->expander_style == GTK_EXPANDER_EXPANDED)
1742 group->priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
1744 group->priv->expander_style = GTK_EXPANDER_COLLAPSED;
1748 if (group->priv->expander_style == GTK_EXPANDER_COLLAPSED)
1749 group->priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
1751 group->priv->expander_style = GTK_EXPANDER_EXPANDED;
1754 gtk_tool_item_group_force_expose (group);
1756 /* Finish animation when done. */
1757 if (timestamp >= ANIMATION_DURATION)
1758 group->priv->animation_timeout = NULL;
1760 /* Ensure that all composited windows and child windows are repainted, before
1761 * the parent widget gets its expose-event. This is needed to avoid heavy
1762 * rendering artifacts. GTK+ should take care about this issue by itself I
1763 * guess, but currently it doesn't. Also I don't understand the parameters
1764 * of this issue well enough yet, to file a bug report.
1766 gdk_window_process_updates (GTK_WIDGET (group)->window, TRUE);
1768 return (group->priv->animation_timeout != NULL);
1772 * gtk_tool_item_group_set_collapsed:
1773 * @group: an #GtkToolItemGroup.
1774 * @collapsed: whether the @group should be collapsed or expanded.
1776 * Sets whether the @group should be collapsed or expanded.
1781 gtk_tool_item_group_set_collapsed (GtkToolItemGroup *group,
1784 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1787 parent = gtk_widget_get_parent (GTK_WIDGET (group));
1788 if (GTK_IS_TOOL_PALETTE (parent) && !collapsed)
1789 _gtk_tool_palette_set_expanding_child (GTK_TOOL_PALETTE (parent),
1790 GTK_WIDGET (group));
1791 if (collapsed != group->priv->collapsed)
1793 if (group->priv->animation)
1797 g_get_current_time (&now);
1799 if (group->priv->animation_timeout)
1800 g_source_destroy (group->priv->animation_timeout);
1802 group->priv->animation_start = (now.tv_sec * G_USEC_PER_SEC + now.tv_usec);
1803 group->priv->animation_timeout = g_timeout_source_new (ANIMATION_TIMEOUT);
1805 g_source_set_callback (group->priv->animation_timeout,
1806 gtk_tool_item_group_animation_cb,
1809 g_source_attach (group->priv->animation_timeout, NULL);
1813 group->priv->expander_style = GTK_EXPANDER_COLLAPSED;
1814 gtk_tool_item_group_force_expose (group);
1816 group->priv->collapsed = collapsed;
1817 g_object_notify (G_OBJECT (group), "collapsed");
1822 * gtk_tool_item_group_set_ellipsize:
1823 * @group: an #GtkToolItemGroup.
1824 * @ellipsize: the #PangoEllipsizeMode labels in @group should use.
1826 * Sets the ellipsization mode which should be used by labels in @group.
1831 gtk_tool_item_group_set_ellipsize (GtkToolItemGroup *group,
1832 PangoEllipsizeMode ellipsize)
1834 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1836 if (ellipsize != group->priv->ellipsize)
1838 group->priv->ellipsize = ellipsize;
1839 gtk_tool_item_group_header_adjust_style (group);
1840 g_object_notify (G_OBJECT (group), "ellipsize");
1841 _gtk_tool_item_group_palette_reconfigured (group);
1846 * gtk_tool_item_group_get_name:
1847 * @group: an #GtkToolItemGroup.
1849 * Gets the name of @group.
1851 * Returns: the name of @group. The name is an internal string of @group and must not be modified.
1855 G_CONST_RETURN gchar*
1856 gtk_tool_item_group_get_name (GtkToolItemGroup *group)
1860 g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_NAME);
1862 label = gtk_tool_item_group_get_label (group);
1863 return gtk_label_get_text (GTK_LABEL (label));
1867 * gtk_tool_item_group_get_collapsed:
1868 * @group: an GtkToolItemGroup.
1870 * Gets whether @group is collapsed or expanded.
1872 * Returns: %TRUE if @group is collapsed, %FALSE if it is expanded.
1877 gtk_tool_item_group_get_collapsed (GtkToolItemGroup *group)
1879 g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_COLLAPSED);
1880 return group->priv->collapsed;
1884 * gtk_tool_item_group_get_ellipsize:
1885 * @group: an #GtkToolItemGroup.
1887 * Gets the ellipsization mode of @group.
1889 * Returns: the #PangoEllipsizeMode of @group.
1894 gtk_tool_item_group_get_ellipsize (GtkToolItemGroup *group)
1896 g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_ELLIPSIZE);
1897 return group->priv->ellipsize;
1901 * gtk_tool_item_group_get_header_relief:
1902 * @group: an #GtkToolItemGroup.
1904 * Gets the relief mode of the header button of @group.
1906 * Returns: the #GtkReliefStyle
1911 gtk_tool_item_group_get_header_relief (GtkToolItemGroup *group)
1913 g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), GTK_RELIEF_NORMAL);
1914 return gtk_button_get_relief (GTK_BUTTON (group->priv->header));
1918 * gtk_tool_item_group_insert:
1919 * @group: an #GtkToolItemGroup.
1920 * @item: the #GtkToolItem to insert into @group.
1921 * @position: the position of @item in @group, starting with 0. The position -1 means end of list.
1923 * Inserts @item at @position in the list of children of @group.
1928 gtk_tool_item_group_insert (GtkToolItemGroup *group,
1932 GtkWidget *parent, *child_widget;
1933 GtkToolItemGroupChild *child;
1935 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1936 g_return_if_fail (GTK_IS_TOOL_ITEM (item));
1937 g_return_if_fail (position >= -1);
1939 parent = gtk_widget_get_parent (GTK_WIDGET (group));
1941 child = g_new (GtkToolItemGroupChild, 1);
1942 child->item = g_object_ref_sink (item);
1943 child->homogeneous = TRUE;
1944 child->expand = FALSE;
1946 child->new_row = FALSE;
1948 group->priv->children = g_list_insert (group->priv->children, child, position);
1950 if (GTK_IS_TOOL_PALETTE (parent))
1951 _gtk_tool_palette_child_set_drag_source (GTK_WIDGET (item), parent);
1953 child_widget = gtk_bin_get_child (GTK_BIN (item));
1955 if (GTK_IS_BUTTON (child_widget))
1956 gtk_button_set_focus_on_click (GTK_BUTTON (child_widget), TRUE);
1958 gtk_widget_set_parent (GTK_WIDGET (item), GTK_WIDGET (group));
1962 * gtk_tool_item_group_set_item_position:
1963 * @group: an #GtkToolItemGroup.
1964 * @item: the #GtkToolItem to move to a new position, should be a child of @group.
1965 * @position: the new position of @item in @group, starting with 0. The position -1 means end of list.
1967 * Sets the position of @item in the list of children of @group.
1972 gtk_tool_item_group_set_item_position (GtkToolItemGroup *group,
1978 GtkToolItemGroupChild *child;
1980 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1981 g_return_if_fail (GTK_IS_TOOL_ITEM (item));
1983 g_return_if_fail (position >= -1);
1985 child = gtk_tool_item_group_get_child (group, item, &old_position, &link);
1987 g_return_if_fail (child != NULL);
1989 if (position == old_position)
1992 group->priv->children = g_list_delete_link (group->priv->children, link);
1993 group->priv->children = g_list_insert (group->priv->children, child, position);
1995 gtk_widget_child_notify (GTK_WIDGET (item), "position");
1996 if (GTK_WIDGET_VISIBLE (group) && GTK_WIDGET_VISIBLE (item))
1997 gtk_widget_queue_resize (GTK_WIDGET (group));
2001 * gtk_tool_item_group_get_item_position:
2002 * @group: an #GtkToolItemGroup.
2003 * @item: a #GtkToolItem.
2005 * Gets the position of @item in @group as index.
2007 * Returns: the index of @item in @group or -1 if @item is no child of @group.
2012 gtk_tool_item_group_get_item_position (GtkToolItemGroup *group,
2017 g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), -1);
2018 g_return_val_if_fail (GTK_IS_TOOL_ITEM (item), -1);
2020 if (gtk_tool_item_group_get_child (group, item, &position, NULL))
2027 * gtk_tool_item_group_get_n_items:
2028 * @group: an #GtkToolItemGroup.
2030 * Gets the number of tool items in group.
2032 * Returns: the number of tool items in group.
2037 gtk_tool_item_group_get_n_items (GtkToolItemGroup *group)
2039 g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), 0);
2041 return g_list_length (group->priv->children);
2045 * gtk_tool_item_group_get_nth_item:
2046 * @group: an #GtkToolItemGroup.
2047 * @index: the index.
2049 * Gets the tool item at index in group.
2051 * Returns: the #GtkToolItem at index.
2056 gtk_tool_item_group_get_nth_item (GtkToolItemGroup *group,
2059 GtkToolItemGroupChild *child;
2061 g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL);
2063 child = g_list_nth_data (group->priv->children, index);
2065 return child != NULL ? child->item : NULL;
2069 * gtk_tool_item_group_get_drop_item:
2070 * @group: an #GtkToolItemGroup.
2071 * @x: the x position.
2072 * @y: the y position.
2074 * Gets the tool item at position (x, y).
2076 * Returns: the #GtkToolItem at position (x, y).
2081 gtk_tool_item_group_get_drop_item (GtkToolItemGroup *group,
2085 GtkAllocation *allocation;
2086 GtkOrientation orientation;
2089 g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL);
2091 allocation = >K_WIDGET (group)->allocation;
2092 orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
2094 g_return_val_if_fail (x >= 0 && x < allocation->width, NULL);
2095 g_return_val_if_fail (y >= 0 && y < allocation->height, NULL);
2097 for (it = group->priv->children; it != NULL; it = it->next)
2099 GtkToolItemGroupChild *child = it->data;
2100 GtkToolItem *item = child->item;
2103 if (!item || !gtk_tool_item_group_is_item_visible (group, child))
2106 allocation = >K_WIDGET (item)->allocation;
2108 x0 = x - allocation->x;
2109 y0 = y - allocation->y;
2111 if (x0 >= 0 && x0 < allocation->width &&
2112 y0 >= 0 && y0 < allocation->height)
2120 _gtk_tool_item_group_item_size_request (GtkToolItemGroup *group,
2121 GtkRequisition *item_size,
2122 gboolean homogeneous_only,
2123 gint *requested_rows)
2125 GtkRequisition child_requisition;
2128 gboolean new_row = TRUE;
2129 GtkOrientation orientation;
2130 GtkToolbarStyle style;
2132 g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
2133 g_return_if_fail (NULL != item_size);
2135 orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
2136 style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group));
2138 item_size->width = item_size->height = 0;
2140 for (it = group->priv->children; it != NULL; it = it->next)
2142 GtkToolItemGroupChild *child = it->data;
2144 if (!gtk_tool_item_group_is_item_visible (group, child))
2147 if (child->new_row || new_row)
2153 if (!child->homogeneous && child->expand)
2156 gtk_widget_size_request (GTK_WIDGET (child->item), &child_requisition);
2158 if (!homogeneous_only || child->homogeneous)
2159 item_size->width = MAX (item_size->width, child_requisition.width);
2160 item_size->height = MAX (item_size->height, child_requisition.height);
2164 *requested_rows = rows;
2168 _gtk_tool_item_group_paint (GtkToolItemGroup *group,
2171 GtkWidget *widget = GTK_WIDGET (group);
2173 gdk_cairo_set_source_pixmap (cr, widget->window,
2174 widget->allocation.x,
2175 widget->allocation.y);
2177 if (group->priv->animation_timeout)
2179 GtkOrientation orientation = gtk_tool_item_group_get_orientation (GTK_TOOL_SHELL (group));
2180 cairo_pattern_t *mask;
2183 if (GTK_ORIENTATION_VERTICAL == orientation)
2184 v1 = widget->allocation.height;
2186 v1 = widget->allocation.width;
2190 if (!GTK_WIDGET_VISIBLE (group->priv->header))
2192 else if (GTK_ORIENTATION_VERTICAL == orientation)
2193 v0 = MAX (v0, group->priv->header->allocation.height);
2195 v0 = MAX (v0, group->priv->header->allocation.width);
2197 v1 = MIN (v0 + 256, v1);
2199 if (GTK_ORIENTATION_VERTICAL == orientation)
2201 v0 += widget->allocation.y;
2202 v1 += widget->allocation.y;
2204 mask = cairo_pattern_create_linear (0.0, v0, 0.0, v1);
2208 v0 += widget->allocation.x;
2209 v1 += widget->allocation.x;
2211 mask = cairo_pattern_create_linear (v0, 0.0, v1, 0.0);
2214 cairo_pattern_add_color_stop_rgba (mask, 0.00, 0.0, 0.0, 0.0, 1.00);
2215 cairo_pattern_add_color_stop_rgba (mask, 0.25, 0.0, 0.0, 0.0, 0.25);
2216 cairo_pattern_add_color_stop_rgba (mask, 0.50, 0.0, 0.0, 0.0, 0.10);
2217 cairo_pattern_add_color_stop_rgba (mask, 0.75, 0.0, 0.0, 0.0, 0.01);
2218 cairo_pattern_add_color_stop_rgba (mask, 1.00, 0.0, 0.0, 0.0, 0.00);
2220 cairo_mask (cr, mask);
2221 cairo_pattern_destroy (mask);
2228 _gtk_tool_item_group_get_size_for_limit (GtkToolItemGroup *group,
2233 GtkRequisition requisition;
2235 gtk_widget_size_request (GTK_WIDGET (group), &requisition);
2237 if (!group->priv->collapsed || group->priv->animation_timeout)
2239 GtkAllocation allocation = { 0, 0, requisition.width, requisition.height };
2240 GtkRequisition inquery;
2243 allocation.width = limit;
2245 allocation.height = limit;
2247 gtk_tool_item_group_real_size_query (GTK_WIDGET (group),
2248 &allocation, &inquery);
2251 inquery.height -= requisition.height;
2253 inquery.width -= requisition.width;
2255 if (group->priv->animation_timeout && animation)
2257 gint64 timestamp = gtk_tool_item_group_get_animation_timestamp (group);
2259 timestamp = MIN (timestamp, ANIMATION_DURATION);
2261 if (group->priv->collapsed)
2262 timestamp = ANIMATION_DURATION - timestamp;
2266 inquery.height *= timestamp;
2267 inquery.height /= ANIMATION_DURATION;
2271 inquery.width *= timestamp;
2272 inquery.width /= ANIMATION_DURATION;
2277 requisition.height += inquery.height;
2279 requisition.width += inquery.width;
2282 return (vertical ? requisition.height : requisition.width);
2286 _gtk_tool_item_group_get_height_for_width (GtkToolItemGroup *group,
2289 return _gtk_tool_item_group_get_size_for_limit (group, width, TRUE, group->priv->animation);
2293 _gtk_tool_item_group_get_width_for_height (GtkToolItemGroup *group,
2296 return _gtk_tool_item_group_get_size_for_limit (group, height, FALSE, TRUE);
2300 gtk_tool_palette_reconfigured_foreach_item (GtkWidget *child,
2301 gpointer data G_GNUC_UNUSED)
2303 if (GTK_IS_TOOL_ITEM (child))
2304 gtk_tool_item_toolbar_reconfigured (GTK_TOOL_ITEM (child));
2309 _gtk_tool_item_group_palette_reconfigured (GtkToolItemGroup *group)
2311 gtk_container_foreach (GTK_CONTAINER (group),
2312 gtk_tool_palette_reconfigured_foreach_item,
2315 gtk_tool_item_group_header_adjust_style (group);