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