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