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