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