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