]> Pileus Git - ~andy/gtk/blob - gtk/gtktoolitemgroup.c
toolpalette: fixed trailing whitespaces
[~andy/gtk] / gtk / gtktoolitemgroup.c
1 /* GtkToolPalette -- A tool palette with categories and DnD support
2  * Copyright (C) 2008  Openismus GmbH
3  *
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.
8  *
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.
13  *
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
17  *
18  * Authors:
19  *      Mathias Hasselmann
20  *      Jan Arne Petersen
21  */
22
23 #include "gtktoolpaletteprivate.h"
24
25 #include <gtk/gtk.h>
26 #include <math.h>
27 #include <string.h>
28
29 #define P_(msgid)               (msgid)
30
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
36
37 #define DEFAULT_LABEL            NULL
38 #define DEFAULT_COLLAPSED        FALSE
39 #define DEFAULT_ELLIPSIZE        PANGO_ELLIPSIZE_NONE
40
41 /**
42  * SECTION:GtkToolItemGroup
43  * @short_description: A sub container used in a tool palette
44  * @include: gtktoolitemgroup.h
45  *
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.
48  *
49  * Since: 2.20
50  */
51
52 enum
53 {
54   PROP_NONE,
55   PROP_LABEL,
56   PROP_LABEL_WIDGET,
57   PROP_COLLAPSED,
58   PROP_ELLIPSIZE,
59   PROP_RELIEF
60 };
61
62 enum
63 {
64   CHILD_PROP_NONE,
65   CHILD_PROP_HOMOGENEOUS,
66   CHILD_PROP_EXPAND,
67   CHILD_PROP_FILL,
68   CHILD_PROP_NEW_ROW,
69   CHILD_PROP_POSITION,
70 };
71
72 typedef struct _GtkToolItemGroupChild GtkToolItemGroupChild;
73
74 struct _GtkToolItemGroupPrivate
75 {
76   GtkWidget         *header;
77   GtkWidget         *label_widget;
78
79   GList             *children;
80
81   gboolean           animation;
82   gint64             animation_start;
83   GSource           *animation_timeout;
84   GtkExpanderStyle   expander_style;
85   gint               expander_size;
86   gint               header_spacing;
87   PangoEllipsizeMode ellipsize;
88
89   gulong             focus_set_id;
90   GtkWidget         *toplevel;
91
92   GtkSettings       *settings;
93   gulong             settings_connection;
94
95   guint              collapsed : 1;
96 };
97
98 struct _GtkToolItemGroupChild
99 {
100   GtkToolItem *item;
101
102   guint        homogeneous : 1;
103   guint        expand : 1;
104   guint        fill : 1;
105   guint        new_row : 1;
106 };
107
108 static void gtk_tool_item_group_tool_shell_init (GtkToolShellIface *iface);
109
110 G_DEFINE_TYPE_WITH_CODE (GtkToolItemGroup, gtk_tool_item_group, GTK_TYPE_CONTAINER,
111 G_IMPLEMENT_INTERFACE (GTK_TYPE_TOOL_SHELL, gtk_tool_item_group_tool_shell_init));
112
113 static GtkWidget*
114 gtk_tool_item_group_get_alignment (GtkToolItemGroup *group)
115 {
116   return gtk_bin_get_child (GTK_BIN (group->priv->header));
117 }
118
119 static GtkOrientation
120 gtk_tool_item_group_get_orientation (GtkToolShell *shell)
121 {
122   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
123
124   if (GTK_IS_TOOL_PALETTE (parent))
125     return gtk_orientable_get_orientation (GTK_ORIENTABLE (parent));
126
127   return GTK_ORIENTATION_VERTICAL;
128 }
129
130 static GtkToolbarStyle
131 gtk_tool_item_group_get_style (GtkToolShell *shell)
132 {
133   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
134
135   if (GTK_IS_TOOL_PALETTE (parent))
136     return gtk_tool_palette_get_style (GTK_TOOL_PALETTE (parent));
137
138   return GTK_TOOLBAR_ICONS;
139 }
140
141 static GtkIconSize
142 gtk_tool_item_group_get_icon_size (GtkToolShell *shell)
143 {
144   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
145
146   if (GTK_IS_TOOL_PALETTE (parent))
147     return gtk_tool_palette_get_icon_size (GTK_TOOL_PALETTE (parent));
148
149   return GTK_ICON_SIZE_SMALL_TOOLBAR;
150 }
151
152 static PangoEllipsizeMode
153 gtk_tool_item_group_get_ellipsize_mode (GtkToolShell *shell)
154 {
155   return GTK_TOOL_ITEM_GROUP (shell)->priv->ellipsize;
156 }
157
158 static gfloat
159 gtk_tool_item_group_get_text_alignment (GtkToolShell *shell)
160 {
161   if (GTK_TOOLBAR_TEXT == gtk_tool_item_group_get_style (shell) ||
162       GTK_TOOLBAR_BOTH_HORIZ == gtk_tool_item_group_get_style (shell))
163     return 0.0;
164
165   return 0.5;
166 }
167
168 static GtkOrientation
169 gtk_tool_item_group_get_text_orientation (GtkToolShell *shell)
170 {
171   return GTK_ORIENTATION_HORIZONTAL;
172 }
173
174 static GtkSizeGroup *
175 gtk_tool_item_group_get_text_size_group (GtkToolShell *shell)
176 {
177   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell));
178
179   if (GTK_IS_TOOL_PALETTE (parent))
180     return _gtk_tool_palette_get_size_group (GTK_TOOL_PALETTE (parent));
181
182   return NULL;
183 }
184
185 static void
186 animation_change_notify (GtkToolItemGroup *group)
187 {
188   GtkSettings *settings = group->priv->settings;
189   gboolean animation;
190
191   if (settings)
192     g_object_get (settings,
193                   "gtk-enable-animations", &animation,
194                   NULL);
195   else
196     animation = DEFAULT_ANIMATION_STATE;
197
198   group->priv->animation = animation;
199 }
200
201 static void
202 gtk_tool_item_group_settings_change_notify (GtkSettings      *settings,
203                                             const GParamSpec *pspec,
204                                             GtkToolItemGroup *group)
205 {
206   if (strcmp (pspec->name, "gtk-enable-animations") == 0)
207     animation_change_notify (group);
208 }
209
210 static void
211 gtk_tool_item_group_screen_changed (GtkWidget *widget,
212                                     GdkScreen *previous_screen)
213 {
214   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget);
215   GtkToolItemGroupPrivate* priv = group->priv;
216   GtkSettings *old_settings = priv->settings;
217   GtkSettings *settings;
218
219   if (gtk_widget_has_screen (GTK_WIDGET (group)))
220     settings = gtk_widget_get_settings (GTK_WIDGET (group));
221   else
222     settings = NULL;
223
224   if (settings == old_settings)
225     return;
226
227   if (old_settings)
228   {
229     g_signal_handler_disconnect (old_settings, priv->settings_connection);
230     g_object_unref (old_settings);
231   }
232
233   if (settings)
234   {
235     priv->settings_connection =
236       g_signal_connect (settings, "notify",
237                         G_CALLBACK (gtk_tool_item_group_settings_change_notify),
238                         group);
239     priv->settings = g_object_ref (settings);
240   }
241   else
242     priv->settings = NULL;
243
244   animation_change_notify (group);
245 }
246
247 static void
248 gtk_tool_item_group_tool_shell_init (GtkToolShellIface *iface)
249 {
250   iface->get_icon_size = gtk_tool_item_group_get_icon_size;
251   iface->get_orientation = gtk_tool_item_group_get_orientation;
252   iface->get_style = gtk_tool_item_group_get_style;
253   iface->get_text_alignment = gtk_tool_item_group_get_text_alignment;
254   iface->get_text_orientation = gtk_tool_item_group_get_text_orientation;
255   iface->get_text_size_group = gtk_tool_item_group_get_text_size_group;
256   iface->get_ellipsize_mode = gtk_tool_item_group_get_ellipsize_mode;
257 }
258
259 static gboolean
260 gtk_tool_item_group_header_expose_event_cb (GtkWidget      *widget,
261                                             GdkEventExpose *event,
262                                             gpointer        data)
263 {
264   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data);
265   GtkToolItemGroupPrivate* priv = group->priv;
266   GtkExpanderStyle expander_style;
267   GtkOrientation orientation;
268   gint x, y;
269   GtkTextDirection direction;
270
271   orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
272   expander_style = priv->expander_style;
273   direction = gtk_widget_get_direction (widget);
274
275   if (GTK_ORIENTATION_VERTICAL == orientation)
276     {
277       if (GTK_TEXT_DIR_RTL == direction)
278         x = widget->allocation.x + widget->allocation.width - priv->expander_size / 2;
279       else
280         x = widget->allocation.x + priv->expander_size / 2;
281       y = widget->allocation.y + widget->allocation.height / 2;
282     }
283   else
284     {
285       x = widget->allocation.x + widget->allocation.width / 2;
286       y = widget->allocation.y + priv->expander_size / 2;
287
288       /* Unfortunatly gtk_paint_expander() doesn't support rotated drawing
289        * modes. Luckily the following shady arithmetics produce the desired
290        * result. */
291       expander_style = GTK_EXPANDER_EXPANDED - expander_style;
292     }
293
294   gtk_paint_expander (widget->style, widget->window,
295                       priv->header->state,
296                       &event->area, GTK_WIDGET (group),
297                       "tool-palette-header", x, y,
298                       expander_style);
299
300   return FALSE;
301 }
302
303 static void
304 gtk_tool_item_group_header_size_request_cb (GtkWidget      *widget,
305                                             GtkRequisition *requisition,
306                                             gpointer        data)
307 {
308   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data);
309   requisition->height = MAX (requisition->height, group->priv->expander_size);
310 }
311
312 static void
313 gtk_tool_item_group_header_clicked_cb (GtkButton *button,
314                                        gpointer   data)
315 {
316   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data);
317   GtkToolItemGroupPrivate* priv = group->priv;
318   GtkWidget *parent = gtk_widget_get_parent (data);
319
320   if (priv->collapsed ||
321       !GTK_IS_TOOL_PALETTE (parent) ||
322       !gtk_tool_palette_get_exclusive (GTK_TOOL_PALETTE (parent), data))
323     gtk_tool_item_group_set_collapsed (group, !priv->collapsed);
324 }
325
326 static void
327 gtk_tool_item_group_header_adjust_style (GtkToolItemGroup *group)
328 {
329   GtkWidget *alignment = gtk_tool_item_group_get_alignment (group);
330   GtkWidget *label_widget = gtk_bin_get_child (GTK_BIN (alignment));
331   GtkWidget *widget = GTK_WIDGET (group);
332   GtkToolItemGroupPrivate* priv = group->priv;
333   gint dx = 0, dy = 0;
334   GtkTextDirection direction = gtk_widget_get_direction (widget);
335
336   gtk_widget_style_get (widget,
337                         "header-spacing", &(priv->header_spacing),
338                         "expander-size", &(priv->expander_size),
339                         NULL);
340
341   switch (gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)))
342     {
343       case GTK_ORIENTATION_HORIZONTAL:
344         dy = priv->header_spacing + priv->expander_size;
345
346         if (GTK_IS_LABEL (label_widget))
347           {
348             gtk_label_set_ellipsize (GTK_LABEL (label_widget), PANGO_ELLIPSIZE_NONE);
349             if (GTK_TEXT_DIR_RTL == direction)
350               gtk_label_set_angle (GTK_LABEL (label_widget), -90);
351             else
352               gtk_label_set_angle (GTK_LABEL (label_widget), 90);
353           }
354        break;
355
356       case GTK_ORIENTATION_VERTICAL:
357         dx = priv->header_spacing + priv->expander_size;
358
359         if (GTK_IS_LABEL (label_widget))
360           {
361             gtk_label_set_ellipsize (GTK_LABEL (label_widget), priv->ellipsize);
362             gtk_label_set_angle (GTK_LABEL (label_widget), 0);
363           }
364         break;
365     }
366
367   gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), dy, 0, dx, 0);
368 }
369
370 static void
371 gtk_tool_item_group_init (GtkToolItemGroup *group)
372 {
373   GtkWidget *alignment;
374   GtkToolItemGroupPrivate* priv;
375
376   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (group), FALSE);
377
378   group->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (group,
379                                              GTK_TYPE_TOOL_ITEM_GROUP,
380                                              GtkToolItemGroupPrivate);
381
382   priv->children = NULL;
383   priv->header_spacing = DEFAULT_HEADER_SPACING;
384   priv->expander_size = DEFAULT_EXPANDER_SIZE;
385   priv->expander_style = GTK_EXPANDER_EXPANDED;
386
387   priv->label_widget = gtk_label_new (NULL);
388   gtk_misc_set_alignment (GTK_MISC (priv->label_widget), 0.0, 0.5);
389   alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
390   gtk_container_add (GTK_CONTAINER (alignment), priv->label_widget);
391   gtk_widget_show_all (alignment);
392
393   gtk_widget_push_composite_child ();
394   priv->header = gtk_button_new ();
395   gtk_widget_set_composite_name (priv->header, "header");
396   gtk_widget_pop_composite_child ();
397
398   g_object_ref_sink (priv->header);
399   gtk_button_set_focus_on_click (GTK_BUTTON (priv->header), FALSE);
400   gtk_container_add (GTK_CONTAINER (priv->header), alignment);
401   gtk_widget_set_parent (priv->header, GTK_WIDGET (group));
402
403   gtk_tool_item_group_header_adjust_style (group);
404
405   g_signal_connect_after (alignment, "expose-event",
406                           G_CALLBACK (gtk_tool_item_group_header_expose_event_cb),
407                           group);
408   g_signal_connect_after (alignment, "size-request",
409                           G_CALLBACK (gtk_tool_item_group_header_size_request_cb),
410                           group);
411
412   g_signal_connect (priv->header, "clicked",
413                     G_CALLBACK (gtk_tool_item_group_header_clicked_cb),
414                     group);
415 }
416
417 static void
418 gtk_tool_item_group_set_property (GObject      *object,
419                                   guint         prop_id,
420                                   const GValue *value,
421                                   GParamSpec   *pspec)
422 {
423   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object);
424
425   switch (prop_id)
426     {
427       case PROP_LABEL:
428         gtk_tool_item_group_set_label (group, g_value_get_string (value));
429         break;
430
431       case PROP_LABEL_WIDGET:
432         gtk_tool_item_group_set_label_widget (group, g_value_get_object (value));
433
434       case PROP_COLLAPSED:
435         gtk_tool_item_group_set_collapsed (group, g_value_get_boolean (value));
436         break;
437
438       case PROP_ELLIPSIZE:
439         gtk_tool_item_group_set_ellipsize (group, g_value_get_enum (value));
440         break;
441       case PROP_RELIEF:
442         gtk_tool_item_group_set_header_relief (group, g_value_get_enum(value));
443         break;
444       default:
445         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
446         break;
447     }
448 }
449
450 static void
451 gtk_tool_item_group_get_property (GObject    *object,
452                                   guint       prop_id,
453                                   GValue     *value,
454                                   GParamSpec *pspec)
455 {
456   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object);
457
458   switch (prop_id)
459     {
460       case PROP_LABEL:
461         g_value_set_string (value, gtk_tool_item_group_get_label (group));
462         break;
463
464       case PROP_LABEL_WIDGET:
465         g_value_set_object (value,
466                             gtk_tool_item_group_get_label_widget (group));
467         break;
468
469       case PROP_COLLAPSED:
470         g_value_set_boolean (value, gtk_tool_item_group_get_collapsed (group));
471         break;
472
473       case PROP_ELLIPSIZE:
474         g_value_set_enum (value, gtk_tool_item_group_get_ellipsize (group));
475         break;
476
477       case PROP_RELIEF:
478         g_value_set_enum (value, gtk_tool_item_group_get_header_relief (group));
479         break;
480
481       default:
482         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
483         break;
484     }
485 }
486
487 static void
488 gtk_tool_item_group_finalize (GObject *object)
489 {
490   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object);
491
492   if (group->priv->children)
493     {
494       g_list_free (group->priv->children);
495       group->priv->children = NULL;
496     }
497
498   G_OBJECT_CLASS (gtk_tool_item_group_parent_class)->finalize (object);
499 }
500
501 static void
502 gtk_tool_item_group_dispose (GObject *object)
503 {
504   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object);
505   GtkToolItemGroupPrivate* priv = group->priv;
506
507   if (priv->toplevel)
508     {
509       /* disconnect focus tracking handler */
510       g_signal_handler_disconnect (priv->toplevel,
511                                    priv->focus_set_id);
512
513       priv->focus_set_id = 0;
514       priv->toplevel = NULL;
515     }
516
517   G_OBJECT_CLASS (gtk_tool_item_group_parent_class)->dispose (object);
518 }
519
520 static void
521 gtk_tool_item_group_get_item_size (GtkToolItemGroup *group,
522                                    GtkRequisition   *item_size,
523                                    gboolean          homogeneous_only,
524                                    gint             *requested_rows)
525 {
526   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (group));
527
528   if (GTK_IS_TOOL_PALETTE (parent))
529     _gtk_tool_palette_get_item_size (GTK_TOOL_PALETTE (parent), item_size, homogeneous_only, requested_rows);
530   else
531     _gtk_tool_item_group_item_size_request (group, item_size, homogeneous_only, requested_rows);
532 }
533
534 static void
535 gtk_tool_item_group_size_request (GtkWidget      *widget,
536                                   GtkRequisition *requisition)
537 {
538   const gint border_width = GTK_CONTAINER (widget)->border_width;
539   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget);
540   GtkToolItemGroupPrivate* priv = group->priv;
541   GtkOrientation orientation;
542   GtkRequisition item_size;
543   gint requested_rows;
544
545   if (priv->children && gtk_tool_item_group_get_label_widget (group))
546     {
547       gtk_widget_size_request (priv->header, requisition);
548       gtk_widget_show (priv->header);
549     }
550   else
551     {
552       requisition->width = requisition->height = 0;
553       gtk_widget_hide (priv->header);
554     }
555
556   gtk_tool_item_group_get_item_size (group, &item_size, FALSE, &requested_rows);
557
558   orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
559
560   if (GTK_ORIENTATION_VERTICAL == orientation)
561     requisition->width = MAX (requisition->width, item_size.width);
562   else
563     requisition->height = MAX (requisition->height, item_size.height * requested_rows);
564
565   requisition->width += border_width * 2;
566   requisition->height += border_width * 2;
567 }
568
569 static gboolean
570 gtk_tool_item_group_is_item_visible (GtkToolItemGroup      *group,
571                                      GtkToolItemGroupChild *child)
572 {
573   GtkToolbarStyle style;
574   GtkOrientation orientation;
575
576   orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
577   style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group));
578
579   /* horizontal tool palettes with text style support only homogeneous items */
580   if (!child->homogeneous &&
581       GTK_ORIENTATION_HORIZONTAL == orientation &&
582       GTK_TOOLBAR_TEXT == style)
583     return FALSE;
584
585   return
586     (GTK_WIDGET_VISIBLE (child->item)) &&
587     (GTK_ORIENTATION_VERTICAL == orientation ?
588      gtk_tool_item_get_visible_vertical (child->item) :
589      gtk_tool_item_get_visible_horizontal (child->item));
590 }
591
592 static inline unsigned
593 udiv (unsigned x,
594       unsigned y)
595 {
596   return (x + y - 1) / y;
597 }
598
599 static void
600 gtk_tool_item_group_real_size_query (GtkWidget      *widget,
601                                      GtkAllocation  *allocation,
602                                      GtkRequisition *inquery)
603 {
604   const gint border_width = GTK_CONTAINER (widget)->border_width;
605   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget);
606   GtkToolItemGroupPrivate* priv = group->priv;
607
608   GtkRequisition item_size;
609   GtkAllocation item_area;
610
611   GtkOrientation orientation;
612   GtkToolbarStyle style;
613
614   gint min_rows;
615
616   orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
617   style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group));
618
619   /* figure out the size of homogeneous items */
620   gtk_tool_item_group_get_item_size (group, &item_size, TRUE, &min_rows);
621
622   if (GTK_ORIENTATION_VERTICAL == orientation)
623     item_size.width = MIN (item_size.width, allocation->width);
624   else
625     item_size.height = MIN (item_size.height, allocation->height);
626
627   item_area.width = 0;
628   item_area.height = 0;
629
630   /* figure out the required columns (n_columns) and rows (n_rows) to place all items */
631   if (!priv->collapsed || !priv->animation || priv->animation_timeout)
632     {
633       guint n_columns;
634       gint n_rows;
635       GList *it;
636
637       if (GTK_ORIENTATION_VERTICAL == orientation)
638         {
639           gboolean new_row = FALSE;
640           gint row = -1;
641           guint col = 0;
642
643           item_area.width = allocation->width - 2 * border_width;
644           n_columns = MAX (item_area.width / item_size.width, 1);
645
646           /* calculate required rows for n_columns columns */
647           for (it = priv->children; it != NULL; it = it->next)
648             {
649               GtkToolItemGroupChild *child = it->data;
650
651               if (!gtk_tool_item_group_is_item_visible (group, child))
652                 continue;
653
654               if (new_row || child->new_row)
655                 {
656                   new_row = FALSE;
657                   row++;
658                   col = 0;
659                 }
660
661               if (child->expand)
662                 new_row = TRUE;
663
664               if (child->homogeneous)
665                 {
666                   col++;
667                   if (col >= n_columns)
668                     new_row = TRUE;
669                 }
670               else
671                 {
672                   GtkRequisition req = {0, 0};
673                   guint width;
674
675                   gtk_widget_size_request (GTK_WIDGET (child->item), &req);
676
677                   width = udiv (req.width, item_size.width);
678                   col += width;
679
680                   if (col > n_columns)
681                     row++;
682
683                   col = width;
684
685                   if (col >= n_columns)
686                     new_row = TRUE;
687                 }
688             }
689           n_rows = row + 2;
690         }
691       else
692         {
693           guint *row_min_width;
694           gint row = -1;
695           gboolean new_row = TRUE;
696           guint col = 0, min_col, max_col = 0, all_items = 0;
697           gint i;
698
699           item_area.height = allocation->height - 2 * border_width;
700           n_rows = MAX (item_area.height / item_size.height, min_rows);
701
702           row_min_width = g_new0 (guint, n_rows);
703
704           /* calculate minimal and maximal required cols and minimal required rows */
705           for (it = priv->children; it != NULL; it = it->next)
706             {
707               GtkToolItemGroupChild *child = it->data;
708
709               if (!gtk_tool_item_group_is_item_visible (group, child))
710                 continue;
711
712               if (new_row || child->new_row)
713                 {
714                   new_row = FALSE;
715                   row++;
716                   col = 0;
717                   row_min_width[row] = 1;
718                 }
719
720               if (child->expand)
721                 new_row = TRUE;
722
723               if (child->homogeneous)
724                 {
725                   col++;
726                   all_items++;
727                 }
728               else
729                 {
730                   GtkRequisition req = {0, 0};
731                   guint width;
732
733                   gtk_widget_size_request (GTK_WIDGET (child->item), &req);
734
735                   width = udiv (req.width, item_size.width);
736
737                   col += width;
738                   all_items += width;
739
740                   row_min_width[row] = MAX (row_min_width[row], width);
741                 }
742
743               max_col = MAX (max_col, col);
744             }
745
746           /* calculate minimal required cols */
747           min_col = udiv (all_items, n_rows);
748
749           for (i = 0; i <= row; i++)
750             {
751               min_col = MAX (min_col, row_min_width[i]);
752             }
753
754           /* simple linear search for minimal required columns for the given maximal number of rows (n_rows) */
755           for (n_columns = min_col; n_columns < max_col; n_columns ++)
756             {
757               new_row = TRUE;
758               row = -1;
759               /* calculate required rows for n_columns columns */
760               for (it = priv->children; it != NULL; it = it->next)
761                 {
762                   GtkToolItemGroupChild *child = it->data;
763
764                   if (!gtk_tool_item_group_is_item_visible (group, child))
765                     continue;
766
767                   if (new_row || child->new_row)
768                     {
769                       new_row = FALSE;
770                       row++;
771                       col = 0;
772                     }
773
774                   if (child->expand)
775                     new_row = TRUE;
776
777                   if (child->homogeneous)
778                     {
779                       col++;
780                       if (col >= n_columns)
781                         new_row = TRUE;
782                     }
783                   else
784                     {
785                       GtkRequisition req = {0, 0};
786                       guint width;
787
788                       gtk_widget_size_request (GTK_WIDGET (child->item), &req);
789
790                       width = udiv (req.width, item_size.width);
791                       col += width;
792
793                       if (col > n_columns)
794                         row++;
795
796                       col = width;
797
798                       if (col >= n_columns)
799                         new_row = TRUE;
800                     }
801                 }
802
803               if (row < n_rows)
804                 break;
805             }
806         }
807
808       item_area.width = item_size.width * n_columns;
809       item_area.height = item_size.height * n_rows;
810     }
811
812   inquery->width = 0;
813   inquery->height = 0;
814
815   /* figure out header widget size */
816   if (GTK_WIDGET_VISIBLE (priv->header))
817     {
818       GtkRequisition child_requisition;
819
820       gtk_widget_size_request (priv->header, &child_requisition);
821
822       if (GTK_ORIENTATION_VERTICAL == orientation)
823         inquery->height += child_requisition.height;
824       else
825         inquery->width += child_requisition.width;
826     }
827
828   /* report effective widget size */
829   inquery->width += item_area.width + 2 * border_width;
830   inquery->height += item_area.height + 2 * border_width;
831 }
832
833 static void
834 gtk_tool_item_group_real_size_allocate (GtkWidget      *widget,
835                                         GtkAllocation  *allocation)
836 {
837   const gint border_width = GTK_CONTAINER (widget)->border_width;
838   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget);
839   GtkToolItemGroupPrivate* priv = group->priv;
840   GtkRequisition child_requisition;
841   GtkAllocation child_allocation;
842
843   GtkRequisition item_size;
844   GtkAllocation item_area;
845
846   GtkOrientation orientation;
847   GtkToolbarStyle style;
848
849   GList *it;
850
851   gint n_columns, n_rows = 1;
852   gint min_rows;
853
854   GtkTextDirection direction = gtk_widget_get_direction (widget);
855
856   orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
857   style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group));
858
859   /* chain up */
860   GTK_WIDGET_CLASS (gtk_tool_item_group_parent_class)->size_allocate (widget, allocation);
861
862   child_allocation.x = border_width;
863   child_allocation.y = border_width;
864
865   /* place the header widget */
866   if (GTK_WIDGET_VISIBLE (priv->header))
867     {
868       gtk_widget_size_request (priv->header, &child_requisition);
869
870       if (GTK_ORIENTATION_VERTICAL == orientation)
871         {
872           child_allocation.width = allocation->width;
873           child_allocation.height = child_requisition.height;
874         }
875       else
876         {
877           child_allocation.width = child_requisition.width;
878           child_allocation.height = allocation->height;
879
880           if (GTK_TEXT_DIR_RTL == direction)
881             child_allocation.x = allocation->width - border_width - child_allocation.width;
882         }
883
884       gtk_widget_size_allocate (priv->header, &child_allocation);
885
886       if (GTK_ORIENTATION_VERTICAL == orientation)
887         child_allocation.y += child_allocation.height;
888       else if (GTK_TEXT_DIR_RTL != direction)
889         child_allocation.x += child_allocation.width;
890       else
891         child_allocation.x = border_width;
892     }
893   else
894     child_requisition.width = child_requisition.height = 0;
895
896   /* figure out the size of homogeneous items */
897   gtk_tool_item_group_get_item_size (group, &item_size, TRUE, &min_rows);
898
899   /* figure out the available columns and size of item_area */
900   if (GTK_ORIENTATION_VERTICAL == orientation)
901     {
902       item_size.width = MIN (item_size.width, allocation->width);
903
904       item_area.width = allocation->width - 2 * border_width;
905       item_area.height = allocation->height - 2 * border_width - child_requisition.height;
906
907       n_columns = MAX (item_area.width / item_size.width, 1);
908
909       item_size.width = item_area.width / n_columns;
910     }
911   else
912     {
913       item_size.height = MIN (item_size.height, allocation->height);
914
915       item_area.width = allocation->width - 2 * border_width - child_requisition.width;
916       item_area.height = allocation->height - 2 * border_width;
917
918       n_columns = MAX (item_area.width / item_size.width, 1);
919       n_rows = MAX (item_area.height / item_size.height, min_rows);
920
921       item_size.height = item_area.height / n_rows;
922     }
923
924   item_area.x = child_allocation.x;
925   item_area.y = child_allocation.y;
926
927   /* when expanded or in transition, place the tool items in a grid like layout */
928   if (!priv->collapsed || !priv->animation || priv->animation_timeout)
929     {
930       gint col = 0, row = 0;
931
932       for (it = priv->children; it != NULL; it = it->next)
933         {
934           GtkToolItemGroupChild *child = it->data;
935           gint col_child;
936
937           if (!gtk_tool_item_group_is_item_visible (group, child))
938             {
939               gtk_widget_set_child_visible (GTK_WIDGET (child->item), FALSE);
940
941               continue;
942             }
943
944           /* for non homogeneous widgets request the required size */
945           child_requisition.width = 0;
946
947           if (!child->homogeneous)
948             {
949               gtk_widget_size_request (GTK_WIDGET (child->item), &child_requisition);
950               child_requisition.width = MIN (child_requisition.width, item_area.width);
951             }
952
953           /* select next row if at end of row */
954           if (col > 0 && (child->new_row || (col * item_size.width) + MAX (child_requisition.width, item_size.width) > item_area.width))
955             {
956               row++;
957               col = 0;
958               child_allocation.y += child_allocation.height;
959             }
960
961           col_child = col;
962
963           /* calculate the position and size of the item */
964           if (!child->homogeneous)
965             {
966               gint col_width;
967               gint width;
968
969               if (!child->expand)
970                 col_width = udiv (child_requisition.width, item_size.width);
971               else
972                 col_width = n_columns - col;
973
974               width = col_width * item_size.width;
975
976               if (GTK_TEXT_DIR_RTL == direction)
977                 col_child = (n_columns - col - col_width);
978
979               if (child->fill)
980                 {
981                   child_allocation.x = item_area.x + col_child * item_size.width;
982                   child_allocation.width = width;
983                 }
984               else
985                 {
986                   child_allocation.x =
987                     (item_area.x + col_child * item_size.width +
988                     (width - child_requisition.width) / 2);
989                   child_allocation.width = child_requisition.width;
990                 }
991
992               col += col_width;
993             }
994           else
995             {
996               if (GTK_TEXT_DIR_RTL == direction)
997                 col_child = (n_columns - col - 1);
998
999               child_allocation.x = item_area.x + col_child * item_size.width;
1000               child_allocation.width = item_size.width;
1001
1002               col++;
1003             }
1004
1005           child_allocation.height = item_size.height;
1006
1007           gtk_widget_size_allocate (GTK_WIDGET (child->item), &child_allocation);
1008           gtk_widget_set_child_visible (GTK_WIDGET (child->item), TRUE);
1009         }
1010
1011       child_allocation.y += item_size.height;
1012     }
1013
1014   /* or just hide all items, when collapsed */
1015
1016   else
1017     {
1018       for (it = priv->children; it != NULL; it = it->next)
1019         {
1020           GtkToolItemGroupChild *child = it->data;
1021
1022           gtk_widget_set_child_visible (GTK_WIDGET (child->item), FALSE);
1023         }
1024     }
1025 }
1026
1027 static void
1028 gtk_tool_item_group_size_allocate (GtkWidget     *widget,
1029                                    GtkAllocation *allocation)
1030 {
1031   gtk_tool_item_group_real_size_allocate (widget, allocation);
1032
1033   if (GTK_WIDGET_MAPPED (widget))
1034     gdk_window_invalidate_rect (widget->window, NULL, FALSE);
1035 }
1036
1037 static void
1038 gtk_tool_item_group_set_focus_cb (GtkWidget *window,
1039                                   GtkWidget *widget,
1040                                   gpointer   user_data)
1041 {
1042   GtkAdjustment *adjustment;
1043   GtkWidget *p;
1044
1045   /* Find this group's parent widget in the focused widget's anchestry. */
1046   for (p = widget; p; p = gtk_widget_get_parent (p))
1047     if (p == user_data)
1048       {
1049         p = gtk_widget_get_parent (p);
1050         break;
1051       }
1052
1053   if (GTK_IS_TOOL_PALETTE (p))
1054     {
1055       /* Check that the focused widgets is fully visible within
1056        * the group's parent widget and make it visible otherwise. */
1057
1058       adjustment = gtk_tool_palette_get_hadjustment (GTK_TOOL_PALETTE (p));
1059       adjustment = gtk_tool_palette_get_vadjustment (GTK_TOOL_PALETTE (p));
1060
1061       if (adjustment)
1062         {
1063           int y;
1064
1065           /* Handle vertical adjustment. */
1066           if (gtk_widget_translate_coordinates
1067                 (widget, p, 0, 0, NULL, &y) && y < 0)
1068             {
1069               y += adjustment->value;
1070               gtk_adjustment_clamp_page (adjustment, y, y + widget->allocation.height);
1071             }
1072           else if (gtk_widget_translate_coordinates
1073                       (widget, p, 0, widget->allocation.height, NULL, &y) &&
1074                    y > p->allocation.height)
1075             {
1076               y += adjustment->value;
1077               gtk_adjustment_clamp_page (adjustment, y - widget->allocation.height, y);
1078             }
1079         }
1080
1081       adjustment = gtk_tool_palette_get_hadjustment (GTK_TOOL_PALETTE (p));
1082
1083       if (adjustment)
1084         {
1085           int x;
1086
1087           /* Handle horizontal adjustment. */
1088           if (gtk_widget_translate_coordinates
1089                 (widget, p, 0, 0, &x, NULL) && x < 0)
1090             {
1091               x += adjustment->value;
1092               gtk_adjustment_clamp_page (adjustment, x, x + widget->allocation.width);
1093             }
1094           else if (gtk_widget_translate_coordinates
1095                       (widget, p, widget->allocation.width, 0, &x, NULL) &&
1096                    x > p->allocation.width)
1097             {
1098               x += adjustment->value;
1099               gtk_adjustment_clamp_page (adjustment, x - widget->allocation.width, x);
1100             }
1101
1102           return;
1103         }
1104     }
1105 }
1106
1107 static void
1108 gtk_tool_item_group_set_toplevel_window (GtkToolItemGroup *group,
1109                                          GtkWidget        *toplevel)
1110 {
1111   GtkToolItemGroupPrivate* priv = group->priv;
1112
1113   if (toplevel != priv->toplevel)
1114     {
1115       if (priv->toplevel)
1116         {
1117           /* Disconnect focus tracking handler. */
1118           g_signal_handler_disconnect (priv->toplevel,
1119                                        priv->focus_set_id);
1120
1121           priv->focus_set_id = 0;
1122           priv->toplevel = NULL;
1123         }
1124
1125       if (toplevel)
1126         {
1127           /* Install focus tracking handler. We connect to the window's
1128            * set-focus signal instead of connecting to the focus signal of
1129            * each child to:
1130            *
1131            * 1) Reduce the number of signal handlers used.
1132            * 2) Avoid special handling for group headers.
1133            * 3) Catch focus grabs not only for direct children,
1134            *    but also for nested widgets.
1135            */
1136           priv->focus_set_id =
1137             g_signal_connect (toplevel, "set-focus",
1138                               G_CALLBACK (gtk_tool_item_group_set_focus_cb),
1139                               group);
1140
1141           priv->toplevel = toplevel;
1142         }
1143     }
1144 }
1145
1146 static void
1147 gtk_tool_item_group_realize (GtkWidget *widget)
1148 {
1149   GtkWidget *toplevel_window;
1150   const gint border_width = GTK_CONTAINER (widget)->border_width;
1151   gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1152   GdkWindowAttr attributes;
1153   GdkDisplay *display;
1154
1155   attributes.window_type = GDK_WINDOW_CHILD;
1156   attributes.x = widget->allocation.x + border_width;
1157   attributes.y = widget->allocation.y + border_width;
1158   attributes.width = widget->allocation.width - border_width * 2;
1159   attributes.height = widget->allocation.height - border_width * 2;
1160   attributes.wclass = GDK_INPUT_OUTPUT;
1161   attributes.visual = gtk_widget_get_visual (widget);
1162   attributes.colormap = gtk_widget_get_colormap (widget);
1163   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK
1164                         | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1165                         | GDK_BUTTON_MOTION_MASK;
1166
1167   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1168                                    &attributes, attributes_mask);
1169
1170   display = gdk_drawable_get_display (widget->window);
1171
1172   if (gdk_display_supports_composite (display))
1173     gdk_window_set_composited (widget->window, TRUE);
1174
1175   gdk_window_set_user_data (widget->window, widget);
1176   widget->style = gtk_style_attach (widget->style, widget->window);
1177   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1178   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1179
1180   gtk_container_forall (GTK_CONTAINER (widget),
1181                         (GtkCallback) gtk_widget_set_parent_window,
1182                         widget->window);
1183
1184   gtk_widget_queue_resize_no_redraw (widget);
1185
1186   toplevel_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
1187   gtk_tool_item_group_set_toplevel_window (GTK_TOOL_ITEM_GROUP (widget),
1188                                            toplevel_window);
1189 }
1190
1191 static void
1192 gtk_tool_item_group_unrealize (GtkWidget *widget)
1193 {
1194   gtk_tool_item_group_set_toplevel_window (GTK_TOOL_ITEM_GROUP (widget), NULL);
1195   GTK_WIDGET_CLASS (gtk_tool_item_group_parent_class)->unrealize (widget);
1196 }
1197
1198 static void
1199 gtk_tool_item_group_style_set (GtkWidget *widget,
1200                                GtkStyle  *previous_style)
1201 {
1202   gtk_tool_item_group_header_adjust_style (GTK_TOOL_ITEM_GROUP (widget));
1203   GTK_WIDGET_CLASS (gtk_tool_item_group_parent_class)->style_set (widget, previous_style);
1204 }
1205
1206 static void
1207 gtk_tool_item_group_add (GtkContainer *container,
1208                          GtkWidget    *widget)
1209 {
1210   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (container));
1211   g_return_if_fail (GTK_IS_TOOL_ITEM (widget));
1212
1213   gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (container),
1214                               GTK_TOOL_ITEM (widget), -1);
1215 }
1216
1217 static void
1218 gtk_tool_item_group_remove (GtkContainer *container,
1219                             GtkWidget    *child)
1220 {
1221   GtkToolItemGroup *group;
1222   GtkToolItemGroupPrivate* priv;
1223   GList *it;
1224
1225   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (container));
1226   group = GTK_TOOL_ITEM_GROUP (container);
1227   priv = group->priv;
1228
1229   for (it = priv->children; it != NULL; it = it->next)
1230     {
1231       GtkToolItemGroupChild *child_info = it->data;
1232
1233       if ((GtkWidget *)child_info->item == child)
1234         {
1235           g_object_unref (child);
1236           gtk_widget_unparent (child);
1237
1238           g_free (child_info);
1239           priv->children = g_list_delete_link (priv->children, it);
1240
1241           gtk_widget_queue_resize (GTK_WIDGET (container));
1242           break;
1243         }
1244     }
1245 }
1246
1247 static void
1248 gtk_tool_item_group_forall (GtkContainer *container,
1249                             gboolean      internals,
1250                             GtkCallback   callback,
1251                             gpointer      callback_data)
1252 {
1253   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (container);
1254   GtkToolItemGroupPrivate* priv = group->priv;
1255   GList *children;
1256
1257   if (internals && priv->header)
1258     callback (priv->header, callback_data);
1259
1260   children = priv->children;
1261   while (children)
1262     {
1263       GtkToolItemGroupChild *child = children->data;
1264       children = children->next; /* store pointer before call to callback
1265                                     because the child pointer is invalid if the
1266                                     child->item is removed from the item group
1267                                     in callback */
1268
1269       callback (GTK_WIDGET (child->item), callback_data);
1270     }
1271 }
1272
1273 static GType
1274 gtk_tool_item_group_child_type (GtkContainer *container)
1275 {
1276   return GTK_TYPE_TOOL_ITEM;
1277 }
1278
1279 static GtkToolItemGroupChild *
1280 gtk_tool_item_group_get_child (GtkToolItemGroup  *group,
1281                                GtkToolItem       *item,
1282                                gint              *position,
1283                                GList            **link)
1284 {
1285   guint i;
1286   GList *it;
1287
1288   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL);
1289   g_return_val_if_fail (GTK_IS_TOOL_ITEM (item), NULL);
1290
1291   for (it = group->priv->children, i = 0; it != NULL; it = it->next, ++i)
1292     {
1293       GtkToolItemGroupChild *child = it->data;
1294
1295       if (child->item == item)
1296         {
1297           if (position)
1298             *position = i;
1299
1300           if (link)
1301             *link = it;
1302
1303           return child;
1304         }
1305     }
1306
1307   return NULL;
1308 }
1309
1310 static void
1311 gtk_tool_item_group_get_item_packing (GtkToolItemGroup *group,
1312                                       GtkToolItem      *item,
1313                                       gboolean         *homogeneous,
1314                                       gboolean         *expand,
1315                                       gboolean         *fill,
1316                                       gboolean         *new_row)
1317 {
1318   GtkToolItemGroupChild *child;
1319
1320   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1321   g_return_if_fail (GTK_IS_TOOL_ITEM (item));
1322
1323   child = gtk_tool_item_group_get_child (group, item, NULL, NULL);
1324   if (!child)
1325     return;
1326
1327   if (expand)
1328     *expand = child->expand;
1329
1330   if (homogeneous)
1331     *homogeneous = child->homogeneous;
1332
1333   if (fill)
1334     *fill = child->fill;
1335
1336   if (new_row)
1337     *new_row = child->new_row;
1338 }
1339
1340 static void
1341 gtk_tool_item_group_set_item_packing (GtkToolItemGroup *group,
1342                                       GtkToolItem      *item,
1343                                       gboolean          homogeneous,
1344                                       gboolean          expand,
1345                                       gboolean          fill,
1346                                       gboolean          new_row)
1347 {
1348   GtkToolItemGroupChild *child;
1349   gboolean changed = FALSE;
1350
1351   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1352   g_return_if_fail (GTK_IS_TOOL_ITEM (item));
1353
1354   child = gtk_tool_item_group_get_child (group, item, NULL, NULL);
1355   if (!child)
1356     return;
1357
1358   gtk_widget_freeze_child_notify (GTK_WIDGET (item));
1359
1360   if (child->homogeneous != homogeneous)
1361     {
1362       child->homogeneous = homogeneous;
1363       changed = TRUE;
1364       gtk_widget_child_notify (GTK_WIDGET (item), "homogeneous");
1365     }
1366   if (child->expand != expand)
1367     {
1368       child->expand = expand;
1369       changed = TRUE;
1370       gtk_widget_child_notify (GTK_WIDGET (item), "expand");
1371     }
1372   if (child->fill != fill)
1373     {
1374       child->fill = fill;
1375       changed = TRUE;
1376       gtk_widget_child_notify (GTK_WIDGET (item), "fill");
1377     }
1378   if (child->new_row != new_row)
1379     {
1380       child->new_row = new_row;
1381       changed = TRUE;
1382       gtk_widget_child_notify (GTK_WIDGET (item), "new-row");
1383     }
1384
1385   gtk_widget_thaw_child_notify (GTK_WIDGET (item));
1386
1387   if (changed && GTK_WIDGET_VISIBLE (group) && GTK_WIDGET_VISIBLE (item))
1388     gtk_widget_queue_resize (GTK_WIDGET (group));
1389 }
1390
1391 static void
1392 gtk_tool_item_group_set_child_property (GtkContainer *container,
1393                                         GtkWidget    *child,
1394                                         guint         prop_id,
1395                                         const GValue *value,
1396                                         GParamSpec   *pspec)
1397 {
1398   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (container);
1399   GtkToolItem *item = GTK_TOOL_ITEM (child);
1400   gboolean homogeneous, expand, fill, new_row;
1401
1402   if (prop_id != CHILD_PROP_POSITION)
1403     gtk_tool_item_group_get_item_packing (group, item,
1404                                           &homogeneous,
1405                                           &expand,
1406                                           &fill,
1407                                           &new_row);
1408
1409   switch (prop_id)
1410     {
1411       case CHILD_PROP_HOMOGENEOUS:
1412         gtk_tool_item_group_set_item_packing (group, item,
1413                                               g_value_get_boolean (value),
1414                                               expand,
1415                                               fill,
1416                                               new_row);
1417         break;
1418
1419       case CHILD_PROP_EXPAND:
1420         gtk_tool_item_group_set_item_packing (group, item,
1421                                               homogeneous,
1422                                               g_value_get_boolean (value),
1423                                               fill,
1424                                               new_row);
1425         break;
1426
1427       case CHILD_PROP_FILL:
1428         gtk_tool_item_group_set_item_packing (group, item,
1429                                               homogeneous,
1430                                               expand,
1431                                               g_value_get_boolean (value),
1432                                               new_row);
1433         break;
1434
1435       case CHILD_PROP_NEW_ROW:
1436         gtk_tool_item_group_set_item_packing (group, item,
1437                                               homogeneous,
1438                                               expand,
1439                                               fill,
1440                                               g_value_get_boolean (value));
1441         break;
1442
1443       case CHILD_PROP_POSITION:
1444         gtk_tool_item_group_set_item_position (group, item, g_value_get_int (value));
1445         break;
1446
1447       default:
1448         GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
1449         break;
1450     }
1451 }
1452
1453 static void
1454 gtk_tool_item_group_get_child_property (GtkContainer *container,
1455                                         GtkWidget    *child,
1456                                         guint         prop_id,
1457                                         GValue       *value,
1458                                         GParamSpec   *pspec)
1459 {
1460   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (container);
1461   GtkToolItem *item = GTK_TOOL_ITEM (child);
1462   gboolean homogeneous, expand, fill, new_row;
1463
1464   if (prop_id != CHILD_PROP_POSITION)
1465     gtk_tool_item_group_get_item_packing (group, item,
1466                                           &homogeneous,
1467                                           &expand,
1468                                           &fill,
1469                                           &new_row);
1470
1471   switch (prop_id)
1472     {
1473       case CHILD_PROP_HOMOGENEOUS:
1474         g_value_set_boolean (value, homogeneous);
1475         break;
1476
1477        case CHILD_PROP_EXPAND:
1478         g_value_set_boolean (value, expand);
1479         break;
1480
1481        case CHILD_PROP_FILL:
1482         g_value_set_boolean (value, fill);
1483         break;
1484
1485        case CHILD_PROP_NEW_ROW:
1486         g_value_set_boolean (value, new_row);
1487         break;
1488
1489      case CHILD_PROP_POSITION:
1490         g_value_set_int (value, gtk_tool_item_group_get_item_position (group, item));
1491         break;
1492
1493       default:
1494         GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
1495         break;
1496     }
1497 }
1498
1499 static void
1500 gtk_tool_item_group_class_init (GtkToolItemGroupClass *cls)
1501 {
1502   GObjectClass       *oclass = G_OBJECT_CLASS (cls);
1503   GtkWidgetClass     *wclass = GTK_WIDGET_CLASS (cls);
1504   GtkContainerClass  *cclass = GTK_CONTAINER_CLASS (cls);
1505
1506   oclass->set_property       = gtk_tool_item_group_set_property;
1507   oclass->get_property       = gtk_tool_item_group_get_property;
1508   oclass->finalize           = gtk_tool_item_group_finalize;
1509   oclass->dispose            = gtk_tool_item_group_dispose;
1510
1511   wclass->size_request       = gtk_tool_item_group_size_request;
1512   wclass->size_allocate      = gtk_tool_item_group_size_allocate;
1513   wclass->realize            = gtk_tool_item_group_realize;
1514   wclass->unrealize          = gtk_tool_item_group_unrealize;
1515   wclass->style_set          = gtk_tool_item_group_style_set;
1516   wclass->screen_changed     = gtk_tool_item_group_screen_changed;
1517
1518   cclass->add                = gtk_tool_item_group_add;
1519   cclass->remove             = gtk_tool_item_group_remove;
1520   cclass->forall             = gtk_tool_item_group_forall;
1521   cclass->child_type         = gtk_tool_item_group_child_type;
1522   cclass->set_child_property = gtk_tool_item_group_set_child_property;
1523   cclass->get_child_property = gtk_tool_item_group_get_child_property;
1524
1525   g_object_class_install_property (oclass, PROP_LABEL,
1526                                    g_param_spec_string ("label",
1527                                                         P_("Label"),
1528                                                         P_("The human-readable title of this item group"),
1529                                                         DEFAULT_LABEL,
1530                                                         G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1531                                                         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1532
1533   g_object_class_install_property (oclass, PROP_LABEL_WIDGET,
1534                                    g_param_spec_object  ("label-widget",
1535                                                         P_("Label widget"),
1536                                                         P_("A widget to display in place of the usual label"),
1537                                                         GTK_TYPE_WIDGET,
1538                                                         G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1539                                                         G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1540
1541   g_object_class_install_property (oclass, PROP_COLLAPSED,
1542                                    g_param_spec_boolean ("collapsed",
1543                                                          P_("Collapsed"),
1544                                                          P_("Wether the group has been collapsed and items are hidden"),
1545                                                          DEFAULT_COLLAPSED,
1546                                                          G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1547                                                          G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1548
1549   g_object_class_install_property (oclass, PROP_ELLIPSIZE,
1550                                    g_param_spec_enum ("ellipsize",
1551                                                       P_("ellipsize"),
1552                                                       P_("Ellipsize for item group headers"),
1553                                                       PANGO_TYPE_ELLIPSIZE_MODE, DEFAULT_ELLIPSIZE,
1554                                                       G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1555                                                       G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1556
1557   g_object_class_install_property (oclass, PROP_RELIEF,
1558                                    g_param_spec_enum ("header-relief",
1559                                                       P_("Header Relief"),
1560                                                       P_("Relief of the group header button"),
1561                                                       GTK_TYPE_RELIEF_STYLE, GTK_RELIEF_NORMAL,
1562                                                       G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1563                                                       G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1564
1565   gtk_widget_class_install_style_property (wclass,
1566                                            g_param_spec_int ("expander-size",
1567                                                              P_("Expander Size"),
1568                                                              P_("Size of the expander arrow"),
1569                                                              0,
1570                                                              G_MAXINT,
1571                                                              DEFAULT_EXPANDER_SIZE,
1572                                                              G_PARAM_READABLE | G_PARAM_STATIC_NAME |
1573                                                              G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1574
1575   gtk_widget_class_install_style_property (wclass,
1576                                            g_param_spec_int ("header-spacing",
1577                                                              P_("Header Spacing"),
1578                                                              P_("Spacing between expander arrow and caption"),
1579                                                              0,
1580                                                              G_MAXINT,
1581                                                              DEFAULT_HEADER_SPACING,
1582                                                              G_PARAM_READABLE | G_PARAM_STATIC_NAME |
1583                                                              G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1584
1585   gtk_container_class_install_child_property (cclass, CHILD_PROP_HOMOGENEOUS,
1586                                               g_param_spec_boolean ("homogeneous",
1587                                                                     P_("Homogeneous"),
1588                                                                     P_("Whether the item should be the same size as other homogeneous items"),
1589                                                                     TRUE,
1590                                                                     G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1591                                                                     G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1592
1593   gtk_container_class_install_child_property (cclass, CHILD_PROP_EXPAND,
1594                                               g_param_spec_boolean ("expand",
1595                                                                     P_("Expand"),
1596                                                                     P_("Whether the item should receive extra space when the toolbar grows"),
1597                                                                     FALSE,
1598                                                                     G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1599                                                                     G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1600
1601   gtk_container_class_install_child_property (cclass, CHILD_PROP_FILL,
1602                                               g_param_spec_boolean ("fill",
1603                                                                     P_("Fill"),
1604                                                                     P_("Whether the item should fill the avaiable space"),
1605                                                                     TRUE,
1606                                                                     G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1607                                                                     G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1608
1609   gtk_container_class_install_child_property (cclass, CHILD_PROP_NEW_ROW,
1610                                               g_param_spec_boolean ("new-row",
1611                                                                     P_("New Row"),
1612                                                                     P_("Whether the item should start a new row"),
1613                                                                     FALSE,
1614                                                                     G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1615                                                                     G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1616
1617   gtk_container_class_install_child_property (cclass, CHILD_PROP_POSITION,
1618                                               g_param_spec_int ("position",
1619                                                                 P_("Position"),
1620                                                                 P_("Position of the item within this group"),
1621                                                                 0,
1622                                                                 G_MAXINT,
1623                                                                 0,
1624                                                                 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
1625                                                                 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1626
1627   g_type_class_add_private (cls, sizeof (GtkToolItemGroupPrivate));
1628 }
1629
1630 /**
1631  * gtk_tool_item_group_new:
1632  * @label: the label of the new group.
1633  *
1634  * Creates a new tool item group with label @label.
1635  *
1636  * Returns: a new #GtkToolItemGroup.
1637  *
1638  * Since: 2.20
1639  */
1640 GtkWidget*
1641 gtk_tool_item_group_new (const gchar *label)
1642 {
1643   return g_object_new (GTK_TYPE_TOOL_ITEM_GROUP, "label",
1644     label, NULL);
1645 }
1646
1647 /**
1648  * gtk_tool_item_group_set_label:
1649  * @group: an #GtkToolItemGroup.
1650  * @label: the new human-readable label of of the group.
1651  *
1652  * Sets the label of the tool item group. The label is displayed in the header
1653  * of the group.
1654  *
1655  * Since: 2.20
1656  */
1657 void
1658 gtk_tool_item_group_set_label (GtkToolItemGroup *group,
1659                                const gchar      *label)
1660 {
1661   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1662
1663   if (!label)
1664     gtk_tool_item_group_set_label_widget (group, NULL);
1665   else
1666     {
1667       GtkWidget *child = gtk_label_new (label);
1668       gtk_widget_show (child);
1669
1670       gtk_tool_item_group_set_label_widget (group, child);
1671     }
1672
1673   g_object_notify (G_OBJECT (group), "label");
1674 }
1675
1676 /**
1677  * gtk_tool_item_group_set_label_widget:
1678  * @group: an #GtkToolItemGroup.
1679  * @widget: the new human-readable label of of the group.
1680  *
1681  * Sets the label of the tool item group. The label is displayed in the header
1682  * of the group.
1683  *
1684  * Since: 2.20
1685  */
1686 void
1687 gtk_tool_item_group_set_label_widget (GtkToolItemGroup *group,
1688                                       GtkWidget        *label_widget)
1689 {
1690   GtkToolItemGroupPrivate* priv;
1691   GtkWidget *alignment;
1692
1693   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1694   g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
1695   g_return_if_fail (label_widget == NULL || label_widget->parent == NULL);
1696
1697   priv = group->priv;
1698
1699   if (priv->label_widget == label_widget)
1700     return;
1701
1702   alignment = gtk_tool_item_group_get_alignment (group);
1703
1704   if (priv->label_widget)
1705     {
1706       gtk_widget_set_state (priv->label_widget, GTK_STATE_NORMAL);
1707       gtk_container_remove (GTK_CONTAINER (alignment), priv->label_widget);
1708     }
1709
1710
1711   if (label_widget)
1712       gtk_container_add (GTK_CONTAINER (alignment), label_widget);
1713
1714   priv->label_widget = label_widget;
1715
1716   if (GTK_WIDGET_VISIBLE (group))
1717     gtk_widget_queue_resize (GTK_WIDGET (group));
1718
1719   /* Only show the header widget if the group has children: */
1720   if (label_widget && priv->children)
1721     gtk_widget_show (priv->header);
1722   else
1723     gtk_widget_hide (priv->header);
1724
1725   g_object_freeze_notify (G_OBJECT (group));
1726   g_object_notify (G_OBJECT (group), "label-widget");
1727   g_object_notify (G_OBJECT (group), "label");
1728   g_object_thaw_notify (G_OBJECT (group));
1729 }
1730
1731 /**
1732  * gtk_tool_item_group_header_relief:
1733  * @group: an #GtkToolItemGroup.
1734  * @style: The GtkReliefStyle
1735  *
1736  * Set the button relief of the group header. See #gtk_button_set_relief for
1737  * details
1738  *
1739  * Since: 2.20
1740  */
1741 void
1742 gtk_tool_item_group_set_header_relief (GtkToolItemGroup   *group,
1743                                        GtkReliefStyle      style)
1744 {
1745   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1746   gtk_button_set_relief (GTK_BUTTON(group->priv->header), style);
1747 }
1748
1749 static gint64
1750 gtk_tool_item_group_get_animation_timestamp (GtkToolItemGroup *group)
1751 {
1752   GTimeVal now;
1753   g_source_get_current_time (group->priv->animation_timeout, &now);
1754   return (now.tv_sec * G_USEC_PER_SEC + now.tv_usec - group->priv->animation_start) / 1000;
1755 }
1756
1757 static void
1758 gtk_tool_item_group_force_expose (GtkToolItemGroup *group)
1759 {
1760   GtkToolItemGroupPrivate* priv = group->priv;
1761
1762   if (GTK_WIDGET_REALIZED (priv->header))
1763     {
1764       GtkWidget *alignment = gtk_tool_item_group_get_alignment (group);
1765       GdkRectangle area;
1766
1767       /* Find the header button's arrow area... */
1768       area.x = alignment->allocation.x;
1769       area.y = alignment->allocation.y + (alignment->allocation.height - priv->expander_size) / 2;
1770       area.height = priv->expander_size;
1771       area.width = priv->expander_size;
1772
1773       /* ... and invalidated it to get it animated. */
1774       gdk_window_invalidate_rect (priv->header->window, &area, TRUE);
1775     }
1776
1777   if (GTK_WIDGET_REALIZED (group))
1778     {
1779       GtkWidget *widget = GTK_WIDGET (group);
1780       GtkWidget *parent = gtk_widget_get_parent (widget);
1781       int x, y, width, height;
1782
1783       /* Find the tool item area button's arrow area... */
1784       width = widget->allocation.width;
1785       height = widget->allocation.height;
1786
1787       gtk_widget_translate_coordinates (widget, parent, 0, 0, &x, &y);
1788
1789       if (GTK_WIDGET_VISIBLE (priv->header))
1790         {
1791           height -= priv->header->allocation.height;
1792           y += priv->header->allocation.height;
1793         }
1794
1795       /* ... and invalidated it to get it animated. */
1796       gtk_widget_queue_draw_area (parent, x, y, width, height);
1797     }
1798 }
1799
1800 static gboolean
1801 gtk_tool_item_group_animation_cb (gpointer data)
1802 {
1803   GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data);
1804   GtkToolItemGroupPrivate* priv = group->priv;
1805   gint64 timestamp = gtk_tool_item_group_get_animation_timestamp (group);
1806   gboolean retval;
1807
1808   GDK_THREADS_ENTER();
1809
1810   /* Enque this early to reduce number of expose events. */
1811   gtk_widget_queue_resize_no_redraw (GTK_WIDGET (group));
1812
1813   /* Figure out current style of the expander arrow. */
1814   if (priv->collapsed)
1815     {
1816       if (priv->expander_style == GTK_EXPANDER_EXPANDED)
1817         priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
1818       else
1819         priv->expander_style = GTK_EXPANDER_COLLAPSED;
1820     }
1821   else
1822     {
1823       if (priv->expander_style == GTK_EXPANDER_COLLAPSED)
1824         priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
1825       else
1826         priv->expander_style = GTK_EXPANDER_EXPANDED;
1827     }
1828
1829   gtk_tool_item_group_force_expose (group);
1830
1831   /* Finish animation when done. */
1832   if (timestamp >= ANIMATION_DURATION)
1833     priv->animation_timeout = NULL;
1834
1835   /* Ensure that all composited windows and child windows are repainted, before
1836    * the parent widget gets its expose-event. This is needed to avoid heavy
1837    * rendering artifacts. GTK+ should take care about this issue by itself I
1838    * guess, but currently it doesn't. Also I don't understand the parameters
1839    * of this issue well enough yet, to file a bug report.
1840    */
1841   gdk_window_process_updates (GTK_WIDGET (group)->window, TRUE);
1842
1843   retval = (priv->animation_timeout != NULL);
1844
1845   GDK_THREADS_LEAVE();
1846
1847   return retval;
1848 }
1849
1850 /**
1851  * gtk_tool_item_group_set_collapsed:
1852  * @group: an #GtkToolItemGroup.
1853  * @collapsed: whether the @group should be collapsed or expanded.
1854  *
1855  * Sets whether the @group should be collapsed or expanded.
1856  *
1857  * Since: 2.20
1858  */
1859 void
1860 gtk_tool_item_group_set_collapsed (GtkToolItemGroup *group,
1861                                    gboolean          collapsed)
1862 {
1863   GtkWidget *parent;
1864   GtkToolItemGroupPrivate* priv;
1865
1866   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1867
1868   priv = group->priv;
1869
1870   parent = gtk_widget_get_parent (GTK_WIDGET (group));
1871   if (GTK_IS_TOOL_PALETTE (parent) && !collapsed)
1872     _gtk_tool_palette_set_expanding_child (GTK_TOOL_PALETTE (parent),
1873                                            GTK_WIDGET (group));
1874   if (collapsed != priv->collapsed)
1875     {
1876       if (priv->animation)
1877         {
1878           GTimeVal now;
1879
1880           g_get_current_time (&now);
1881
1882           if (priv->animation_timeout)
1883             g_source_destroy (priv->animation_timeout);
1884
1885           priv->animation_start = (now.tv_sec * G_USEC_PER_SEC + now.tv_usec);
1886           priv->animation_timeout = g_timeout_source_new (ANIMATION_TIMEOUT);
1887
1888           g_source_set_callback (priv->animation_timeout,
1889                                  gtk_tool_item_group_animation_cb,
1890                                  group, NULL);
1891
1892           g_source_attach (priv->animation_timeout, NULL);
1893         }
1894         else
1895         {
1896           priv->expander_style = GTK_EXPANDER_COLLAPSED;
1897           gtk_tool_item_group_force_expose (group);
1898         }
1899
1900       priv->collapsed = collapsed;
1901       g_object_notify (G_OBJECT (group), "collapsed");
1902     }
1903 }
1904
1905 /**
1906  * gtk_tool_item_group_set_ellipsize:
1907  * @group: an #GtkToolItemGroup.
1908  * @ellipsize: the #PangoEllipsizeMode labels in @group should use.
1909  *
1910  * Sets the ellipsization mode which should be used by labels in @group.
1911  *
1912  * Since: 2.20
1913  */
1914 void
1915 gtk_tool_item_group_set_ellipsize (GtkToolItemGroup   *group,
1916                                    PangoEllipsizeMode  ellipsize)
1917 {
1918   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1919
1920   if (ellipsize != group->priv->ellipsize)
1921     {
1922       group->priv->ellipsize = ellipsize;
1923       gtk_tool_item_group_header_adjust_style (group);
1924       g_object_notify (G_OBJECT (group), "ellipsize");
1925       _gtk_tool_item_group_palette_reconfigured (group);
1926     }
1927 }
1928
1929 /**
1930  * gtk_tool_item_group_get_label:
1931  * @group: an #GtkToolItemGroup.
1932  *
1933  * Gets the label of @group.
1934  *
1935  * Returns: the label of @group. The label is an internal string of @group and must not be modified.
1936  *          Note that NULL is returned if a custom label has been set with gtk_tool_item_group_set_label_widget()
1937  *
1938  * Since: 2.20
1939  */
1940 G_CONST_RETURN gchar*
1941 gtk_tool_item_group_get_label (GtkToolItemGroup *group)
1942 {
1943   GtkToolItemGroupPrivate *priv;
1944
1945   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL);
1946
1947   priv = group->priv;
1948
1949   if (GTK_IS_LABEL (priv->label_widget))
1950     return gtk_label_get_label (GTK_LABEL (priv->label_widget));
1951   else
1952     return NULL;
1953 }
1954
1955 /**
1956  * gtk_tool_item_group_get_label_widget:
1957  * @group: an #GtkToolItemGroup.
1958  *
1959  * Gets the label widget of @group.
1960  *
1961  * Returns: the label widget of @group. See gtk_tool_item_group_set_label_widget()
1962  *
1963  * Since: 2.20
1964  */
1965 GtkWidget*
1966 gtk_tool_item_group_get_label_widget (GtkToolItemGroup *group)
1967 {
1968   GtkWidget *alignment = gtk_tool_item_group_get_alignment (group);
1969   return gtk_bin_get_child (GTK_BIN (alignment));
1970 }
1971
1972 /**
1973  * gtk_tool_item_group_get_collapsed:
1974  * @group: an GtkToolItemGroup.
1975  *
1976  * Gets whether @group is collapsed or expanded.
1977  *
1978  * Returns: %TRUE if @group is collapsed, %FALSE if it is expanded.
1979  *
1980  * Since: 2.20
1981  */
1982 gboolean
1983 gtk_tool_item_group_get_collapsed (GtkToolItemGroup *group)
1984 {
1985   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_COLLAPSED);
1986   return group->priv->collapsed;
1987 }
1988
1989 /**
1990  * gtk_tool_item_group_get_ellipsize:
1991  * @group: an #GtkToolItemGroup.
1992  *
1993  * Gets the ellipsization mode of @group.
1994  *
1995  * Returns: the #PangoEllipsizeMode of @group.
1996  *
1997  * Since: 2.20
1998  */
1999 PangoEllipsizeMode
2000 gtk_tool_item_group_get_ellipsize (GtkToolItemGroup *group)
2001 {
2002   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_ELLIPSIZE);
2003   return group->priv->ellipsize;
2004 }
2005
2006 /**
2007  * gtk_tool_item_group_get_header_relief:
2008  * @group: an #GtkToolItemGroup.
2009  *
2010  * Gets the relief mode of the header button of @group.
2011  *
2012  * Returns: the #GtkReliefStyle
2013  *
2014  * Since: 2.20
2015  */
2016 GtkReliefStyle
2017 gtk_tool_item_group_get_header_relief (GtkToolItemGroup   *group)
2018 {
2019   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), GTK_RELIEF_NORMAL);
2020   return gtk_button_get_relief (GTK_BUTTON (group->priv->header));
2021 }
2022
2023 /**
2024  * gtk_tool_item_group_insert:
2025  * @group: an #GtkToolItemGroup.
2026  * @item: the #GtkToolItem to insert into @group.
2027  * @position: the position of @item in @group, starting with 0. The position -1 means end of list.
2028  *
2029  * Inserts @item at @position in the list of children of @group.
2030  *
2031  * Since: 2.20
2032  */
2033 void
2034 gtk_tool_item_group_insert (GtkToolItemGroup *group,
2035                             GtkToolItem      *item,
2036                             gint              position)
2037 {
2038   GtkWidget *parent, *child_widget;
2039   GtkToolItemGroupChild *child;
2040
2041   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
2042   g_return_if_fail (GTK_IS_TOOL_ITEM (item));
2043   g_return_if_fail (position >= -1);
2044
2045   parent = gtk_widget_get_parent (GTK_WIDGET (group));
2046
2047   child = g_new (GtkToolItemGroupChild, 1);
2048   child->item = g_object_ref_sink (item);
2049   child->homogeneous = TRUE;
2050   child->expand = FALSE;
2051   child->fill = TRUE;
2052   child->new_row = FALSE;
2053
2054   group->priv->children = g_list_insert (group->priv->children, child, position);
2055
2056   if (GTK_IS_TOOL_PALETTE (parent))
2057     _gtk_tool_palette_child_set_drag_source (GTK_WIDGET (item), parent);
2058
2059   child_widget = gtk_bin_get_child (GTK_BIN (item));
2060
2061   if (GTK_IS_BUTTON (child_widget))
2062     gtk_button_set_focus_on_click (GTK_BUTTON (child_widget), TRUE);
2063
2064   gtk_widget_set_parent (GTK_WIDGET (item), GTK_WIDGET (group));
2065 }
2066
2067 /**
2068  * gtk_tool_item_group_set_item_position:
2069  * @group: an #GtkToolItemGroup.
2070  * @item: the #GtkToolItem to move to a new position, should be a child of @group.
2071  * @position: the new position of @item in @group, starting with 0. The position -1 means end of list.
2072  *
2073  * Sets the position of @item in the list of children of @group.
2074  *
2075  * Since: 2.20
2076  */
2077 void
2078 gtk_tool_item_group_set_item_position (GtkToolItemGroup *group,
2079                                        GtkToolItem      *item,
2080                                        gint              position)
2081 {
2082   gint old_position;
2083   GList *link;
2084   GtkToolItemGroupChild *child;
2085   GtkToolItemGroupPrivate* priv;
2086
2087   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
2088   g_return_if_fail (GTK_IS_TOOL_ITEM (item));
2089
2090   g_return_if_fail (position >= -1);
2091
2092   child = gtk_tool_item_group_get_child (group, item, &old_position, &link);
2093   priv = group->priv;
2094
2095   g_return_if_fail (child != NULL);
2096
2097   if (position == old_position)
2098     return;
2099
2100   priv->children = g_list_delete_link (priv->children, link);
2101   priv->children = g_list_insert (priv->children, child, position);
2102
2103   gtk_widget_child_notify (GTK_WIDGET (item), "position");
2104   if (GTK_WIDGET_VISIBLE (group) && GTK_WIDGET_VISIBLE (item))
2105     gtk_widget_queue_resize (GTK_WIDGET (group));
2106 }
2107
2108 /**
2109  * gtk_tool_item_group_get_item_position:
2110  * @group: an #GtkToolItemGroup.
2111  * @item: a #GtkToolItem.
2112  *
2113  * Gets the position of @item in @group as index.
2114  *
2115  * Returns: the index of @item in @group or -1 if @item is no child of @group.
2116  *
2117  * Since: 2.20
2118  */
2119 gint
2120 gtk_tool_item_group_get_item_position (GtkToolItemGroup *group,
2121                                        GtkToolItem      *item)
2122 {
2123   gint position;
2124
2125   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), -1);
2126   g_return_val_if_fail (GTK_IS_TOOL_ITEM (item), -1);
2127
2128   if (gtk_tool_item_group_get_child (group, item, &position, NULL))
2129     return position;
2130
2131   return -1;
2132 }
2133
2134 /**
2135  * gtk_tool_item_group_get_n_items:
2136  * @group: an #GtkToolItemGroup.
2137  *
2138  * Gets the number of tool items in group.
2139  *
2140  * Returns: the number of tool items in group.
2141  *
2142  * Since: 2.20
2143  */
2144 guint
2145 gtk_tool_item_group_get_n_items (GtkToolItemGroup *group)
2146 {
2147   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), 0);
2148
2149   return g_list_length (group->priv->children);
2150 }
2151
2152 /**
2153  * gtk_tool_item_group_get_nth_item:
2154  * @group: an #GtkToolItemGroup.
2155  * @index: the index.
2156  *
2157  * Gets the tool item at index in group.
2158  *
2159  * Returns: the #GtkToolItem at index.
2160  *
2161  * Since: 2.20
2162  */
2163 GtkToolItem*
2164 gtk_tool_item_group_get_nth_item (GtkToolItemGroup *group,
2165                                   guint             index)
2166 {
2167   GtkToolItemGroupChild *child;
2168
2169   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL);
2170
2171   child = g_list_nth_data (group->priv->children, index);
2172
2173   return child != NULL ? child->item : NULL;
2174 }
2175
2176 /**
2177  * gtk_tool_item_group_get_drop_item:
2178  * @group: an #GtkToolItemGroup.
2179  * @x: the x position.
2180  * @y: the y position.
2181  *
2182  * Gets the tool item at position (x, y).
2183  *
2184  * Returns: the #GtkToolItem at position (x, y).
2185  *
2186  * Since: 2.20
2187  */
2188 GtkToolItem*
2189 gtk_tool_item_group_get_drop_item (GtkToolItemGroup *group,
2190                                    gint              x,
2191                                    gint              y)
2192 {
2193   GtkAllocation *allocation;
2194   GtkOrientation orientation;
2195   GList *it;
2196
2197   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL);
2198
2199   allocation = &GTK_WIDGET (group)->allocation;
2200   orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
2201
2202   g_return_val_if_fail (x >= 0 && x < allocation->width, NULL);
2203   g_return_val_if_fail (y >= 0 && y < allocation->height, NULL);
2204
2205   for (it = group->priv->children; it != NULL; it = it->next)
2206     {
2207       GtkToolItemGroupChild *child = it->data;
2208       GtkToolItem *item = child->item;
2209       gint x0, y0;
2210
2211       if (!item || !gtk_tool_item_group_is_item_visible (group, child))
2212         continue;
2213
2214       allocation = &GTK_WIDGET (item)->allocation;
2215
2216       x0 = x - allocation->x;
2217       y0 = y - allocation->y;
2218
2219       if (x0 >= 0 && x0 < allocation->width &&
2220           y0 >= 0 && y0 < allocation->height)
2221         return item;
2222     }
2223
2224   return NULL;
2225 }
2226
2227 void
2228 _gtk_tool_item_group_item_size_request (GtkToolItemGroup *group,
2229                                         GtkRequisition   *item_size,
2230                                         gboolean          homogeneous_only,
2231                                         gint             *requested_rows)
2232 {
2233   GtkRequisition child_requisition;
2234   GList *it;
2235   gint rows = 0;
2236   gboolean new_row = TRUE;
2237   GtkOrientation orientation;
2238   GtkToolbarStyle style;
2239
2240   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
2241   g_return_if_fail (NULL != item_size);
2242
2243   orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group));
2244   style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group));
2245
2246   item_size->width = item_size->height = 0;
2247
2248   for (it = group->priv->children; it != NULL; it = it->next)
2249     {
2250       GtkToolItemGroupChild *child = it->data;
2251
2252       if (!gtk_tool_item_group_is_item_visible (group, child))
2253         continue;
2254
2255       if (child->new_row || new_row)
2256         {
2257           rows++;
2258           new_row = FALSE;
2259         }
2260
2261       if (!child->homogeneous && child->expand)
2262           new_row = TRUE;
2263
2264       gtk_widget_size_request (GTK_WIDGET (child->item), &child_requisition);
2265
2266       if (!homogeneous_only || child->homogeneous)
2267         item_size->width = MAX (item_size->width, child_requisition.width);
2268       item_size->height = MAX (item_size->height, child_requisition.height);
2269     }
2270
2271   if (requested_rows)
2272     *requested_rows = rows;
2273 }
2274
2275 void
2276 _gtk_tool_item_group_paint (GtkToolItemGroup *group,
2277                             cairo_t          *cr)
2278 {
2279   GtkWidget *widget = GTK_WIDGET (group);
2280   GtkToolItemGroupPrivate* priv = group->priv;
2281
2282   gdk_cairo_set_source_pixmap (cr, widget->window,
2283                                widget->allocation.x,
2284                                widget->allocation.y);
2285
2286   if (priv->animation_timeout)
2287     {
2288       GtkOrientation orientation = gtk_tool_item_group_get_orientation (GTK_TOOL_SHELL (group));
2289       cairo_pattern_t *mask;
2290       gdouble v0, v1;
2291
2292       if (GTK_ORIENTATION_VERTICAL == orientation)
2293         v1 = widget->allocation.height;
2294       else
2295         v1 = widget->allocation.width;
2296
2297       v0 = v1 - 256;
2298
2299       if (!GTK_WIDGET_VISIBLE (priv->header))
2300         v0 = MAX (v0, 0);
2301       else if (GTK_ORIENTATION_VERTICAL == orientation)
2302         v0 = MAX (v0, priv->header->allocation.height);
2303       else
2304         v0 = MAX (v0, priv->header->allocation.width);
2305
2306       v1 = MIN (v0 + 256, v1);
2307
2308       if (GTK_ORIENTATION_VERTICAL == orientation)
2309         {
2310           v0 += widget->allocation.y;
2311           v1 += widget->allocation.y;
2312
2313           mask = cairo_pattern_create_linear (0.0, v0, 0.0, v1);
2314         }
2315       else
2316         {
2317           v0 += widget->allocation.x;
2318           v1 += widget->allocation.x;
2319
2320           mask = cairo_pattern_create_linear (v0, 0.0, v1, 0.0);
2321         }
2322
2323       cairo_pattern_add_color_stop_rgba (mask, 0.00, 0.0, 0.0, 0.0, 1.00);
2324       cairo_pattern_add_color_stop_rgba (mask, 0.25, 0.0, 0.0, 0.0, 0.25);
2325       cairo_pattern_add_color_stop_rgba (mask, 0.50, 0.0, 0.0, 0.0, 0.10);
2326       cairo_pattern_add_color_stop_rgba (mask, 0.75, 0.0, 0.0, 0.0, 0.01);
2327       cairo_pattern_add_color_stop_rgba (mask, 1.00, 0.0, 0.0, 0.0, 0.00);
2328
2329       cairo_mask (cr, mask);
2330       cairo_pattern_destroy (mask);
2331     }
2332   else
2333     cairo_paint (cr);
2334 }
2335
2336 gint
2337 _gtk_tool_item_group_get_size_for_limit (GtkToolItemGroup *group,
2338                                          gint              limit,
2339                                          gboolean          vertical,
2340                                          gboolean          animation)
2341 {
2342   GtkRequisition requisition;
2343   GtkToolItemGroupPrivate* priv = group->priv;
2344
2345   gtk_widget_size_request (GTK_WIDGET (group), &requisition);
2346
2347   if (!priv->collapsed || priv->animation_timeout)
2348     {
2349       GtkAllocation allocation = { 0, 0, requisition.width, requisition.height };
2350       GtkRequisition inquery;
2351
2352       if (vertical)
2353         allocation.width = limit;
2354       else
2355         allocation.height = limit;
2356
2357       gtk_tool_item_group_real_size_query (GTK_WIDGET (group),
2358                                            &allocation, &inquery);
2359
2360       if (vertical)
2361         inquery.height -= requisition.height;
2362       else
2363         inquery.width -= requisition.width;
2364
2365       if (priv->animation_timeout && animation)
2366         {
2367           gint64 timestamp = gtk_tool_item_group_get_animation_timestamp (group);
2368
2369           timestamp = MIN (timestamp, ANIMATION_DURATION);
2370
2371           if (priv->collapsed)
2372             timestamp = ANIMATION_DURATION - timestamp;
2373
2374           if (vertical)
2375             {
2376               inquery.height *= timestamp;
2377               inquery.height /= ANIMATION_DURATION;
2378             }
2379           else
2380             {
2381               inquery.width *= timestamp;
2382               inquery.width /= ANIMATION_DURATION;
2383             }
2384         }
2385
2386       if (vertical)
2387         requisition.height += inquery.height;
2388       else
2389         requisition.width += inquery.width;
2390     }
2391
2392   return (vertical ? requisition.height : requisition.width);
2393 }
2394
2395 gint
2396 _gtk_tool_item_group_get_height_for_width (GtkToolItemGroup *group,
2397                                            gint              width)
2398 {
2399   return _gtk_tool_item_group_get_size_for_limit (group, width, TRUE, group->priv->animation);
2400 }
2401
2402 gint
2403 _gtk_tool_item_group_get_width_for_height (GtkToolItemGroup *group,
2404                                            gint              height)
2405 {
2406   return _gtk_tool_item_group_get_size_for_limit (group, height, FALSE, TRUE);
2407 }
2408
2409 static void
2410 gtk_tool_palette_reconfigured_foreach_item (GtkWidget *child,
2411                                             gpointer   data)
2412 {
2413   if (GTK_IS_TOOL_ITEM (child))
2414     gtk_tool_item_toolbar_reconfigured (GTK_TOOL_ITEM (child));
2415 }
2416
2417
2418 void
2419 _gtk_tool_item_group_palette_reconfigured (GtkToolItemGroup *group)
2420 {
2421   gtk_container_foreach (GTK_CONTAINER (group),
2422                          gtk_tool_palette_reconfigured_foreach_item,
2423                          NULL);
2424
2425   gtk_tool_item_group_header_adjust_style (group);
2426 }