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