]> Pileus Git - ~andy/gtk/blob - gtk/gtktoolpalette.c
195a43adfb61dcd37ba0e0a40c34e0bff67fd645
[~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 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_style_context_set_background (gtk_widget_get_style_context (widget),
761                                     window);
762
763   gtk_container_forall (GTK_CONTAINER (widget),
764                         (GtkCallback) gtk_widget_set_parent_window,
765                         window);
766
767   gtk_widget_queue_resize_no_redraw (widget);
768 }
769
770 static void
771 gtk_tool_palette_adjustment_value_changed (GtkAdjustment *adjustment,
772                                            gpointer       data)
773 {
774   GtkAllocation allocation;
775   GtkWidget *widget = GTK_WIDGET (data);
776
777   gtk_widget_get_allocation (widget, &allocation);
778   gtk_tool_palette_size_allocate (widget, &allocation);
779 }
780
781 static void
782 gtk_tool_palette_add (GtkContainer *container,
783                       GtkWidget    *child)
784 {
785   GtkToolPalette *palette;
786   GtkToolItemGroupInfo *info = g_new0(GtkToolItemGroupInfo, 1);
787
788   g_return_if_fail (GTK_IS_TOOL_PALETTE (container));
789   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (child));
790
791   palette = GTK_TOOL_PALETTE (container);
792
793   g_ptr_array_add (palette->priv->groups, info);
794   info->pos = palette->priv->groups->len - 1;
795   info->widget = g_object_ref_sink (child);
796
797   gtk_widget_set_parent (child, GTK_WIDGET (palette));
798 }
799
800 static void
801 gtk_tool_palette_remove (GtkContainer *container,
802                          GtkWidget    *child)
803 {
804   GtkToolPalette *palette;
805   guint i;
806
807   g_return_if_fail (GTK_IS_TOOL_PALETTE (container));
808   palette = GTK_TOOL_PALETTE (container);
809
810   for (i = 0; i < palette->priv->groups->len; ++i)
811     {
812       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
813       if (GTK_WIDGET(info->widget) == child)
814         {
815           g_object_unref (child);
816           gtk_widget_unparent (child);
817
818           g_ptr_array_remove_index (palette->priv->groups, i);
819         }
820     }
821 }
822
823 static void
824 gtk_tool_palette_forall (GtkContainer *container,
825                          gboolean      internals,
826                          GtkCallback   callback,
827                          gpointer      callback_data)
828 {
829   GtkToolPalette *palette = GTK_TOOL_PALETTE (container);
830   guint i;
831
832
833   for (i = 0; i < palette->priv->groups->len; ++i)
834     {
835       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
836       if (info->widget)
837         callback (GTK_WIDGET (info->widget),
838                   callback_data);
839     }
840 }
841
842 static GType
843 gtk_tool_palette_child_type (GtkContainer *container)
844 {
845   return GTK_TYPE_TOOL_ITEM_GROUP;
846 }
847
848 static void
849 gtk_tool_palette_set_child_property (GtkContainer *container,
850                                      GtkWidget    *child,
851                                      guint         prop_id,
852                                      const GValue *value,
853                                      GParamSpec   *pspec)
854 {
855   GtkToolPalette *palette = GTK_TOOL_PALETTE (container);
856
857   switch (prop_id)
858     {
859       case CHILD_PROP_EXCLUSIVE:
860         gtk_tool_palette_set_exclusive (palette, GTK_TOOL_ITEM_GROUP (child), 
861           g_value_get_boolean (value));
862         break;
863
864       case CHILD_PROP_EXPAND:
865         gtk_tool_palette_set_expand (palette, GTK_TOOL_ITEM_GROUP (child), 
866           g_value_get_boolean (value));
867         break;
868
869       default:
870         GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
871         break;
872     }
873 }
874
875 static void
876 gtk_tool_palette_get_child_property (GtkContainer *container,
877                                      GtkWidget    *child,
878                                      guint         prop_id,
879                                      GValue       *value,
880                                      GParamSpec   *pspec)
881 {
882   GtkToolPalette *palette = GTK_TOOL_PALETTE (container);
883
884   switch (prop_id)
885     {
886       case CHILD_PROP_EXCLUSIVE:
887         g_value_set_boolean (value, 
888           gtk_tool_palette_get_exclusive (palette, GTK_TOOL_ITEM_GROUP (child)));
889         break;
890
891       case CHILD_PROP_EXPAND:
892         g_value_set_boolean (value, 
893           gtk_tool_palette_get_expand (palette, GTK_TOOL_ITEM_GROUP (child)));
894         break;
895
896       default:
897         GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
898         break;
899     }
900 }
901
902 static void
903 style_change_notify (GtkToolPalette *palette)
904 {
905   GtkToolPalettePrivate* priv = palette->priv;
906
907   if (!priv->style_set)
908     {
909       /* pretend it was set, then unset, thus reverting to new default */
910       priv->style_set = TRUE;
911       gtk_tool_palette_unset_style (palette);
912     }
913 }
914
915 static void
916 icon_size_change_notify (GtkToolPalette *palette)
917 {
918   GtkToolPalettePrivate* priv = palette->priv;
919
920   if (!priv->icon_size_set)
921     {
922       /* pretend it was set, then unset, thus reverting to new default */
923       priv->icon_size_set = TRUE;
924       gtk_tool_palette_unset_icon_size (palette);
925     }
926 }
927
928 static void
929 gtk_tool_palette_settings_change_notify (GtkSettings      *settings,
930                                          const GParamSpec *pspec,
931                                          GtkToolPalette   *palette)
932 {
933   if (strcmp (pspec->name, "gtk-toolbar-style") == 0)
934     style_change_notify (palette);
935   else if (strcmp (pspec->name, "gtk-toolbar-icon-size") == 0)
936     icon_size_change_notify (palette);
937 }
938
939 static void
940 gtk_tool_palette_screen_changed (GtkWidget *widget,
941                                  GdkScreen *previous_screen)
942 {
943   GtkToolPalette *palette = GTK_TOOL_PALETTE (widget);
944   GtkToolPalettePrivate* priv = palette->priv;
945   GtkSettings *old_settings = priv->settings;
946   GtkSettings *settings;
947
948   if (gtk_widget_has_screen (GTK_WIDGET (palette)))
949     settings = gtk_widget_get_settings (GTK_WIDGET (palette));
950   else
951     settings = NULL;
952
953   if (settings == old_settings)
954     return;
955
956   if (old_settings)
957   {
958     g_signal_handler_disconnect (old_settings, priv->settings_connection);
959     g_object_unref (old_settings);
960   }
961
962   if (settings)
963   {
964     priv->settings_connection =
965       g_signal_connect (settings, "notify",
966                         G_CALLBACK (gtk_tool_palette_settings_change_notify),
967                         palette);
968     priv->settings = g_object_ref (settings);
969   }
970   else
971     priv->settings = NULL;
972
973   gtk_tool_palette_reconfigured (palette);
974 }
975
976
977 static void
978 gtk_tool_palette_class_init (GtkToolPaletteClass *cls)
979 {
980   GObjectClass      *oclass   = G_OBJECT_CLASS (cls);
981   GtkWidgetClass    *wclass   = GTK_WIDGET_CLASS (cls);
982   GtkContainerClass *cclass   = GTK_CONTAINER_CLASS (cls);
983
984   oclass->set_property        = gtk_tool_palette_set_property;
985   oclass->get_property        = gtk_tool_palette_get_property;
986   oclass->dispose             = gtk_tool_palette_dispose;
987   oclass->finalize            = gtk_tool_palette_finalize;
988
989   wclass->get_preferred_width = gtk_tool_palette_get_preferred_width;
990   wclass->get_preferred_height= gtk_tool_palette_get_preferred_height;
991   wclass->size_allocate       = gtk_tool_palette_size_allocate;
992   wclass->draw                = gtk_tool_palette_draw;
993   wclass->realize             = gtk_tool_palette_realize;
994
995   cclass->add                 = gtk_tool_palette_add;
996   cclass->remove              = gtk_tool_palette_remove;
997   cclass->forall              = gtk_tool_palette_forall;
998   cclass->child_type          = gtk_tool_palette_child_type;
999   cclass->set_child_property  = gtk_tool_palette_set_child_property;
1000   cclass->get_child_property  = gtk_tool_palette_get_child_property;
1001
1002   /* Handle screen-changed so we can update our GtkSettings.
1003    */
1004   wclass->screen_changed      = gtk_tool_palette_screen_changed;
1005
1006   g_object_class_override_property (oclass, PROP_ORIENTATION,    "orientation");
1007
1008   g_object_class_override_property (oclass, PROP_HADJUSTMENT,    "hadjustment");
1009   g_object_class_override_property (oclass, PROP_VADJUSTMENT,    "vadjustment");
1010   g_object_class_override_property (oclass, PROP_HSCROLL_POLICY, "hscroll-policy");
1011   g_object_class_override_property (oclass, PROP_VSCROLL_POLICY, "vscroll-policy");
1012
1013   /**
1014    * GtkToolPalette:icon-size:
1015    *
1016    * The size of the icons in a tool palette is normally determined by
1017    * the #GtkSettings:toolbar-icon-size setting. When this property is set,
1018    * it overrides the setting.
1019    *
1020    * This should only be used for special-purpose tool palettes, normal
1021    * application tool palettes should respect the user preferences for the
1022    * size of icons.
1023    *
1024    * Since: 2.20
1025    */
1026   g_object_class_install_property (oclass,
1027                                    PROP_ICON_SIZE,
1028                                    g_param_spec_enum ("icon-size",
1029                                                       P_("Icon size"),
1030                                                       P_("Size of icons in this tool palette"),
1031                                                       GTK_TYPE_ICON_SIZE,
1032                                                       DEFAULT_ICON_SIZE,
1033                                                       GTK_PARAM_READWRITE));
1034
1035   /**
1036    * GtkToolPalette:icon-size-set:
1037    *
1038    * Is %TRUE if the #GtkToolPalette:icon-size property has been set.
1039    *
1040    * Since: 2.20
1041    */
1042   g_object_class_install_property (oclass,
1043                                    PROP_ICON_SIZE_SET,
1044                                    g_param_spec_boolean ("icon-size-set",
1045                                                       P_("Icon size set"),
1046                                                       P_("Whether the icon-size property has been set"),
1047                                                       FALSE,
1048                                                       GTK_PARAM_READWRITE));
1049
1050   /**
1051    * GtkToolPalette:toolbar-style:
1052    *
1053    * The style of items in the tool palette.
1054    *
1055    * Since: 2.20
1056    */
1057   g_object_class_install_property (oclass, PROP_TOOLBAR_STYLE,
1058                                    g_param_spec_enum ("toolbar-style",
1059                                                       P_("Toolbar Style"),
1060                                                       P_("Style of items in the tool palette"),
1061                                                       GTK_TYPE_TOOLBAR_STYLE,
1062                                                       DEFAULT_TOOLBAR_STYLE,
1063                                                       GTK_PARAM_READWRITE));
1064
1065
1066   /**
1067    * GtkToolPalette:exclusive:
1068    *
1069    * Whether the item group should be the only one that is expanded
1070    * at a given time.
1071    *
1072    * Since: 2.20
1073    */
1074   gtk_container_class_install_child_property (cclass, CHILD_PROP_EXCLUSIVE,
1075                                               g_param_spec_boolean ("exclusive",
1076                                                                     P_("Exclusive"),
1077                                                                     P_("Whether the item group should be the only expanded at a given time"),
1078                                                                     DEFAULT_CHILD_EXCLUSIVE,
1079                                                                     GTK_PARAM_READWRITE));
1080
1081   /**
1082    * GtkToolPalette:expand:
1083    *
1084    * Whether the item group should receive extra space when the palette grows.
1085    * at a given time.
1086    *
1087    * Since: 2.20
1088    */
1089   gtk_container_class_install_child_property (cclass, CHILD_PROP_EXPAND,
1090                                               g_param_spec_boolean ("expand",
1091                                                                     P_("Expand"),
1092                                                                     P_("Whether the item group should receive extra space when the palette grows"),
1093                                                                     DEFAULT_CHILD_EXPAND,
1094                                                                     GTK_PARAM_READWRITE));
1095
1096   g_type_class_add_private (cls, sizeof (GtkToolPalettePrivate));
1097
1098   dnd_target_atom_item = gdk_atom_intern_static_string (dnd_targets[0].target);
1099   dnd_target_atom_group = gdk_atom_intern_static_string (dnd_targets[1].target);
1100 }
1101
1102 /**
1103  * gtk_tool_palette_new:
1104  *
1105  * Creates a new tool palette.
1106  *
1107  * Returns: a new #GtkToolPalette
1108  *
1109  * Since: 2.20
1110  */
1111 GtkWidget*
1112 gtk_tool_palette_new (void)
1113 {
1114   return g_object_new (GTK_TYPE_TOOL_PALETTE, NULL);
1115 }
1116
1117 /**
1118  * gtk_tool_palette_set_icon_size:
1119  * @palette: a #GtkToolPalette
1120  * @icon_size: (type int): the #GtkIconSize that icons in the tool
1121  *     palette shall have
1122  *
1123  * Sets the size of icons in the tool palette.
1124  *
1125  * Since: 2.20
1126  */
1127 void
1128 gtk_tool_palette_set_icon_size (GtkToolPalette *palette,
1129                                 GtkIconSize     icon_size)
1130 {
1131   GtkToolPalettePrivate *priv;
1132
1133   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1134   g_return_if_fail (icon_size != GTK_ICON_SIZE_INVALID);
1135
1136   priv = palette->priv;
1137
1138   if (!priv->icon_size_set)
1139     {
1140       priv->icon_size_set = TRUE;
1141       g_object_notify (G_OBJECT (palette), "icon-size-set");
1142     }
1143
1144   if (priv->icon_size == icon_size)
1145     return;
1146
1147   priv->icon_size = icon_size;
1148   g_object_notify (G_OBJECT (palette), "icon-size");
1149
1150   gtk_tool_palette_reconfigured (palette);
1151
1152   gtk_widget_queue_resize (GTK_WIDGET (palette));
1153 }
1154
1155 static GtkSettings *
1156 toolpalette_get_settings (GtkToolPalette *palette)
1157 {
1158   GtkToolPalettePrivate *priv = palette->priv;
1159   return priv->settings;
1160 }
1161
1162 /**
1163  * gtk_tool_palette_unset_icon_size:
1164  * @palette: a #GtkToolPalette
1165  *
1166  * Unsets the tool palette icon size set with gtk_tool_palette_set_icon_size(),
1167  * so that user preferences will be used to determine the icon size.
1168  *
1169  * Since: 2.20
1170  */
1171 void
1172 gtk_tool_palette_unset_icon_size (GtkToolPalette *palette)
1173 {
1174   GtkToolPalettePrivate* priv = palette->priv;
1175   GtkIconSize size;
1176
1177   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1178
1179   if (palette->priv->icon_size_set)
1180     {
1181       GtkSettings *settings = toolpalette_get_settings (palette);
1182
1183       if (settings)
1184         {
1185           g_object_get (settings,
1186             "gtk-toolbar-icon-size", &size,
1187             NULL);
1188         }
1189       else
1190         size = DEFAULT_ICON_SIZE;
1191
1192       if (size != palette->priv->icon_size)
1193       {
1194         gtk_tool_palette_set_icon_size (palette, size);
1195         g_object_notify (G_OBJECT (palette), "icon-size");
1196             }
1197
1198       priv->icon_size_set = FALSE;
1199       g_object_notify (G_OBJECT (palette), "icon-size-set");
1200     }
1201 }
1202
1203 /* Set the "toolbar-style" property and do appropriate things.
1204  * GtkToolbar does this by emitting a signal instead of just
1205  * calling a function...
1206  */
1207 static void
1208 gtk_tool_palette_change_style (GtkToolPalette  *palette,
1209                                GtkToolbarStyle  style)
1210 {
1211   GtkToolPalettePrivate* priv = palette->priv;
1212
1213   if (priv->style != style)
1214     {
1215       priv->style = style;
1216
1217       gtk_tool_palette_reconfigured (palette);
1218
1219       gtk_widget_queue_resize (GTK_WIDGET (palette));
1220       g_object_notify (G_OBJECT (palette), "toolbar-style");
1221     }
1222 }
1223
1224
1225 /**
1226  * gtk_tool_palette_set_style:
1227  * @palette: a #GtkToolPalette
1228  * @style: the #GtkToolbarStyle that items in the tool palette shall have
1229  *
1230  * Sets the style (text, icons or both) of items in the tool palette.
1231  *
1232  * Since: 2.20
1233  */
1234 void
1235 gtk_tool_palette_set_style (GtkToolPalette  *palette,
1236                             GtkToolbarStyle  style)
1237 {
1238   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1239
1240   palette->priv->style_set = TRUE;
1241   gtk_tool_palette_change_style (palette, style);
1242 }
1243
1244
1245 /**
1246  * gtk_tool_palette_unset_style:
1247  * @palette: a #GtkToolPalette
1248  *
1249  * Unsets a toolbar style set with gtk_tool_palette_set_style(),
1250  * so that user preferences will be used to determine the toolbar style.
1251  *
1252  * Since: 2.20
1253  */
1254 void
1255 gtk_tool_palette_unset_style (GtkToolPalette *palette)
1256 {
1257   GtkToolPalettePrivate* priv = palette->priv;
1258   GtkToolbarStyle style;
1259
1260   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1261
1262   if (priv->style_set)
1263     {
1264       GtkSettings *settings = toolpalette_get_settings (palette);
1265
1266       if (settings)
1267         g_object_get (settings,
1268                       "gtk-toolbar-style", &style,
1269                       NULL);
1270       else
1271         style = DEFAULT_TOOLBAR_STYLE;
1272
1273       if (style != priv->style)
1274         gtk_tool_palette_change_style (palette, style);
1275
1276       priv->style_set = FALSE;
1277     }
1278 }
1279
1280 /**
1281  * gtk_tool_palette_get_icon_size:
1282  * @palette: a #GtkToolPalette
1283  *
1284  * Gets the size of icons in the tool palette.
1285  * See gtk_tool_palette_set_icon_size().
1286  *
1287  * Returns: (type int): the #GtkIconSize of icons in the tool palette
1288  *
1289  * Since: 2.20
1290  */
1291 GtkIconSize
1292 gtk_tool_palette_get_icon_size (GtkToolPalette *palette)
1293 {
1294   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_ICON_SIZE);
1295
1296   return palette->priv->icon_size;
1297 }
1298
1299 /**
1300  * gtk_tool_palette_get_style:
1301  * @palette: a #GtkToolPalette
1302  *
1303  * Gets the style (icons, text or both) of items in the tool palette.
1304  *
1305  * Returns: the #GtkToolbarStyle of items in the tool palette.
1306  *
1307  * Since: 2.20
1308  */
1309 GtkToolbarStyle
1310 gtk_tool_palette_get_style (GtkToolPalette *palette)
1311 {
1312   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_TOOLBAR_STYLE);
1313
1314   return palette->priv->style;
1315 }
1316
1317 gint
1318 _gtk_tool_palette_compare_groups (gconstpointer a,
1319                                   gconstpointer b)
1320 {
1321   const GtkToolItemGroupInfo *group_a = a;
1322   const GtkToolItemGroupInfo *group_b = b;
1323
1324   return group_a->pos - group_b->pos;
1325 }
1326
1327 /**
1328  * gtk_tool_palette_set_group_position:
1329  * @palette: a #GtkToolPalette
1330  * @group: a #GtkToolItemGroup which is a child of palette
1331  * @position: a new index for group
1332  *
1333  * Sets the position of the group as an index of the tool palette.
1334  * If position is 0 the group will become the first child, if position is
1335  * -1 it will become the last child.
1336  *
1337  * Since: 2.20
1338  */
1339 void
1340 gtk_tool_palette_set_group_position (GtkToolPalette   *palette,
1341                                      GtkToolItemGroup *group,
1342                                      gint             position)
1343 {
1344   GtkToolItemGroupInfo *group_new;
1345   GtkToolItemGroupInfo *group_old;
1346   gint old_position;
1347
1348   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1349   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1350   g_return_if_fail (position >= -1);
1351
1352   if (-1 == position)
1353     position = palette->priv->groups->len - 1;
1354
1355   g_return_if_fail ((guint) position < palette->priv->groups->len);
1356
1357   group_new = g_ptr_array_index (palette->priv->groups, position);
1358
1359   if (GTK_TOOL_ITEM_GROUP (group) == group_new->widget)
1360     return;
1361
1362   old_position = gtk_tool_palette_get_group_position (palette, group);
1363   g_return_if_fail (old_position >= 0);
1364
1365   group_old = g_ptr_array_index (palette->priv->groups, old_position);
1366
1367   group_new->pos = position;
1368   group_old->pos = old_position;
1369
1370   g_ptr_array_sort (palette->priv->groups, _gtk_tool_palette_compare_groups);
1371
1372   gtk_widget_queue_resize (GTK_WIDGET (palette));
1373 }
1374
1375 static void
1376 gtk_tool_palette_group_notify_collapsed (GtkToolItemGroup *group,
1377                                          GParamSpec       *pspec,
1378                                          gpointer          data)
1379 {
1380   GtkToolPalette *palette = GTK_TOOL_PALETTE (data);
1381   guint i;
1382
1383   if (gtk_tool_item_group_get_collapsed (group))
1384     return;
1385
1386   for (i = 0; i < palette->priv->groups->len; ++i)
1387     {
1388       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
1389       GtkToolItemGroup *current_group = info->widget;
1390
1391       if (current_group && current_group != group)
1392         gtk_tool_item_group_set_collapsed (current_group, TRUE);
1393     }
1394 }
1395
1396 /**
1397  * gtk_tool_palette_set_exclusive:
1398  * @palette: a #GtkToolPalette
1399  * @group: a #GtkToolItemGroup which is a child of palette
1400  * @exclusive: whether the group should be exclusive or not
1401  *
1402  * Sets whether the group should be exclusive or not.
1403  * If an exclusive group is expanded all other groups are collapsed.
1404  *
1405  * Since: 2.20
1406  */
1407 void
1408 gtk_tool_palette_set_exclusive (GtkToolPalette   *palette,
1409                                 GtkToolItemGroup *group,
1410                                 gboolean          exclusive)
1411 {
1412   GtkToolItemGroupInfo *group_info;
1413   gint position;
1414
1415   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1416   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1417
1418   position = gtk_tool_palette_get_group_position (palette, group);
1419   g_return_if_fail (position >= 0);
1420
1421   group_info = g_ptr_array_index (palette->priv->groups, position);
1422
1423   if (exclusive == group_info->exclusive)
1424     return;
1425
1426   group_info->exclusive = exclusive;
1427
1428   if (group_info->exclusive != (0 != group_info->notify_collapsed))
1429     {
1430       if (group_info->exclusive)
1431         {
1432           group_info->notify_collapsed =
1433             g_signal_connect (group, "notify::collapsed",
1434                               G_CALLBACK (gtk_tool_palette_group_notify_collapsed),
1435                               palette);
1436         }
1437       else
1438         {
1439           g_signal_handler_disconnect (group, group_info->notify_collapsed);
1440           group_info->notify_collapsed = 0;
1441         }
1442     }
1443
1444   gtk_tool_palette_group_notify_collapsed (group_info->widget, NULL, palette);
1445   gtk_widget_child_notify (GTK_WIDGET (group), "exclusive");
1446 }
1447
1448 /**
1449  * gtk_tool_palette_set_expand:
1450  * @palette: a #GtkToolPalette
1451  * @group: a #GtkToolItemGroup which is a child of palette
1452  * @expand: whether the group should be given extra space
1453  *
1454  * Sets whether the group should be given extra space.
1455  *
1456  * Since: 2.20
1457  */
1458 void
1459 gtk_tool_palette_set_expand (GtkToolPalette   *palette,
1460                              GtkToolItemGroup *group,
1461                              gboolean        expand)
1462 {
1463   GtkToolItemGroupInfo *group_info;
1464   gint position;
1465
1466   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1467   g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
1468
1469   position = gtk_tool_palette_get_group_position (palette, group);
1470   g_return_if_fail (position >= 0);
1471
1472   group_info = g_ptr_array_index (palette->priv->groups, position);
1473
1474   if (expand != group_info->expand)
1475     {
1476       group_info->expand = expand;
1477       gtk_widget_queue_resize (GTK_WIDGET (palette));
1478       gtk_widget_child_notify (GTK_WIDGET (group), "expand");
1479     }
1480 }
1481
1482 /**
1483  * gtk_tool_palette_get_group_position:
1484  * @palette: a #GtkToolPalette
1485  * @group: a #GtkToolItemGroup
1486  *
1487  * Gets the position of @group in @palette as index.
1488  * See gtk_tool_palette_set_group_position().
1489  *
1490  * Returns: the index of group or -1 if @group is not a child of @palette
1491  *
1492  * Since: 2.20
1493  */
1494 gint
1495 gtk_tool_palette_get_group_position (GtkToolPalette   *palette,
1496                                      GtkToolItemGroup *group)
1497 {
1498   guint i;
1499
1500   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), -1);
1501   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), -1);
1502
1503   for (i = 0; i < palette->priv->groups->len; ++i)
1504     {
1505       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
1506       if ((gpointer) group == info->widget)
1507         return i;
1508     }
1509
1510   return -1;
1511 }
1512
1513 /**
1514  * gtk_tool_palette_get_exclusive:
1515  * @palette: a #GtkToolPalette
1516  * @group: a #GtkToolItemGroup which is a child of palette
1517  *
1518  * Gets whether @group is exclusive or not.
1519  * See gtk_tool_palette_set_exclusive().
1520  *
1521  * Returns: %TRUE if @group is exclusive
1522  *
1523  * Since: 2.20
1524  */
1525 gboolean
1526 gtk_tool_palette_get_exclusive (GtkToolPalette   *palette,
1527                                 GtkToolItemGroup *group)
1528 {
1529   gint position;
1530   GtkToolItemGroupInfo *info;
1531
1532   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXCLUSIVE);
1533   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXCLUSIVE);
1534
1535   position = gtk_tool_palette_get_group_position (palette, group);
1536   g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXCLUSIVE);
1537
1538   info = g_ptr_array_index (palette->priv->groups, position);
1539
1540   return info->exclusive;
1541 }
1542
1543 /**
1544  * gtk_tool_palette_get_expand:
1545  * @palette: a #GtkToolPalette
1546  * @group: a #GtkToolItemGroup which is a child of palette
1547  *
1548  * Gets whether group should be given extra space.
1549  * See gtk_tool_palette_set_expand().
1550  *
1551  * Returns: %TRUE if group should be given extra space, %FALSE otherwise
1552  *
1553  * Since: 2.20
1554  */
1555 gboolean
1556 gtk_tool_palette_get_expand (GtkToolPalette   *palette,
1557                              GtkToolItemGroup *group)
1558 {
1559   gint position;
1560   GtkToolItemGroupInfo *info;
1561
1562   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXPAND);
1563   g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXPAND);
1564
1565   position = gtk_tool_palette_get_group_position (palette, group);
1566   g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXPAND);
1567
1568   info = g_ptr_array_index (palette->priv->groups, position);
1569
1570   return info->expand;
1571 }
1572
1573 /**
1574  * gtk_tool_palette_get_drop_item:
1575  * @palette: a #GtkToolPalette
1576  * @x: the x position
1577  * @y: the y position
1578  *
1579  * Gets the item at position (x, y).
1580  * See gtk_tool_palette_get_drop_group().
1581  *
1582  * Returns: (transfer none): the #GtkToolItem at position or %NULL if there is no such item
1583  *
1584  * Since: 2.20
1585  */
1586 GtkToolItem*
1587 gtk_tool_palette_get_drop_item (GtkToolPalette *palette,
1588                                 gint            x,
1589                                 gint            y)
1590 {
1591   GtkAllocation allocation;
1592   GtkToolItemGroup *group = gtk_tool_palette_get_drop_group (palette, x, y);
1593   GtkWidget *widget = GTK_WIDGET (group);
1594
1595   if (group)
1596     {
1597       gtk_widget_get_allocation (widget, &allocation);
1598       return gtk_tool_item_group_get_drop_item (group,
1599                                                 x - allocation.x,
1600                                                 y - allocation.y);
1601     }
1602
1603   return NULL;
1604 }
1605
1606 /**
1607  * gtk_tool_palette_get_drop_group:
1608  * @palette: a #GtkToolPalette
1609  * @x: the x position
1610  * @y: the y position
1611  *
1612  * Gets the group at position (x, y).
1613  *
1614  * Returns: (transfer none): the #GtkToolItemGroup at position or %NULL
1615  *     if there is no such group
1616  *
1617  * Since: 2.20
1618  */
1619 GtkToolItemGroup*
1620 gtk_tool_palette_get_drop_group (GtkToolPalette *palette,
1621                                  gint            x,
1622                                  gint            y)
1623 {
1624   GtkAllocation allocation;
1625   guint i;
1626
1627   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
1628
1629   gtk_widget_get_allocation (GTK_WIDGET (palette), &allocation);
1630
1631   g_return_val_if_fail (x >= 0 && x < allocation.width, NULL);
1632   g_return_val_if_fail (y >= 0 && y < allocation.height, NULL);
1633
1634   for (i = 0; i < palette->priv->groups->len; ++i)
1635     {
1636       GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
1637       GtkWidget *widget;
1638       gint x0, y0;
1639
1640       if (!group->widget)
1641         continue;
1642
1643       widget = GTK_WIDGET (group->widget);
1644       gtk_widget_get_allocation (widget, &allocation);
1645
1646       x0 = x - allocation.x;
1647       y0 = y - allocation.y;
1648
1649       if (x0 >= 0 && x0 < allocation.width &&
1650           y0 >= 0 && y0 < allocation.height)
1651         return GTK_TOOL_ITEM_GROUP (widget);
1652     }
1653
1654   return NULL;
1655 }
1656
1657 /**
1658  * gtk_tool_palette_get_drag_item:
1659  * @palette: a #GtkToolPalette
1660  * @selection: a #GtkSelectionData
1661  *
1662  * Get the dragged item from the selection.
1663  * This could be a #GtkToolItem or a #GtkToolItemGroup.
1664  *
1665  * Returns: (transfer none): the dragged item in selection
1666  *
1667  * Since: 2.20
1668  */
1669 GtkWidget*
1670 gtk_tool_palette_get_drag_item (GtkToolPalette         *palette,
1671                                 const GtkSelectionData *selection)
1672 {
1673   GtkToolPaletteDragData *data;
1674   GdkAtom target;
1675
1676   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
1677   g_return_val_if_fail (NULL != selection, NULL);
1678
1679   g_return_val_if_fail (gtk_selection_data_get_format (selection) == 8, NULL);
1680   g_return_val_if_fail (gtk_selection_data_get_length (selection) == sizeof (GtkToolPaletteDragData), NULL);
1681   target = gtk_selection_data_get_target (selection);
1682   g_return_val_if_fail (target == dnd_target_atom_item ||
1683                         target == dnd_target_atom_group,
1684                         NULL);
1685
1686   data = (GtkToolPaletteDragData*) gtk_selection_data_get_data (selection);
1687
1688   g_return_val_if_fail (data->palette == palette, NULL);
1689
1690   if (dnd_target_atom_item == target)
1691     g_return_val_if_fail (GTK_IS_TOOL_ITEM (data->item), NULL);
1692   else if (dnd_target_atom_group == target)
1693     g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (data->item), NULL);
1694
1695   return data->item;
1696 }
1697
1698 /**
1699  * gtk_tool_palette_set_drag_source:
1700  * @palette: a #GtkToolPalette
1701  * @targets: the #GtkToolPaletteDragTarget<!-- -->s
1702  *     which the widget should support
1703  *
1704  * Sets the tool palette as a drag source.
1705  * Enables all groups and items in the tool palette as drag sources
1706  * on button 1 and button 3 press with copy and move actions.
1707  * See gtk_drag_source_set().
1708  *
1709  * Since: 2.20
1710  */
1711 void
1712 gtk_tool_palette_set_drag_source (GtkToolPalette            *palette,
1713                                   GtkToolPaletteDragTargets  targets)
1714 {
1715   guint i;
1716
1717   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1718
1719   if ((palette->priv->drag_source & targets) == targets)
1720     return;
1721
1722   palette->priv->drag_source |= targets;
1723
1724   for (i = 0; i < palette->priv->groups->len; ++i)
1725     {
1726       GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
1727       if (info->widget)
1728         gtk_container_forall (GTK_CONTAINER (info->widget),
1729                               _gtk_tool_palette_child_set_drag_source,
1730                               palette);
1731     }
1732 }
1733
1734 /**
1735  * gtk_tool_palette_add_drag_dest:
1736  * @palette: a #GtkToolPalette
1737  * @widget: a #GtkWidget which should be a drag destination for @palette
1738  * @flags: the flags that specify what actions GTK+ should take for drops
1739  *     on that widget
1740  * @targets: the #GtkToolPaletteDragTarget<!-- -->s which the widget
1741  *     should support
1742  * @actions: the #GdkDragAction<!-- -->s which the widget should suppport
1743  *
1744  * Sets @palette as drag source (see gtk_tool_palette_set_drag_source())
1745  * and sets @widget as a drag destination for drags from @palette.
1746  * See gtk_drag_dest_set().
1747  *
1748  * Since: 2.20
1749  */
1750 void
1751 gtk_tool_palette_add_drag_dest (GtkToolPalette            *palette,
1752                                 GtkWidget                 *widget,
1753                                 GtkDestDefaults            flags,
1754                                 GtkToolPaletteDragTargets  targets,
1755                                 GdkDragAction              actions)
1756 {
1757   GtkTargetEntry entries[G_N_ELEMENTS (dnd_targets)];
1758   gint n_entries = 0;
1759
1760   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1761   g_return_if_fail (GTK_IS_WIDGET (widget));
1762
1763   gtk_tool_palette_set_drag_source (palette,
1764                                     targets);
1765
1766   if (targets & GTK_TOOL_PALETTE_DRAG_ITEMS)
1767     entries[n_entries++] = dnd_targets[0];
1768   if (targets & GTK_TOOL_PALETTE_DRAG_GROUPS)
1769     entries[n_entries++] = dnd_targets[1];
1770
1771   gtk_drag_dest_set (widget, flags, entries, n_entries, actions);
1772 }
1773
1774 void
1775 _gtk_tool_palette_get_item_size (GtkToolPalette *palette,
1776                                  GtkRequisition *item_size,
1777                                  gboolean        homogeneous_only,
1778                                  gint           *requested_rows)
1779 {
1780   GtkRequisition max_requisition;
1781   gint max_rows;
1782   guint i;
1783
1784   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1785   g_return_if_fail (NULL != item_size);
1786
1787   max_requisition.width = 0;
1788   max_requisition.height = 0;
1789   max_rows = 0;
1790
1791   /* iterate over all groups and calculate the max item_size and max row request */
1792   for (i = 0; i < palette->priv->groups->len; ++i)
1793     {
1794       GtkRequisition requisition;
1795       gint rows;
1796       GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
1797
1798       if (!group->widget)
1799         continue;
1800
1801       _gtk_tool_item_group_item_size_request (group->widget, &requisition, homogeneous_only, &rows);
1802
1803       max_requisition.width = MAX (max_requisition.width, requisition.width);
1804       max_requisition.height = MAX (max_requisition.height, requisition.height);
1805       max_rows = MAX (max_rows, rows);
1806     }
1807
1808   *item_size = max_requisition;
1809   if (requested_rows)
1810     *requested_rows = max_rows;
1811 }
1812
1813 static void
1814 gtk_tool_palette_item_drag_data_get (GtkWidget        *widget,
1815                                      GdkDragContext   *context,
1816                                      GtkSelectionData *selection,
1817                                      guint             info,
1818                                      guint             time,
1819                                      gpointer          data)
1820 {
1821   GtkToolPaletteDragData drag_data = { GTK_TOOL_PALETTE (data), NULL };
1822   GdkAtom target;
1823
1824   target = gtk_selection_data_get_target (selection);
1825
1826   if (target == dnd_target_atom_item)
1827     drag_data.item = gtk_widget_get_ancestor (widget, GTK_TYPE_TOOL_ITEM);
1828
1829   if (drag_data.item)
1830     gtk_selection_data_set (selection, target, 8,
1831                             (guchar*) &drag_data, sizeof (drag_data));
1832 }
1833
1834 static void
1835 gtk_tool_palette_child_drag_data_get (GtkWidget        *widget,
1836                                       GdkDragContext   *context,
1837                                       GtkSelectionData *selection,
1838                                       guint             info,
1839                                       guint             time,
1840                                       gpointer          data)
1841 {
1842   GtkToolPaletteDragData drag_data = { GTK_TOOL_PALETTE (data), NULL };
1843   GdkAtom target;
1844
1845   target = gtk_selection_data_get_target (selection);
1846
1847   if (target == dnd_target_atom_group)
1848     drag_data.item = gtk_widget_get_ancestor (widget, GTK_TYPE_TOOL_ITEM_GROUP);
1849
1850   if (drag_data.item)
1851     gtk_selection_data_set (selection, target, 8,
1852                             (guchar*) &drag_data, sizeof (drag_data));
1853 }
1854
1855 void
1856 _gtk_tool_palette_child_set_drag_source (GtkWidget *child,
1857                                          gpointer   data)
1858 {
1859   GtkToolPalette *palette = GTK_TOOL_PALETTE (data);
1860
1861   /* Check drag_source,
1862    * to work properly when called from gtk_tool_item_group_insert().
1863    */
1864   if (!palette->priv->drag_source)
1865     return;
1866
1867   if (GTK_IS_TOOL_ITEM (child) &&
1868       (palette->priv->drag_source & GTK_TOOL_PALETTE_DRAG_ITEMS))
1869     {
1870       /* Connect to child instead of the item itself,
1871        * to work arround bug 510377.
1872        */
1873       if (GTK_IS_TOOL_BUTTON (child))
1874         child = gtk_bin_get_child (GTK_BIN (child));
1875
1876       if (!child)
1877         return;
1878
1879       gtk_drag_source_set (child, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1880                            &dnd_targets[0], 1, GDK_ACTION_COPY | GDK_ACTION_MOVE);
1881
1882       g_signal_connect (child, "drag-data-get",
1883                         G_CALLBACK (gtk_tool_palette_item_drag_data_get),
1884                         palette);
1885     }
1886   else if (GTK_IS_BUTTON (child) &&
1887            (palette->priv->drag_source & GTK_TOOL_PALETTE_DRAG_GROUPS))
1888     {
1889       gtk_drag_source_set (child, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1890                            &dnd_targets[1], 1, GDK_ACTION_COPY | GDK_ACTION_MOVE);
1891
1892       g_signal_connect (child, "drag-data-get",
1893                         G_CALLBACK (gtk_tool_palette_child_drag_data_get),
1894                         palette);
1895     }
1896 }
1897
1898 /**
1899  * gtk_tool_palette_get_drag_target_item:
1900  *
1901  * Gets the target entry for a dragged #GtkToolItem.
1902  *
1903  * Returns: (transfer none): the #GtkTargetEntry for a dragged item.
1904  *
1905  * Since: 2.20
1906  */
1907 G_CONST_RETURN GtkTargetEntry*
1908 gtk_tool_palette_get_drag_target_item (void)
1909 {
1910   return &dnd_targets[0];
1911 }
1912
1913 /**
1914  * gtk_tool_palette_get_drag_target_group:
1915  *
1916  * Get the target entry for a dragged #GtkToolItemGroup.
1917  *
1918  * Returns: (transfer none): the #GtkTargetEntry for a dragged group
1919  *
1920  * Since: 2.20
1921  */
1922 G_CONST_RETURN GtkTargetEntry*
1923 gtk_tool_palette_get_drag_target_group (void)
1924 {
1925   return &dnd_targets[1];
1926 }
1927
1928 void
1929 _gtk_tool_palette_set_expanding_child (GtkToolPalette *palette,
1930                                        GtkWidget      *widget)
1931 {
1932   g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1933   palette->priv->expanding_child = widget;
1934 }
1935
1936 /**
1937  * gtk_tool_palette_get_hadjustment:
1938  * @palette: a #GtkToolPalette
1939  *
1940  * Gets the horizontal adjustment of the tool palette.
1941  *
1942  * Returns: (transfer none): the horizontal adjustment of @palette
1943  *
1944  * Since: 2.20
1945  *
1946  * Deprecated: 3.0: Use gtk_scrollable_get_hadjustment()
1947  */
1948 GtkAdjustment*
1949 gtk_tool_palette_get_hadjustment (GtkToolPalette *palette)
1950 {
1951   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
1952
1953   return palette->priv->hadjustment;
1954 }
1955
1956 static void
1957 gtk_tool_palette_set_hadjustment (GtkToolPalette *palette,
1958                                   GtkAdjustment  *adjustment)
1959 {
1960   GtkToolPalettePrivate *priv = palette->priv;
1961
1962   if (adjustment && priv->hadjustment == adjustment)
1963     return;
1964
1965   if (priv->hadjustment != NULL)
1966     {
1967       g_signal_handlers_disconnect_by_func (priv->hadjustment,
1968                                             gtk_tool_palette_adjustment_value_changed,
1969                                             palette);
1970       g_object_unref (priv->hadjustment);
1971     }
1972
1973   if (adjustment == NULL)
1974     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
1975                                      0.0, 0.0, 0.0);
1976
1977   g_signal_connect (adjustment, "value-changed",
1978                     G_CALLBACK (gtk_tool_palette_adjustment_value_changed),
1979                     palette);
1980   priv->hadjustment = g_object_ref_sink (adjustment);
1981   /* FIXME: Adjustment should probably have it's values updated now */
1982   g_object_notify (G_OBJECT (palette), "hadjustment");
1983 }
1984
1985 /**
1986  * gtk_tool_palette_get_vadjustment:
1987  * @palette: a #GtkToolPalette
1988  *
1989  * Gets the vertical adjustment of the tool palette.
1990  *
1991  * Returns: (transfer none): the vertical adjustment of @palette
1992  *
1993  * Since: 2.20
1994  *
1995  * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
1996  */
1997 GtkAdjustment*
1998 gtk_tool_palette_get_vadjustment (GtkToolPalette *palette)
1999 {
2000   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
2001
2002   return palette->priv->vadjustment;
2003 }
2004
2005 static void
2006 gtk_tool_palette_set_vadjustment (GtkToolPalette *palette,
2007                                   GtkAdjustment  *adjustment)
2008 {
2009   GtkToolPalettePrivate *priv = palette->priv;
2010
2011   if (adjustment && priv->vadjustment == adjustment)
2012     return;
2013
2014   if (priv->vadjustment != NULL)
2015     {
2016       g_signal_handlers_disconnect_by_func (priv->vadjustment,
2017                                             gtk_tool_palette_adjustment_value_changed,
2018                                             palette);
2019       g_object_unref (priv->vadjustment);
2020     }
2021
2022   if (adjustment == NULL)
2023     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
2024                                      0.0, 0.0, 0.0);
2025
2026   g_signal_connect (adjustment, "value-changed",
2027                     G_CALLBACK (gtk_tool_palette_adjustment_value_changed),
2028                     palette);
2029   priv->vadjustment = g_object_ref_sink (adjustment);
2030   /* FIXME: Adjustment should probably have it's values updated now */
2031   g_object_notify (G_OBJECT (palette), "vadjustment");
2032 }
2033
2034 GtkSizeGroup *
2035 _gtk_tool_palette_get_size_group (GtkToolPalette *palette)
2036 {
2037   g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);
2038
2039   return palette->priv->text_size_group;
2040 }