]> Pileus Git - ~andy/gtk/blob - gtk/gtktoolpalette.c
491cf9e9afef858e0f6bc72df5a6483540596988
[~andy/gtk] / gtk / gtktoolpalette.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  */
21
22 #include "config.h"
23
24 #include <string.h>
25 #include <gtk/gtk.h>
26
27 #include "gtktoolpaletteprivate.h"
28 #include "gtkmarshalers.h"
29 #include "gtktypebuiltins.h"
30 #include "gtkprivate.h"
31 #include "gtkscrollable.h"
32 #include "gtkintl.h"
33
34 #define DEFAULT_ICON_SIZE       GTK_ICON_SIZE_SMALL_TOOLBAR
35 #define DEFAULT_ORIENTATION     GTK_ORIENTATION_VERTICAL
36 #define DEFAULT_TOOLBAR_STYLE   GTK_TOOLBAR_ICONS
37
38 #define DEFAULT_CHILD_EXCLUSIVE FALSE
39 #define DEFAULT_CHILD_EXPAND    FALSE
40
41 /**
42  * SECTION:gtktoolpalette
43  * @Short_description: A tool palette with categories
44  * @Title: GtkToolPalette
45  *
46  * A #GtkToolPalette allows you to add #GtkToolItem<!-- -->s to a palette-like
47  * container with different categories and drag and drop support.
48  *
49  * A #GtkToolPalette is created with a call to gtk_tool_palette_new().
50  *
51  * #GtkToolItem<!-- -->s cannot be added directly to a #GtkToolPalette - 
52  * instead they are added to a #GtkToolItemGroup which can than be added
53  * to a #GtkToolPalette. To add a #GtkToolItemGroup to a #GtkToolPalette,
54  * use gtk_container_add().
55  *
56  * |[
57  * GtkWidget *palette, *group;
58  * GtkToolItem *item;
59  *
60  * palette = gtk_tool_palette_new ();
61  * group = gtk_tool_item_group_new (_("Test Category"));
62  * gtk_container_add (GTK_CONTAINER (palette), group);
63  *
64  * item = gtk_tool_button_new_from_stock (GTK_STOCK_OK);
65  * gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1);
66  * ]|
67  *
68  * The easiest way to use drag and drop with #GtkToolPalette is to call
69  * gtk_tool_palette_add_drag_dest() with the desired drag source @palette
70  * and the desired drag target @widget. Then gtk_tool_palette_get_drag_item()
71  * can be used to get the dragged item in the #GtkWidget::drag-data-received
72  * signal handler of the drag target.
73  *
74  * |[
75  * static void
76  * passive_canvas_drag_data_received (GtkWidget        *widget,
77  *                                    GdkDragContext   *context,
78  *                                    gint              x,
79  *                                    gint              y,
80  *                                    GtkSelectionData *selection,
81  *                                    guint             info,
82  *                                    guint             time,
83  *                                    gpointer          data)
84  * {
85  *   GtkWidget *palette;
86  *   GtkWidget *item;
87  *
88  *   /<!-- -->* Get the dragged item *<!-- -->/
89  *   palette = gtk_widget_get_ancestor (gtk_drag_get_source_widget (context),
90  *                                      GTK_TYPE_TOOL_PALETTE);
91  *   if (palette != NULL)
92  *     item = gtk_tool_palette_get_drag_item (GTK_TOOL_PALETTE (palette),
93  *                                            selection);
94  *
95  *   /<!-- -->* Do something with item *<!-- -->/
96  * }
97  *
98  * GtkWidget *target, palette;
99  *
100  * palette = gtk_tool_palette_new ();
101  * target = gtk_drawing_area_new ();
102  *
103  * g_signal_connect (G_OBJECT (target), "drag-data-received",
104  *                   G_CALLBACK (passive_canvas_drag_data_received), NULL);
105  * gtk_tool_palette_add_drag_dest (GTK_TOOL_PALETTE (palette), target,
106  *                                 GTK_DEST_DEFAULT_ALL,
107  *                                 GTK_TOOL_PALETTE_DRAG_ITEMS,
108  *                                 GDK_ACTION_COPY);
109  * ]|
110  *
111  * Since: 2.20
112  */
113
114 typedef struct _GtkToolItemGroupInfo   GtkToolItemGroupInfo;
115 typedef struct _GtkToolPaletteDragData GtkToolPaletteDragData;
116
117 enum
118 {
119   PROP_NONE,
120   PROP_ICON_SIZE,
121   PROP_ICON_SIZE_SET,
122   PROP_ORIENTATION,
123   PROP_TOOLBAR_STYLE,
124   PROP_HADJUSTMENT,
125   PROP_VADJUSTMENT,
126   PROP_HSCROLL_POLICY,
127   PROP_VSCROLL_POLICY
128 };
129
130 enum
131 {
132   CHILD_PROP_NONE,
133   CHILD_PROP_EXCLUSIVE,
134   CHILD_PROP_EXPAND,
135 };
136
137 struct _GtkToolItemGroupInfo
138 {
139   GtkToolItemGroup *widget;
140
141   gulong            notify_collapsed;
142   guint             pos;
143   guint             exclusive : 1;
144   guint             expand : 1;
145 };
146
147 struct _GtkToolPalettePrivate
148 {
149   GPtrArray* groups;
150
151   GtkAdjustment        *hadjustment;
152   GtkAdjustment        *vadjustment;
153
154   GtkIconSize           icon_size;
155   gboolean              icon_size_set;
156   GtkOrientation        orientation;
157   GtkToolbarStyle       style;
158   gboolean              style_set;
159
160   GtkWidget            *expanding_child;
161
162   GtkSizeGroup         *text_size_group;
163
164   GtkSettings          *settings;
165   gulong                settings_connection;
166
167   guint                 drag_source : 2;
168
169   /* GtkScrollablePolicy needs to be checked when
170    * driving the scrollable adjustment values */
171   guint hscroll_policy : 1;
172   guint vscroll_policy : 1;
173 };
174
175 struct _GtkToolPaletteDragData
176 {
177   GtkToolPalette *palette;
178   GtkWidget      *item;
179 };
180
181 static GdkAtom dnd_target_atom_item = GDK_NONE;
182 static GdkAtom dnd_target_atom_group = GDK_NONE;
183
184 static const GtkTargetEntry dnd_targets[] =
185 {
186   { "application/x-gtk-tool-palette-item", GTK_TARGET_SAME_APP, 0 },
187   { "application/x-gtk-tool-palette-group", GTK_TARGET_SAME_APP, 0 },
188 };
189
190 static void gtk_tool_palette_set_hadjustment (GtkToolPalette *palette,
191                                               GtkAdjustment  *adjustment);
192 static void gtk_tool_palette_set_vadjustment (GtkToolPalette *palette,
193                                               GtkAdjustment  *adjustment);
194
195
196 G_DEFINE_TYPE_WITH_CODE (GtkToolPalette,
197                gtk_tool_palette,
198                GTK_TYPE_CONTAINER,
199                G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
200                G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
201
202 static void
203 gtk_tool_palette_init (GtkToolPalette *palette)
204 {
205   palette->priv = G_TYPE_INSTANCE_GET_PRIVATE (palette,
206                                                GTK_TYPE_TOOL_PALETTE,
207                                                GtkToolPalettePrivate);
208
209   palette->priv->groups = g_ptr_array_sized_new (4);
210   g_ptr_array_set_free_func (palette->priv->groups, g_free);
211
212   palette->priv->icon_size = DEFAULT_ICON_SIZE;
213   palette->priv->icon_size_set = FALSE;
214   palette->priv->orientation = DEFAULT_ORIENTATION;
215   palette->priv->style = DEFAULT_TOOLBAR_STYLE;
216   palette->priv->style_set = FALSE;
217
218   palette->priv->text_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
219 }
220
221 static void
222 gtk_tool_palette_reconfigured (GtkToolPalette *palette)
223 {
224   guint i;
225
226   for (i = 0; i < palette->priv->groups->len; ++i)
227     {
228       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
229       if (info->widget)
230         _gtk_tool_item_group_palette_reconfigured (info->widget);
231     }
232
233   gtk_widget_queue_resize_no_redraw (GTK_WIDGET (palette));
234 }
235
236 static void
237 reset_orientation_style (GtkToolPalette *palette)
238 {
239   GtkStyleContext *context;
240
241   context = gtk_widget_get_style_context (GTK_WIDGET (palette));
242
243   if (palette->priv->orientation == GTK_ORIENTATION_VERTICAL)
244     {
245       gtk_style_context_add_class (context, GTK_STYLE_CLASS_VERTICAL);
246       gtk_style_context_remove_class (context, GTK_STYLE_CLASS_HORIZONTAL);
247     }
248   else
249     {
250       gtk_style_context_add_class (context, GTK_STYLE_CLASS_HORIZONTAL);
251       gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VERTICAL);
252     }
253 }
254
255 static void
256 gtk_tool_palette_set_property (GObject      *object,
257                                guint         prop_id,
258                                const GValue *value,
259                                GParamSpec   *pspec)
260 {
261   GtkToolPalette *palette = GTK_TOOL_PALETTE (object);
262
263   switch (prop_id)
264     {
265       case PROP_ICON_SIZE:
266         if ((guint) g_value_get_enum (value) != palette->priv->icon_size)
267           {
268             palette->priv->icon_size = g_value_get_enum (value);
269             gtk_tool_palette_reconfigured (palette);
270           }
271         break;
272
273       case PROP_ICON_SIZE_SET:
274         if ((guint) g_value_get_enum (value) != palette->priv->icon_size)
275           {
276             palette->priv->icon_size_set = g_value_get_enum (value);
277             gtk_tool_palette_reconfigured (palette);
278           }
279         break;
280
281       case PROP_ORIENTATION:
282         if ((guint) g_value_get_enum (value) != palette->priv->orientation)
283           {
284             palette->priv->orientation = g_value_get_enum (value);
285             reset_orientation_style (palette);
286             gtk_tool_palette_reconfigured (palette);
287           }
288         break;
289
290       case PROP_TOOLBAR_STYLE:
291         if ((guint) g_value_get_enum (value) != palette->priv->style)
292           {
293             palette->priv->style = g_value_get_enum (value);
294             gtk_tool_palette_reconfigured (palette);
295           }
296         break;
297
298       case PROP_HADJUSTMENT:
299         gtk_tool_palette_set_hadjustment (palette, g_value_get_object (value));
300         break;
301
302       case PROP_VADJUSTMENT:
303         gtk_tool_palette_set_vadjustment (palette, g_value_get_object (value));
304         break;
305
306       case PROP_HSCROLL_POLICY:
307         palette->priv->hscroll_policy = g_value_get_enum (value);
308         gtk_widget_queue_resize (GTK_WIDGET (palette));
309         break;
310
311       case PROP_VSCROLL_POLICY:
312         palette->priv->vscroll_policy = g_value_get_enum (value);
313         gtk_widget_queue_resize (GTK_WIDGET (palette));
314         break;
315
316       default:
317         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
318         break;
319     }
320 }
321
322 static void
323 gtk_tool_palette_get_property (GObject    *object,
324                                guint       prop_id,
325                                GValue     *value,
326                                GParamSpec *pspec)
327 {
328   GtkToolPalette *palette = GTK_TOOL_PALETTE (object);
329
330   switch (prop_id)
331     {
332       case PROP_ICON_SIZE:
333         g_value_set_enum (value, gtk_tool_palette_get_icon_size (palette));
334         break;
335
336       case PROP_ICON_SIZE_SET:
337         g_value_set_boolean (value, palette->priv->icon_size_set);
338         break;
339
340       case PROP_ORIENTATION:
341         g_value_set_enum (value, palette->priv->orientation);
342         break;
343
344       case PROP_TOOLBAR_STYLE:
345         g_value_set_enum (value, gtk_tool_palette_get_style (palette));
346         break;
347
348       case PROP_HADJUSTMENT:
349         g_value_set_object (value, palette->priv->hadjustment);
350         break;
351
352       case PROP_VADJUSTMENT:
353         g_value_set_object (value, palette->priv->vadjustment);
354         break;
355
356       case PROP_HSCROLL_POLICY:
357         g_value_set_enum (value, palette->priv->hscroll_policy);
358         break;
359
360       case PROP_VSCROLL_POLICY:
361         g_value_set_enum (value, palette->priv->vscroll_policy);
362         break;
363
364       default:
365         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
366         break;
367     }
368 }
369
370 static void
371 gtk_tool_palette_dispose (GObject *object)
372 {
373   GtkToolPalette *palette = GTK_TOOL_PALETTE (object);
374   guint i;
375
376   if (palette->priv->hadjustment)
377     {
378       g_object_unref (palette->priv->hadjustment);
379       palette->priv->hadjustment = NULL;
380     }
381
382   if (palette->priv->vadjustment)
383     {
384       g_object_unref (palette->priv->vadjustment);
385       palette->priv->vadjustment = NULL;
386     }
387
388   for (i = 0; i < palette->priv->groups->len; ++i)
389     {
390       GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
391
392       if (group->notify_collapsed)
393         {
394           g_signal_handler_disconnect (group->widget, group->notify_collapsed);
395           group->notify_collapsed = 0;
396         }
397     }
398
399   if (palette->priv->text_size_group)
400     {
401       g_object_unref (palette->priv->text_size_group);
402       palette->priv->text_size_group = NULL;
403     }
404
405   G_OBJECT_CLASS (gtk_tool_palette_parent_class)->dispose (object);
406 }
407
408 static void
409 gtk_tool_palette_finalize (GObject *object)
410 {
411   GtkToolPalette *palette = GTK_TOOL_PALETTE (object);
412
413   g_ptr_array_free (palette->priv->groups, TRUE);
414
415   G_OBJECT_CLASS (gtk_tool_palette_parent_class)->finalize (object);
416 }
417
418 static void
419 gtk_tool_palette_size_request (GtkWidget      *widget,
420                                GtkRequisition *requisition)
421 {
422   GtkToolPalette *palette = GTK_TOOL_PALETTE (widget);
423   GtkRequisition child_requisition;
424   guint border_width;
425   guint i;
426
427   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
428
429   requisition->width = 0;
430   requisition->height = 0;
431
432   for (i = 0; i < palette->priv->groups->len; ++i)
433     {
434       GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
435
436       if (!group->widget)
437         continue;
438
439       gtk_widget_get_preferred_size (GTK_WIDGET (group->widget),
440                                      &child_requisition, NULL);
441
442       if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
443         {
444           requisition->width = MAX (requisition->width, child_requisition.width);
445           requisition->height += child_requisition.height;
446         }
447       else
448         {
449           requisition->width += child_requisition.width;
450           requisition->height = MAX (requisition->height, child_requisition.height);
451         }
452     }
453
454   requisition->width += border_width * 2;
455   requisition->height += border_width * 2;
456 }
457
458 static void
459 gtk_tool_palette_get_preferred_width (GtkWidget *widget,
460                                       gint      *minimum,
461                                       gint      *natural)
462 {
463   GtkRequisition requisition;
464
465   gtk_tool_palette_size_request (widget, &requisition);
466
467   *minimum = *natural = requisition.width;
468 }
469
470 static void
471 gtk_tool_palette_get_preferred_height (GtkWidget *widget,
472                                        gint      *minimum,
473                                        gint      *natural)
474 {
475   GtkRequisition requisition;
476
477   gtk_tool_palette_size_request (widget, &requisition);
478
479   *minimum = *natural = requisition.height;
480 }
481
482
483 static void
484 gtk_tool_palette_size_allocate (GtkWidget     *widget,
485                                 GtkAllocation *allocation)
486 {
487   GtkToolPalette *palette = GTK_TOOL_PALETTE (widget);
488   GtkAdjustment *adjustment = NULL;
489   GtkAllocation child_allocation;
490
491   gint n_expand_groups = 0;
492   gint remaining_space = 0;
493   gint expand_space = 0;
494
495   gint page_start, page_size = 0;
496   gint offset = 0;
497   guint i;
498   guint border_width;
499
500   gint min_offset = -1, max_offset = -1;
501
502   gint x;
503
504   gint *group_sizes = g_newa (gint, palette->priv->groups->len);
505   GtkTextDirection direction;
506
507   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
508   direction = gtk_widget_get_direction (widget);
509
510   GTK_WIDGET_CLASS (gtk_tool_palette_parent_class)->size_allocate (widget, allocation);
511
512   if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
513     {
514       adjustment = palette->priv->vadjustment;
515       page_size = allocation->height;
516     }
517   else
518     {
519       adjustment = palette->priv->hadjustment;
520       page_size = allocation->width;
521     }
522
523   if (adjustment)
524     offset = gtk_adjustment_get_value (adjustment);
525   if (GTK_ORIENTATION_HORIZONTAL == palette->priv->orientation &&
526       GTK_TEXT_DIR_RTL == direction)
527     offset = -offset;
528
529   if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
530     child_allocation.width = allocation->width - border_width * 2;
531   else
532     child_allocation.height = allocation->height - border_width * 2;
533
534   if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
535     remaining_space = allocation->height;
536   else
537     remaining_space = allocation->width;
538
539   /* figure out the required size of all groups to be able to distribute the
540    * remaining space on allocation
541    */
542   for (i = 0; i < palette->priv->groups->len; ++i)
543     {
544       GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
545       gint size;
546
547       if (!group->widget)
548         continue;
549
550       widget = GTK_WIDGET (group->widget);
551
552       if (gtk_tool_item_group_get_n_items (group->widget))
553         {
554           if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
555             size = _gtk_tool_item_group_get_height_for_width (group->widget, child_allocation.width);
556           else
557             size = _gtk_tool_item_group_get_width_for_height (group->widget, child_allocation.height);
558
559           if (group->expand && !gtk_tool_item_group_get_collapsed (group->widget))
560             n_expand_groups += 1;
561         }
562       else
563         size = 0;
564
565       remaining_space -= size;
566       group_sizes[i] = size;
567
568       /* if the widget is currently expanding an offset which allows to
569        * display as much of the widget as possible is calculated
570        */
571       if (widget == palette->priv->expanding_child)
572         {
573           gint limit =
574             GTK_ORIENTATION_VERTICAL == palette->priv->orientation ?
575             child_allocation.width : child_allocation.height;
576
577           gint real_size;
578           guint j;
579
580           min_offset = 0;
581
582           for (j = 0; j < i; ++j)
583             min_offset += group_sizes[j];
584
585           max_offset = min_offset + group_sizes[i];
586
587           real_size = _gtk_tool_item_group_get_size_for_limit
588             (GTK_TOOL_ITEM_GROUP (widget), limit,
589              GTK_ORIENTATION_VERTICAL == palette->priv->orientation,
590              FALSE);
591
592           if (size == real_size)
593             palette->priv->expanding_child = NULL;
594         }
595     }
596
597   if (n_expand_groups > 0)
598     {
599       remaining_space = MAX (0, remaining_space);
600       expand_space = remaining_space / n_expand_groups;
601     }
602
603   if (max_offset != -1)
604     {
605       gint limit =
606         GTK_ORIENTATION_VERTICAL == palette->priv->orientation ?
607         allocation->height : allocation->width;
608
609       offset = MIN (MAX (offset, max_offset - limit), min_offset);
610     }
611
612   if (remaining_space > 0)
613     offset = 0;
614
615   x = border_width;
616   child_allocation.y = border_width;
617
618   if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
619     child_allocation.y -= offset;
620   else
621     x -= offset;
622
623   /* allocate all groups at the calculated positions */
624   for (i = 0; i < palette->priv->groups->len; ++i)
625     {
626       GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
627       GtkWidget *widget;
628
629       if (!group->widget)
630         continue;
631
632       widget = GTK_WIDGET (group->widget);
633
634       if (gtk_tool_item_group_get_n_items (group->widget))
635         {
636           gint size = group_sizes[i];
637
638           if (group->expand && !gtk_tool_item_group_get_collapsed (group->widget))
639             {
640               size += MIN (expand_space, remaining_space);
641               remaining_space -= expand_space;
642             }
643
644           if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
645             child_allocation.height = size;
646           else
647             child_allocation.width = size;
648
649           if (GTK_ORIENTATION_HORIZONTAL == palette->priv->orientation &&
650               GTK_TEXT_DIR_RTL == direction)
651             child_allocation.x = allocation->width - x - child_allocation.width;
652           else
653             child_allocation.x = x;
654
655           gtk_widget_size_allocate (widget, &child_allocation);
656           gtk_widget_show (widget);
657
658           if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
659             child_allocation.y += child_allocation.height;
660           else
661             x += child_allocation.width;
662         }
663       else
664         gtk_widget_hide (widget);
665     }
666
667   if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
668     {
669       child_allocation.y += border_width;
670       child_allocation.y += offset;
671
672       page_start = child_allocation.y;
673     }
674   else
675     {
676       x += border_width;
677       x += offset;
678
679       page_start = x;
680     }
681
682   /* update the scrollbar to match the displayed adjustment */
683   if (adjustment)
684     {
685       gdouble value, lower, upper;
686
687       if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation ||
688           GTK_TEXT_DIR_LTR == direction)
689         {
690           lower = 0;
691           upper = MAX (0, page_start);
692
693           value = MIN (offset, upper - page_size);
694           gtk_adjustment_clamp_page (adjustment, value, offset + page_size);
695         }
696       else
697         {
698           lower = page_size - MAX (0, page_start);
699           upper = page_size;
700
701           offset = -offset;
702
703           value = MAX (offset, lower);
704           gtk_adjustment_clamp_page (adjustment, offset, value + page_size);
705         }
706
707       gtk_adjustment_configure (adjustment,
708                                 value,
709                                 lower,
710                                 upper,
711                                 page_size * 0.1,
712                                 page_size * 0.9,
713                                 page_size);
714     }
715 }
716
717 static gboolean
718 gtk_tool_palette_draw (GtkWidget      *widget,
719                        cairo_t        *cr)
720 {
721   GtkToolPalette *palette = GTK_TOOL_PALETTE (widget);
722   GdkDisplay *display;
723   GdkWindow *window;
724   guint i;
725
726   window = gtk_widget_get_window (widget);
727
728   display = gdk_window_get_display (window);
729
730   if (!gdk_display_supports_composite (display))
731     return FALSE;
732
733   cairo_push_group (cr);
734
735   for (i = 0; i < palette->priv->groups->len; ++i)
736   {
737     GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
738     if (info->widget)
739       _gtk_tool_item_group_paint (info->widget, cr);
740   }
741
742   cairo_pop_group_to_source (cr);
743
744   cairo_paint (cr);
745
746   return FALSE;
747 }
748
749 static void
750 gtk_tool_palette_realize (GtkWidget *widget)
751 {
752   GtkAllocation allocation;
753   GdkWindow *window;
754   GdkWindowAttr attributes;
755   gint attributes_mask;
756   guint border_width;
757
758   gtk_widget_set_realized (widget, TRUE);
759
760   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
761
762   gtk_widget_get_allocation (widget, &allocation);
763
764   attributes.window_type = GDK_WINDOW_CHILD;
765   attributes.x = allocation.x + border_width;
766   attributes.y = allocation.y + border_width;
767   attributes.width = allocation.width - border_width * 2;
768   attributes.height = allocation.height - border_width * 2;
769   attributes.wclass = GDK_INPUT_OUTPUT;
770   attributes.visual = gtk_widget_get_visual (widget);
771   attributes.event_mask = gtk_widget_get_events (widget)
772                          | GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK
773                          | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
774                          | GDK_BUTTON_MOTION_MASK;
775   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
776
777   window = gdk_window_new (gtk_widget_get_parent_window (widget),
778                            &attributes, attributes_mask);
779   gtk_widget_set_window (widget, window);
780   gdk_window_set_user_data (window, widget);
781
782   gtk_style_context_set_background (gtk_widget_get_style_context (widget),
783                                     window);
784
785   gtk_container_forall (GTK_CONTAINER (widget),
786                         (GtkCallback) gtk_widget_set_parent_window,
787                         window);
788
789   gtk_widget_queue_resize_no_redraw (widget);
790 }
791
792 static void
793 gtk_tool_palette_adjustment_value_changed (GtkAdjustment *adjustment,
794                                            gpointer       data)
795 {
796   GtkAllocation allocation;
797   GtkWidget *widget = GTK_WIDGET (data);
798
799   gtk_widget_get_allocation (widget, &allocation);
800   gtk_tool_palette_size_allocate (widget, &allocation);
801 }
802
803 static void
804 gtk_tool_palette_add (GtkContainer *container,
805                       GtkWidget    *child)
806 {
807   GtkToolPalette *palette;
808   GtkToolItemGroupInfo *info = g_new0(GtkToolItemGroupInfo, 1);
809
810   g_return_if_fail (GTK_IS_TOOL_PALETTE (container));
811   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (child));
812
813   palette = GTK_TOOL_PALETTE (container);
814
815   g_ptr_array_add (palette->priv->groups, info);
816   info->pos = palette->priv->groups->len - 1;
817   info->widget = g_object_ref_sink (child);
818
819   gtk_widget_set_parent (child, GTK_WIDGET (palette));
820 }
821
822 static void
823 gtk_tool_palette_remove (GtkContainer *container,
824                          GtkWidget    *child)
825 {
826   GtkToolPalette *palette;
827   guint i;
828
829   g_return_if_fail (GTK_IS_TOOL_PALETTE (container));
830   palette = GTK_TOOL_PALETTE (container);
831
832   for (i = 0; i < palette->priv->groups->len; ++i)
833     {
834       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
835       if (GTK_WIDGET(info->widget) == child)
836         {
837           g_object_unref (child);
838           gtk_widget_unparent (child);
839
840           g_ptr_array_remove_index (palette->priv->groups, i);
841         }
842     }
843 }
844
845 static void
846 gtk_tool_palette_forall (GtkContainer *container,
847                          gboolean      internals,
848                          GtkCallback   callback,
849                          gpointer      callback_data)
850 {
851   GtkToolPalette *palette = GTK_TOOL_PALETTE (container);
852   guint i;
853
854
855   for (i = 0; i < palette->priv->groups->len; ++i)
856     {
857       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
858       if (info->widget)
859         callback (GTK_WIDGET (info->widget),
860                   callback_data);
861     }
862 }
863
864 static GType
865 gtk_tool_palette_child_type (GtkContainer *container)
866 {
867   return GTK_TYPE_TOOL_ITEM_GROUP;
868 }
869
870 static void
871 gtk_tool_palette_set_child_property (GtkContainer *container,
872                                      GtkWidget    *child,
873                                      guint         prop_id,
874                                      const GValue *value,
875                                      GParamSpec   *pspec)
876 {
877   GtkToolPalette *palette = GTK_TOOL_PALETTE (container);
878
879   switch (prop_id)
880     {
881       case CHILD_PROP_EXCLUSIVE:
882         gtk_tool_palette_set_exclusive (palette, GTK_TOOL_ITEM_GROUP (child), 
883           g_value_get_boolean (value));
884         break;
885
886       case CHILD_PROP_EXPAND:
887         gtk_tool_palette_set_expand (palette, GTK_TOOL_ITEM_GROUP (child), 
888           g_value_get_boolean (value));
889         break;
890
891       default:
892         GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
893         break;
894     }
895 }
896
897 static void
898 gtk_tool_palette_get_child_property (GtkContainer *container,
899                                      GtkWidget    *child,
900                                      guint         prop_id,
901                                      GValue       *value,
902                                      GParamSpec   *pspec)
903 {
904   GtkToolPalette *palette = GTK_TOOL_PALETTE (container);
905
906   switch (prop_id)
907     {
908       case CHILD_PROP_EXCLUSIVE:
909         g_value_set_boolean (value, 
910           gtk_tool_palette_get_exclusive (palette, GTK_TOOL_ITEM_GROUP (child)));
911         break;
912
913       case CHILD_PROP_EXPAND:
914         g_value_set_boolean (value, 
915           gtk_tool_palette_get_expand (palette, GTK_TOOL_ITEM_GROUP (child)));
916         break;
917
918       default:
919         GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
920         break;
921     }
922 }
923
924 static void
925 style_change_notify (GtkToolPalette *palette)
926 {
927   GtkToolPalettePrivate* priv = palette->priv;
928
929   if (!priv->style_set)
930     {
931       /* pretend it was set, then unset, thus reverting to new default */
932       priv->style_set = TRUE;
933       gtk_tool_palette_unset_style (palette);
934     }
935 }
936
937 static void
938 icon_size_change_notify (GtkToolPalette *palette)
939 {
940   GtkToolPalettePrivate* priv = palette->priv;
941
942   if (!priv->icon_size_set)
943     {
944       /* pretend it was set, then unset, thus reverting to new default */
945       priv->icon_size_set = TRUE;
946       gtk_tool_palette_unset_icon_size (palette);
947     }
948 }
949
950 static void
951 gtk_tool_palette_settings_change_notify (GtkSettings      *settings,
952                                          const GParamSpec *pspec,
953                                          GtkToolPalette   *palette)
954 {
955   if (strcmp (pspec->name, "gtk-toolbar-style") == 0)
956     style_change_notify (palette);
957   else if (strcmp (pspec->name, "gtk-toolbar-icon-size") == 0)
958     icon_size_change_notify (palette);
959 }
960
961 static void
962 gtk_tool_palette_screen_changed (GtkWidget *widget,
963                                  GdkScreen *previous_screen)
964 {
965   GtkToolPalette *palette = GTK_TOOL_PALETTE (widget);
966   GtkToolPalettePrivate* priv = palette->priv;
967   GtkSettings *old_settings = priv->settings;
968   GtkSettings *settings;
969
970   if (gtk_widget_has_screen (GTK_WIDGET (palette)))
971     settings = gtk_widget_get_settings (GTK_WIDGET (palette));
972   else
973     settings = NULL;
974
975   if (settings == old_settings)
976     return;
977
978   if (old_settings)
979   {
980     g_signal_handler_disconnect (old_settings, priv->settings_connection);
981     g_object_unref (old_settings);
982   }
983
984   if (settings)
985   {
986     priv->settings_connection =
987       g_signal_connect (settings, "notify",
988                         G_CALLBACK (gtk_tool_palette_settings_change_notify),
989                         palette);
990     priv->settings = g_object_ref (settings);
991   }
992   else
993     priv->settings = NULL;
994
995   gtk_tool_palette_reconfigured (palette);
996 }
997
998
999 static void
1000 gtk_tool_palette_class_init (GtkToolPaletteClass *cls)
1001 {
1002   GObjectClass      *oclass   = G_OBJECT_CLASS (cls);
1003   GtkWidgetClass    *wclass   = GTK_WIDGET_CLASS (cls);
1004   GtkContainerClass *cclass   = GTK_CONTAINER_CLASS (cls);
1005
1006   oclass->set_property        = gtk_tool_palette_set_property;
1007   oclass->get_property        = gtk_tool_palette_get_property;
1008   oclass->dispose             = gtk_tool_palette_dispose;
1009   oclass->finalize            = gtk_tool_palette_finalize;
1010
1011   wclass->get_preferred_width = gtk_tool_palette_get_preferred_width;
1012   wclass->get_preferred_height= gtk_tool_palette_get_preferred_height;
1013   wclass->size_allocate       = gtk_tool_palette_size_allocate;
1014   wclass->draw                = gtk_tool_palette_draw;
1015   wclass->realize             = gtk_tool_palette_realize;
1016
1017   cclass->add                 = gtk_tool_palette_add;
1018   cclass->remove              = gtk_tool_palette_remove;
1019   cclass->forall              = gtk_tool_palette_forall;
1020   cclass->child_type          = gtk_tool_palette_child_type;
1021   cclass->set_child_property  = gtk_tool_palette_set_child_property;
1022   cclass->get_child_property  = gtk_tool_palette_get_child_property;
1023
1024   /* Handle screen-changed so we can update our GtkSettings.
1025    */
1026   wclass->screen_changed      = gtk_tool_palette_screen_changed;
1027
1028   g_object_class_override_property (oclass, PROP_ORIENTATION,    "orientation");
1029
1030   g_object_class_override_property (oclass, PROP_HADJUSTMENT,    "hadjustment");
1031   g_object_class_override_property (oclass, PROP_VADJUSTMENT,    "vadjustment");
1032   g_object_class_override_property (oclass, PROP_HSCROLL_POLICY, "hscroll-policy");
1033   g_object_class_override_property (oclass, PROP_VSCROLL_POLICY, "vscroll-policy");
1034
1035   /**
1036    * GtkToolPalette:icon-size:
1037    *
1038    * The size of the icons in a tool palette is normally determined by
1039    * the #GtkSettings:toolbar-icon-size setting. When this property is set,
1040    * it overrides the setting.
1041    *
1042    * This should only be used for special-purpose tool palettes, normal
1043    * application tool palettes should respect the user preferences for the
1044    * size of icons.
1045    *
1046    * Since: 2.20
1047    */
1048   g_object_class_install_property (oclass,
1049                                    PROP_ICON_SIZE,
1050                                    g_param_spec_enum ("icon-size",
1051                                                       P_("Icon size"),
1052                                                       P_("Size of icons in this tool palette"),
1053                                                       GTK_TYPE_ICON_SIZE,
1054                                                       DEFAULT_ICON_SIZE,
1055                                                       GTK_PARAM_READWRITE));
1056
1057   /**
1058    * GtkToolPalette:icon-size-set:
1059    *
1060    * Is %TRUE if the #GtkToolPalette:icon-size property has been set.
1061    *
1062    * Since: 2.20
1063    */
1064   g_object_class_install_property (oclass,
1065                                    PROP_ICON_SIZE_SET,
1066                                    g_param_spec_boolean ("icon-size-set",
1067                                                       P_("Icon size set"),
1068                                                       P_("Whether the icon-size property has been set"),
1069                                                       FALSE,
1070                                                       GTK_PARAM_READWRITE));
1071
1072   /**
1073    * GtkToolPalette:toolbar-style:
1074    *
1075    * The style of items in the tool palette.
1076    *
1077    * Since: 2.20
1078    */
1079   g_object_class_install_property (oclass, PROP_TOOLBAR_STYLE,
1080                                    g_param_spec_enum ("toolbar-style",
1081                                                       P_("Toolbar Style"),
1082                                                       P_("Style of items in the tool palette"),
1083                                                       GTK_TYPE_TOOLBAR_STYLE,
1084                                                       DEFAULT_TOOLBAR_STYLE,
1085                                                       GTK_PARAM_READWRITE));
1086
1087
1088   /**
1089    * GtkToolPalette:exclusive:
1090    *
1091    * Whether the item group should be the only one that is expanded
1092    * at a given time.
1093    *
1094    * Since: 2.20
1095    */
1096   gtk_container_class_install_child_property (cclass, CHILD_PROP_EXCLUSIVE,
1097                                               g_param_spec_boolean ("exclusive",
1098                                                                     P_("Exclusive"),
1099                                                                     P_("Whether the item group should be the only expanded at a given time"),
1100                                                                     DEFAULT_CHILD_EXCLUSIVE,
1101                                                                     GTK_PARAM_READWRITE));
1102
1103   /**
1104    * GtkToolPalette:expand:
1105    *
1106    * Whether the item group should receive extra space when the palette grows.
1107    * at a given time.
1108    *
1109    * Since: 2.20
1110    */
1111   gtk_container_class_install_child_property (cclass, CHILD_PROP_EXPAND,
1112                                               g_param_spec_boolean ("expand",
1113                                                                     P_("Expand"),
1114                                                                     P_("Whether the item group should receive extra space when the palette grows"),
1115                                                                     DEFAULT_CHILD_EXPAND,
1116                                                                     GTK_PARAM_READWRITE));
1117
1118   g_type_class_add_private (cls, sizeof (GtkToolPalettePrivate));
1119
1120   dnd_target_atom_item = gdk_atom_intern_static_string (dnd_targets[0].target);
1121   dnd_target_atom_group = gdk_atom_intern_static_string (dnd_targets[1].target);
1122 }
1123
1124 /**
1125  * gtk_tool_palette_new:
1126  *
1127  * Creates a new tool palette.
1128  *
1129  * Returns: a new #GtkToolPalette
1130  *
1131  * Since: 2.20
1132  */
1133 GtkWidget*
1134 gtk_tool_palette_new (void)
1135 {
1136   return g_object_new (GTK_TYPE_TOOL_PALETTE, NULL);
1137 }
1138
1139 /**
1140  * gtk_tool_palette_set_icon_size:
1141  * @palette: a #GtkToolPalette
1142  * @icon_size: (type int): the #GtkIconSize that icons in the tool
1143  *     palette shall have
1144  *
1145  * Sets the size of icons in the tool palette.
1146  *
1147  * Since: 2.20
1148  */
1149 void
1150 gtk_tool_palette_set_icon_size (GtkToolPalette *palette,
1151                                 GtkIconSize     icon_size)
1152 {
1153   GtkToolPalettePrivate *priv;
1154
1155   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1156   g_return_if_fail (icon_size != GTK_ICON_SIZE_INVALID);
1157
1158   priv = palette->priv;
1159
1160   if (!priv->icon_size_set)
1161     {
1162       priv->icon_size_set = TRUE;
1163       g_object_notify (G_OBJECT (palette), "icon-size-set");
1164     }
1165
1166   if (priv->icon_size == icon_size)
1167     return;
1168
1169   priv->icon_size = icon_size;
1170   g_object_notify (G_OBJECT (palette), "icon-size");
1171
1172   gtk_tool_palette_reconfigured (palette);
1173
1174   gtk_widget_queue_resize (GTK_WIDGET (palette));
1175 }
1176
1177 static GtkSettings *
1178 toolpalette_get_settings (GtkToolPalette *palette)
1179 {
1180   GtkToolPalettePrivate *priv = palette->priv;
1181   return priv->settings;
1182 }
1183
1184 /**
1185  * gtk_tool_palette_unset_icon_size:
1186  * @palette: a #GtkToolPalette
1187  *
1188  * Unsets the tool palette icon size set with gtk_tool_palette_set_icon_size(),
1189  * so that user preferences will be used to determine the icon size.
1190  *
1191  * Since: 2.20
1192  */
1193 void
1194 gtk_tool_palette_unset_icon_size (GtkToolPalette *palette)
1195 {
1196   GtkToolPalettePrivate* priv = palette->priv;
1197   GtkIconSize size;
1198
1199   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1200
1201   if (palette->priv->icon_size_set)
1202     {
1203       GtkSettings *settings = toolpalette_get_settings (palette);
1204
1205       if (settings)
1206         {
1207           g_object_get (settings,
1208             "gtk-toolbar-icon-size", &size,
1209             NULL);
1210         }
1211       else
1212         size = DEFAULT_ICON_SIZE;
1213
1214       if (size != palette->priv->icon_size)
1215       {
1216         gtk_tool_palette_set_icon_size (palette, size);
1217         g_object_notify (G_OBJECT (palette), "icon-size");
1218             }
1219
1220       priv->icon_size_set = FALSE;
1221       g_object_notify (G_OBJECT (palette), "icon-size-set");
1222     }
1223 }
1224
1225 /* Set the "toolbar-style" property and do appropriate things.
1226  * GtkToolbar does this by emitting a signal instead of just
1227  * calling a function...
1228  */
1229 static void
1230 gtk_tool_palette_change_style (GtkToolPalette  *palette,
1231                                GtkToolbarStyle  style)
1232 {
1233   GtkToolPalettePrivate* priv = palette->priv;
1234
1235   if (priv->style != style)
1236     {
1237       priv->style = style;
1238
1239       gtk_tool_palette_reconfigured (palette);
1240
1241       gtk_widget_queue_resize (GTK_WIDGET (palette));
1242       g_object_notify (G_OBJECT (palette), "toolbar-style");
1243     }
1244 }
1245
1246
1247 /**
1248  * gtk_tool_palette_set_style:
1249  * @palette: a #GtkToolPalette
1250  * @style: the #GtkToolbarStyle that items in the tool palette shall have
1251  *
1252  * Sets the style (text, icons or both) of items in the tool palette.
1253  *
1254  * Since: 2.20
1255  */
1256 void
1257 gtk_tool_palette_set_style (GtkToolPalette  *palette,
1258                             GtkToolbarStyle  style)
1259 {
1260   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1261
1262   palette->priv->style_set = TRUE;
1263   gtk_tool_palette_change_style (palette, style);
1264 }
1265
1266
1267 /**
1268  * gtk_tool_palette_unset_style:
1269  * @palette: a #GtkToolPalette
1270  *
1271  * Unsets a toolbar style set with gtk_tool_palette_set_style(),
1272  * so that user preferences will be used to determine the toolbar style.
1273  *
1274  * Since: 2.20
1275  */
1276 void
1277 gtk_tool_palette_unset_style (GtkToolPalette *palette)
1278 {
1279   GtkToolPalettePrivate* priv = palette->priv;
1280   GtkToolbarStyle style;
1281
1282   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1283
1284   if (priv->style_set)
1285     {
1286       GtkSettings *settings = toolpalette_get_settings (palette);
1287
1288       if (settings)
1289         g_object_get (settings,
1290                       "gtk-toolbar-style", &style,
1291                       NULL);
1292       else
1293         style = DEFAULT_TOOLBAR_STYLE;
1294
1295       if (style != priv->style)
1296         gtk_tool_palette_change_style (palette, style);
1297
1298       priv->style_set = FALSE;
1299     }
1300 }
1301
1302 /**
1303  * gtk_tool_palette_get_icon_size:
1304  * @palette: a #GtkToolPalette
1305  *
1306  * Gets the size of icons in the tool palette.
1307  * See gtk_tool_palette_set_icon_size().
1308  *
1309  * Returns: (type int): the #GtkIconSize of icons in the tool palette
1310  *
1311  * Since: 2.20
1312  */
1313 GtkIconSize
1314 gtk_tool_palette_get_icon_size (GtkToolPalette *palette)
1315 {
1316   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_ICON_SIZE);
1317
1318   return palette->priv->icon_size;
1319 }
1320
1321 /**
1322  * gtk_tool_palette_get_style:
1323  * @palette: a #GtkToolPalette
1324  *
1325  * Gets the style (icons, text or both) of items in the tool palette.
1326  *
1327  * Returns: the #GtkToolbarStyle of items in the tool palette.
1328  *
1329  * Since: 2.20
1330  */
1331 GtkToolbarStyle
1332 gtk_tool_palette_get_style (GtkToolPalette *palette)
1333 {
1334   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_TOOLBAR_STYLE);
1335
1336   return palette->priv->style;
1337 }
1338
1339 gint
1340 _gtk_tool_palette_compare_groups (gconstpointer a,
1341                                   gconstpointer b)
1342 {
1343   const GtkToolItemGroupInfo *group_a = a;
1344   const GtkToolItemGroupInfo *group_b = b;
1345
1346   return group_a->pos - group_b->pos;
1347 }
1348
1349 /**
1350  * gtk_tool_palette_set_group_position:
1351  * @palette: a #GtkToolPalette
1352  * @group: a #GtkToolItemGroup which is a child of palette
1353  * @position: a new index for group
1354  *
1355  * Sets the position of the group as an index of the tool palette.
1356  * If position is 0 the group will become the first child, if position is
1357  * -1 it will become the last child.
1358  *
1359  * Since: 2.20
1360  */
1361 void
1362 gtk_tool_palette_set_group_position (GtkToolPalette   *palette,
1363                                      GtkToolItemGroup *group,
1364                                      gint             position)
1365 {
1366   GtkToolItemGroupInfo *group_new;
1367   GtkToolItemGroupInfo *group_old;
1368   gint old_position;
1369
1370   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1371   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1372   g_return_if_fail (position >= -1);
1373
1374   if (-1 == position)
1375     position = palette->priv->groups->len - 1;
1376
1377   g_return_if_fail ((guint) position < palette->priv->groups->len);
1378
1379   group_new = g_ptr_array_index (palette->priv->groups, position);
1380
1381   if (GTK_TOOL_ITEM_GROUP (group) == group_new->widget)
1382     return;
1383
1384   old_position = gtk_tool_palette_get_group_position (palette, group);
1385   g_return_if_fail (old_position >= 0);
1386
1387   group_old = g_ptr_array_index (palette->priv->groups, old_position);
1388
1389   group_new->pos = position;
1390   group_old->pos = old_position;
1391
1392   g_ptr_array_sort (palette->priv->groups, _gtk_tool_palette_compare_groups);
1393
1394   gtk_widget_queue_resize (GTK_WIDGET (palette));
1395 }
1396
1397 static void
1398 gtk_tool_palette_group_notify_collapsed (GtkToolItemGroup *group,
1399                                          GParamSpec       *pspec,
1400                                          gpointer          data)
1401 {
1402   GtkToolPalette *palette = GTK_TOOL_PALETTE (data);
1403   guint i;
1404
1405   if (gtk_tool_item_group_get_collapsed (group))
1406     return;
1407
1408   for (i = 0; i < palette->priv->groups->len; ++i)
1409     {
1410       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
1411       GtkToolItemGroup *current_group = info->widget;
1412
1413       if (current_group && current_group != group)
1414         gtk_tool_item_group_set_collapsed (current_group, TRUE);
1415     }
1416 }
1417
1418 /**
1419  * gtk_tool_palette_set_exclusive:
1420  * @palette: a #GtkToolPalette
1421  * @group: a #GtkToolItemGroup which is a child of palette
1422  * @exclusive: whether the group should be exclusive or not
1423  *
1424  * Sets whether the group should be exclusive or not.
1425  * If an exclusive group is expanded all other groups are collapsed.
1426  *
1427  * Since: 2.20
1428  */
1429 void
1430 gtk_tool_palette_set_exclusive (GtkToolPalette   *palette,
1431                                 GtkToolItemGroup *group,
1432                                 gboolean          exclusive)
1433 {
1434   GtkToolItemGroupInfo *group_info;
1435   gint position;
1436
1437   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1438   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1439
1440   position = gtk_tool_palette_get_group_position (palette, group);
1441   g_return_if_fail (position >= 0);
1442
1443   group_info = g_ptr_array_index (palette->priv->groups, position);
1444
1445   if (exclusive == group_info->exclusive)
1446     return;
1447
1448   group_info->exclusive = exclusive;
1449
1450   if (group_info->exclusive != (0 != group_info->notify_collapsed))
1451     {
1452       if (group_info->exclusive)
1453         {
1454           group_info->notify_collapsed =
1455             g_signal_connect (group, "notify::collapsed",
1456                               G_CALLBACK (gtk_tool_palette_group_notify_collapsed),
1457                               palette);
1458         }
1459       else
1460         {
1461           g_signal_handler_disconnect (group, group_info->notify_collapsed);
1462           group_info->notify_collapsed = 0;
1463         }
1464     }
1465
1466   gtk_tool_palette_group_notify_collapsed (group_info->widget, NULL, palette);
1467   gtk_widget_child_notify (GTK_WIDGET (group), "exclusive");
1468 }
1469
1470 /**
1471  * gtk_tool_palette_set_expand:
1472  * @palette: a #GtkToolPalette
1473  * @group: a #GtkToolItemGroup which is a child of palette
1474  * @expand: whether the group should be given extra space
1475  *
1476  * Sets whether the group should be given extra space.
1477  *
1478  * Since: 2.20
1479  */
1480 void
1481 gtk_tool_palette_set_expand (GtkToolPalette   *palette,
1482                              GtkToolItemGroup *group,
1483                              gboolean        expand)
1484 {
1485   GtkToolItemGroupInfo *group_info;
1486   gint position;
1487
1488   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1489   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1490
1491   position = gtk_tool_palette_get_group_position (palette, group);
1492   g_return_if_fail (position >= 0);
1493
1494   group_info = g_ptr_array_index (palette->priv->groups, position);
1495
1496   if (expand != group_info->expand)
1497     {
1498       group_info->expand = expand;
1499       gtk_widget_queue_resize (GTK_WIDGET (palette));
1500       gtk_widget_child_notify (GTK_WIDGET (group), "expand");
1501     }
1502 }
1503
1504 /**
1505  * gtk_tool_palette_get_group_position:
1506  * @palette: a #GtkToolPalette
1507  * @group: a #GtkToolItemGroup
1508  *
1509  * Gets the position of @group in @palette as index.
1510  * See gtk_tool_palette_set_group_position().
1511  *
1512  * Returns: the index of group or -1 if @group is not a child of @palette
1513  *
1514  * Since: 2.20
1515  */
1516 gint
1517 gtk_tool_palette_get_group_position (GtkToolPalette   *palette,
1518                                      GtkToolItemGroup *group)
1519 {
1520   guint i;
1521
1522   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), -1);
1523   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), -1);
1524
1525   for (i = 0; i < palette->priv->groups->len; ++i)
1526     {
1527       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
1528       if ((gpointer) group == info->widget)
1529         return i;
1530     }
1531
1532   return -1;
1533 }
1534
1535 /**
1536  * gtk_tool_palette_get_exclusive:
1537  * @palette: a #GtkToolPalette
1538  * @group: a #GtkToolItemGroup which is a child of palette
1539  *
1540  * Gets whether @group is exclusive or not.
1541  * See gtk_tool_palette_set_exclusive().
1542  *
1543  * Returns: %TRUE if @group is exclusive
1544  *
1545  * Since: 2.20
1546  */
1547 gboolean
1548 gtk_tool_palette_get_exclusive (GtkToolPalette   *palette,
1549                                 GtkToolItemGroup *group)
1550 {
1551   gint position;
1552   GtkToolItemGroupInfo *info;
1553
1554   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXCLUSIVE);
1555   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXCLUSIVE);
1556
1557   position = gtk_tool_palette_get_group_position (palette, group);
1558   g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXCLUSIVE);
1559
1560   info = g_ptr_array_index (palette->priv->groups, position);
1561
1562   return info->exclusive;
1563 }
1564
1565 /**
1566  * gtk_tool_palette_get_expand:
1567  * @palette: a #GtkToolPalette
1568  * @group: a #GtkToolItemGroup which is a child of palette
1569  *
1570  * Gets whether group should be given extra space.
1571  * See gtk_tool_palette_set_expand().
1572  *
1573  * Returns: %TRUE if group should be given extra space, %FALSE otherwise
1574  *
1575  * Since: 2.20
1576  */
1577 gboolean
1578 gtk_tool_palette_get_expand (GtkToolPalette   *palette,
1579                              GtkToolItemGroup *group)
1580 {
1581   gint position;
1582   GtkToolItemGroupInfo *info;
1583
1584   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXPAND);
1585   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXPAND);
1586
1587   position = gtk_tool_palette_get_group_position (palette, group);
1588   g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXPAND);
1589
1590   info = g_ptr_array_index (palette->priv->groups, position);
1591
1592   return info->expand;
1593 }
1594
1595 /**
1596  * gtk_tool_palette_get_drop_item:
1597  * @palette: a #GtkToolPalette
1598  * @x: the x position
1599  * @y: the y position
1600  *
1601  * Gets the item at position (x, y).
1602  * See gtk_tool_palette_get_drop_group().
1603  *
1604  * Returns: (transfer none): the #GtkToolItem at position or %NULL if there is no such item
1605  *
1606  * Since: 2.20
1607  */
1608 GtkToolItem*
1609 gtk_tool_palette_get_drop_item (GtkToolPalette *palette,
1610                                 gint            x,
1611                                 gint            y)
1612 {
1613   GtkAllocation allocation;
1614   GtkToolItemGroup *group = gtk_tool_palette_get_drop_group (palette, x, y);
1615   GtkWidget *widget = GTK_WIDGET (group);
1616
1617   if (group)
1618     {
1619       gtk_widget_get_allocation (widget, &allocation);
1620       return gtk_tool_item_group_get_drop_item (group,
1621                                                 x - allocation.x,
1622                                                 y - allocation.y);
1623     }
1624
1625   return NULL;
1626 }
1627
1628 /**
1629  * gtk_tool_palette_get_drop_group:
1630  * @palette: a #GtkToolPalette
1631  * @x: the x position
1632  * @y: the y position
1633  *
1634  * Gets the group at position (x, y).
1635  *
1636  * Returns: (transfer none): the #GtkToolItemGroup at position or %NULL
1637  *     if there is no such group
1638  *
1639  * Since: 2.20
1640  */
1641 GtkToolItemGroup*
1642 gtk_tool_palette_get_drop_group (GtkToolPalette *palette,
1643                                  gint            x,
1644                                  gint            y)
1645 {
1646   GtkAllocation allocation;
1647   guint i;
1648
1649   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
1650
1651   gtk_widget_get_allocation (GTK_WIDGET (palette), &allocation);
1652
1653   g_return_val_if_fail (x >= 0 && x < allocation.width, NULL);
1654   g_return_val_if_fail (y >= 0 && y < allocation.height, NULL);
1655
1656   for (i = 0; i < palette->priv->groups->len; ++i)
1657     {
1658       GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
1659       GtkWidget *widget;
1660       gint x0, y0;
1661
1662       if (!group->widget)
1663         continue;
1664
1665       widget = GTK_WIDGET (group->widget);
1666       gtk_widget_get_allocation (widget, &allocation);
1667
1668       x0 = x - allocation.x;
1669       y0 = y - allocation.y;
1670
1671       if (x0 >= 0 && x0 < allocation.width &&
1672           y0 >= 0 && y0 < allocation.height)
1673         return GTK_TOOL_ITEM_GROUP (widget);
1674     }
1675
1676   return NULL;
1677 }
1678
1679 /**
1680  * gtk_tool_palette_get_drag_item:
1681  * @palette: a #GtkToolPalette
1682  * @selection: a #GtkSelectionData
1683  *
1684  * Get the dragged item from the selection.
1685  * This could be a #GtkToolItem or a #GtkToolItemGroup.
1686  *
1687  * Returns: (transfer none): the dragged item in selection
1688  *
1689  * Since: 2.20
1690  */
1691 GtkWidget*
1692 gtk_tool_palette_get_drag_item (GtkToolPalette         *palette,
1693                                 const GtkSelectionData *selection)
1694 {
1695   GtkToolPaletteDragData *data;
1696   GdkAtom target;
1697
1698   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
1699   g_return_val_if_fail (NULL != selection, NULL);
1700
1701   g_return_val_if_fail (gtk_selection_data_get_format (selection) == 8, NULL);
1702   g_return_val_if_fail (gtk_selection_data_get_length (selection) == sizeof (GtkToolPaletteDragData), NULL);
1703   target = gtk_selection_data_get_target (selection);
1704   g_return_val_if_fail (target == dnd_target_atom_item ||
1705                         target == dnd_target_atom_group,
1706                         NULL);
1707
1708   data = (GtkToolPaletteDragData*) gtk_selection_data_get_data (selection);
1709
1710   g_return_val_if_fail (data->palette == palette, NULL);
1711
1712   if (dnd_target_atom_item == target)
1713     g_return_val_if_fail (GTK_IS_TOOL_ITEM (data->item), NULL);
1714   else if (dnd_target_atom_group == target)
1715     g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (data->item), NULL);
1716
1717   return data->item;
1718 }
1719
1720 /**
1721  * gtk_tool_palette_set_drag_source:
1722  * @palette: a #GtkToolPalette
1723  * @targets: the #GtkToolPaletteDragTarget<!-- -->s
1724  *     which the widget should support
1725  *
1726  * Sets the tool palette as a drag source.
1727  * Enables all groups and items in the tool palette as drag sources
1728  * on button 1 and button 3 press with copy and move actions.
1729  * See gtk_drag_source_set().
1730  *
1731  * Since: 2.20
1732  */
1733 void
1734 gtk_tool_palette_set_drag_source (GtkToolPalette            *palette,
1735                                   GtkToolPaletteDragTargets  targets)
1736 {
1737   guint i;
1738
1739   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1740
1741   if ((palette->priv->drag_source & targets) == targets)
1742     return;
1743
1744   palette->priv->drag_source |= targets;
1745
1746   for (i = 0; i < palette->priv->groups->len; ++i)
1747     {
1748       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
1749       if (info->widget)
1750         gtk_container_forall (GTK_CONTAINER (info->widget),
1751                               _gtk_tool_palette_child_set_drag_source,
1752                               palette);
1753     }
1754 }
1755
1756 /**
1757  * gtk_tool_palette_add_drag_dest:
1758  * @palette: a #GtkToolPalette
1759  * @widget: a #GtkWidget which should be a drag destination for @palette
1760  * @flags: the flags that specify what actions GTK+ should take for drops
1761  *     on that widget
1762  * @targets: the #GtkToolPaletteDragTarget<!-- -->s which the widget
1763  *     should support
1764  * @actions: the #GdkDragAction<!-- -->s which the widget should suppport
1765  *
1766  * Sets @palette as drag source (see gtk_tool_palette_set_drag_source())
1767  * and sets @widget as a drag destination for drags from @palette.
1768  * See gtk_drag_dest_set().
1769  *
1770  * Since: 2.20
1771  */
1772 void
1773 gtk_tool_palette_add_drag_dest (GtkToolPalette            *palette,
1774                                 GtkWidget                 *widget,
1775                                 GtkDestDefaults            flags,
1776                                 GtkToolPaletteDragTargets  targets,
1777                                 GdkDragAction              actions)
1778 {
1779   GtkTargetEntry entries[G_N_ELEMENTS (dnd_targets)];
1780   gint n_entries = 0;
1781
1782   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1783   g_return_if_fail (GTK_IS_WIDGET (widget));
1784
1785   gtk_tool_palette_set_drag_source (palette,
1786                                     targets);
1787
1788   if (targets & GTK_TOOL_PALETTE_DRAG_ITEMS)
1789     entries[n_entries++] = dnd_targets[0];
1790   if (targets & GTK_TOOL_PALETTE_DRAG_GROUPS)
1791     entries[n_entries++] = dnd_targets[1];
1792
1793   gtk_drag_dest_set (widget, flags, entries, n_entries, actions);
1794 }
1795
1796 void
1797 _gtk_tool_palette_get_item_size (GtkToolPalette *palette,
1798                                  GtkRequisition *item_size,
1799                                  gboolean        homogeneous_only,
1800                                  gint           *requested_rows)
1801 {
1802   GtkRequisition max_requisition;
1803   gint max_rows;
1804   guint i;
1805
1806   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1807   g_return_if_fail (NULL != item_size);
1808
1809   max_requisition.width = 0;
1810   max_requisition.height = 0;
1811   max_rows = 0;
1812
1813   /* iterate over all groups and calculate the max item_size and max row request */
1814   for (i = 0; i < palette->priv->groups->len; ++i)
1815     {
1816       GtkRequisition requisition;
1817       gint rows;
1818       GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
1819
1820       if (!group->widget)
1821         continue;
1822
1823       _gtk_tool_item_group_item_size_request (group->widget, &requisition, homogeneous_only, &rows);
1824
1825       max_requisition.width = MAX (max_requisition.width, requisition.width);
1826       max_requisition.height = MAX (max_requisition.height, requisition.height);
1827       max_rows = MAX (max_rows, rows);
1828     }
1829
1830   *item_size = max_requisition;
1831   if (requested_rows)
1832     *requested_rows = max_rows;
1833 }
1834
1835 static void
1836 gtk_tool_palette_item_drag_data_get (GtkWidget        *widget,
1837                                      GdkDragContext   *context,
1838                                      GtkSelectionData *selection,
1839                                      guint             info,
1840                                      guint             time,
1841                                      gpointer          data)
1842 {
1843   GtkToolPaletteDragData drag_data = { GTK_TOOL_PALETTE (data), NULL };
1844   GdkAtom target;
1845
1846   target = gtk_selection_data_get_target (selection);
1847
1848   if (target == dnd_target_atom_item)
1849     drag_data.item = gtk_widget_get_ancestor (widget, GTK_TYPE_TOOL_ITEM);
1850
1851   if (drag_data.item)
1852     gtk_selection_data_set (selection, target, 8,
1853                             (guchar*) &drag_data, sizeof (drag_data));
1854 }
1855
1856 static void
1857 gtk_tool_palette_child_drag_data_get (GtkWidget        *widget,
1858                                       GdkDragContext   *context,
1859                                       GtkSelectionData *selection,
1860                                       guint             info,
1861                                       guint             time,
1862                                       gpointer          data)
1863 {
1864   GtkToolPaletteDragData drag_data = { GTK_TOOL_PALETTE (data), NULL };
1865   GdkAtom target;
1866
1867   target = gtk_selection_data_get_target (selection);
1868
1869   if (target == dnd_target_atom_group)
1870     drag_data.item = gtk_widget_get_ancestor (widget, GTK_TYPE_TOOL_ITEM_GROUP);
1871
1872   if (drag_data.item)
1873     gtk_selection_data_set (selection, target, 8,
1874                             (guchar*) &drag_data, sizeof (drag_data));
1875 }
1876
1877 void
1878 _gtk_tool_palette_child_set_drag_source (GtkWidget *child,
1879                                          gpointer   data)
1880 {
1881   GtkToolPalette *palette = GTK_TOOL_PALETTE (data);
1882
1883   /* Check drag_source,
1884    * to work properly when called from gtk_tool_item_group_insert().
1885    */
1886   if (!palette->priv->drag_source)
1887     return;
1888
1889   if (GTK_IS_TOOL_ITEM (child) &&
1890       (palette->priv->drag_source & GTK_TOOL_PALETTE_DRAG_ITEMS))
1891     {
1892       /* Connect to child instead of the item itself,
1893        * to work arround bug 510377.
1894        */
1895       if (GTK_IS_TOOL_BUTTON (child))
1896         child = gtk_bin_get_child (GTK_BIN (child));
1897
1898       if (!child)
1899         return;
1900
1901       gtk_drag_source_set (child, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1902                            &dnd_targets[0], 1, GDK_ACTION_COPY | GDK_ACTION_MOVE);
1903
1904       g_signal_connect (child, "drag-data-get",
1905                         G_CALLBACK (gtk_tool_palette_item_drag_data_get),
1906                         palette);
1907     }
1908   else if (GTK_IS_BUTTON (child) &&
1909            (palette->priv->drag_source & GTK_TOOL_PALETTE_DRAG_GROUPS))
1910     {
1911       gtk_drag_source_set (child, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1912                            &dnd_targets[1], 1, GDK_ACTION_COPY | GDK_ACTION_MOVE);
1913
1914       g_signal_connect (child, "drag-data-get",
1915                         G_CALLBACK (gtk_tool_palette_child_drag_data_get),
1916                         palette);
1917     }
1918 }
1919
1920 /**
1921  * gtk_tool_palette_get_drag_target_item:
1922  *
1923  * Gets the target entry for a dragged #GtkToolItem.
1924  *
1925  * Returns: (transfer none): the #GtkTargetEntry for a dragged item.
1926  *
1927  * Since: 2.20
1928  */
1929 G_CONST_RETURN GtkTargetEntry*
1930 gtk_tool_palette_get_drag_target_item (void)
1931 {
1932   return &dnd_targets[0];
1933 }
1934
1935 /**
1936  * gtk_tool_palette_get_drag_target_group:
1937  *
1938  * Get the target entry for a dragged #GtkToolItemGroup.
1939  *
1940  * Returns: (transfer none): the #GtkTargetEntry for a dragged group
1941  *
1942  * Since: 2.20
1943  */
1944 G_CONST_RETURN GtkTargetEntry*
1945 gtk_tool_palette_get_drag_target_group (void)
1946 {
1947   return &dnd_targets[1];
1948 }
1949
1950 void
1951 _gtk_tool_palette_set_expanding_child (GtkToolPalette *palette,
1952                                        GtkWidget      *widget)
1953 {
1954   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1955   palette->priv->expanding_child = widget;
1956 }
1957
1958 /**
1959  * gtk_tool_palette_get_hadjustment:
1960  * @palette: a #GtkToolPalette
1961  *
1962  * Gets the horizontal adjustment of the tool palette.
1963  *
1964  * Returns: (transfer none): the horizontal adjustment of @palette
1965  *
1966  * Since: 2.20
1967  *
1968  * Deprecated: 3.0: Use gtk_scrollable_get_hadjustment()
1969  */
1970 GtkAdjustment*
1971 gtk_tool_palette_get_hadjustment (GtkToolPalette *palette)
1972 {
1973   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
1974
1975   return palette->priv->hadjustment;
1976 }
1977
1978 static void
1979 gtk_tool_palette_set_hadjustment (GtkToolPalette *palette,
1980                                   GtkAdjustment  *adjustment)
1981 {
1982   GtkToolPalettePrivate *priv = palette->priv;
1983
1984   if (adjustment && priv->hadjustment == adjustment)
1985     return;
1986
1987   if (priv->hadjustment != NULL)
1988     {
1989       g_signal_handlers_disconnect_by_func (priv->hadjustment,
1990                                             gtk_tool_palette_adjustment_value_changed,
1991                                             palette);
1992       g_object_unref (priv->hadjustment);
1993     }
1994
1995   if (adjustment == NULL)
1996     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
1997                                      0.0, 0.0, 0.0);
1998
1999   g_signal_connect (adjustment, "value-changed",
2000                     G_CALLBACK (gtk_tool_palette_adjustment_value_changed),
2001                     palette);
2002   priv->hadjustment = g_object_ref_sink (adjustment);
2003   /* FIXME: Adjustment should probably have it's values updated now */
2004   g_object_notify (G_OBJECT (palette), "hadjustment");
2005 }
2006
2007 /**
2008  * gtk_tool_palette_get_vadjustment:
2009  * @palette: a #GtkToolPalette
2010  *
2011  * Gets the vertical adjustment of the tool palette.
2012  *
2013  * Returns: (transfer none): the vertical adjustment of @palette
2014  *
2015  * Since: 2.20
2016  *
2017  * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
2018  */
2019 GtkAdjustment*
2020 gtk_tool_palette_get_vadjustment (GtkToolPalette *palette)
2021 {
2022   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
2023
2024   return palette->priv->vadjustment;
2025 }
2026
2027 static void
2028 gtk_tool_palette_set_vadjustment (GtkToolPalette *palette,
2029                                   GtkAdjustment  *adjustment)
2030 {
2031   GtkToolPalettePrivate *priv = palette->priv;
2032
2033   if (adjustment && priv->vadjustment == adjustment)
2034     return;
2035
2036   if (priv->vadjustment != NULL)
2037     {
2038       g_signal_handlers_disconnect_by_func (priv->vadjustment,
2039                                             gtk_tool_palette_adjustment_value_changed,
2040                                             palette);
2041       g_object_unref (priv->vadjustment);
2042     }
2043
2044   if (adjustment == NULL)
2045     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
2046                                      0.0, 0.0, 0.0);
2047
2048   g_signal_connect (adjustment, "value-changed",
2049                     G_CALLBACK (gtk_tool_palette_adjustment_value_changed),
2050                     palette);
2051   priv->vadjustment = g_object_ref_sink (adjustment);
2052   /* FIXME: Adjustment should probably have it's values updated now */
2053   g_object_notify (G_OBJECT (palette), "vadjustment");
2054 }
2055
2056 GtkSizeGroup *
2057 _gtk_tool_palette_get_size_group (GtkToolPalette *palette)
2058 {
2059   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
2060
2061   return palette->priv->text_size_group;
2062 }