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