gtktoolpalette.c 55.7 KB
Newer Older
Johannes Schmid's avatar
Johannes Schmid committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* GtkToolPalette -- A tool palette with categories and DnD support
 * Copyright (C) 2008  Openismus GmbH
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Johannes Schmid's avatar
Johannes Schmid committed
16 17 18 19 20
 *
 * Authors:
 *      Mathias Hasselmann
 */

21 22 23 24 25
#include "config.h"

#include <string.h>
#include <gtk/gtk.h>

Johannes Schmid's avatar
Johannes Schmid committed
26 27
#include "gtktoolpaletteprivate.h"
#include "gtkmarshalers.h"
28
#include "gtktypebuiltins.h"
29
#include "gtkprivate.h"
30
#include "gtkscrollable.h"
31
#include "gtkorientableprivate.h"
32
#include "gtkintl.h"
Johannes Schmid's avatar
Johannes Schmid committed
33 34 35 36 37 38 39 40 41

#define DEFAULT_ICON_SIZE       GTK_ICON_SIZE_SMALL_TOOLBAR
#define DEFAULT_ORIENTATION     GTK_ORIENTATION_VERTICAL
#define DEFAULT_TOOLBAR_STYLE   GTK_TOOLBAR_ICONS

#define DEFAULT_CHILD_EXCLUSIVE FALSE
#define DEFAULT_CHILD_EXPAND    FALSE

/**
42 43 44
 * SECTION:gtktoolpalette
 * @Short_description: A tool palette with categories
 * @Title: GtkToolPalette
Johannes Schmid's avatar
Johannes Schmid committed
45
 *
46
 * A #GtkToolPalette allows you to add #GtkToolItems to a palette-like
47
 * container with different categories and drag and drop support.
Johannes Schmid's avatar
Johannes Schmid committed
48
 *
49
 * A #GtkToolPalette is created with a call to gtk_tool_palette_new().
Johannes Schmid's avatar
Johannes Schmid committed
50
 *
51
 * #GtkToolItems cannot be added directly to a #GtkToolPalette - 
52 53 54
 * instead they are added to a #GtkToolItemGroup which can than be added
 * to a #GtkToolPalette. To add a #GtkToolItemGroup to a #GtkToolPalette,
 * use gtk_container_add().
Johannes Schmid's avatar
Johannes Schmid committed
55
 *
56
 * |[<!-- language="C" -->
Johannes Schmid's avatar
Johannes Schmid committed
57 58 59 60 61 62 63
 * GtkWidget *palette, *group;
 * GtkToolItem *item;
 *
 * palette = gtk_tool_palette_new ();
 * group = gtk_tool_item_group_new (_("Test Category"));
 * gtk_container_add (GTK_CONTAINER (palette), group);
 *
64 65
 * item = gtk_tool_button_new_new (NULL, _("_Open"));
 * gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), "document-open");
Johannes Schmid's avatar
Johannes Schmid committed
66 67 68
 * gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1);
 * ]|
 *
69 70 71 72 73
 * The easiest way to use drag and drop with #GtkToolPalette is to call
 * gtk_tool_palette_add_drag_dest() with the desired drag source @palette
 * and the desired drag target @widget. Then gtk_tool_palette_get_drag_item()
 * can be used to get the dragged item in the #GtkWidget::drag-data-received
 * signal handler of the drag target.
Johannes Schmid's avatar
Johannes Schmid committed
74
 *
75
 * |[<!-- language="C" -->
Johannes Schmid's avatar
Johannes Schmid committed
76 77 78 79 80 81 82 83 84 85 86 87 88
 * static void
 * passive_canvas_drag_data_received (GtkWidget        *widget,
 *                                    GdkDragContext   *context,
 *                                    gint              x,
 *                                    gint              y,
 *                                    GtkSelectionData *selection,
 *                                    guint             info,
 *                                    guint             time,
 *                                    gpointer          data)
 * {
 *   GtkWidget *palette;
 *   GtkWidget *item;
 *
89
 *   // Get the dragged item
90 91
 *   palette = gtk_widget_get_ancestor (gtk_drag_get_source_widget (context),
 *                                      GTK_TYPE_TOOL_PALETTE);
Johannes Schmid's avatar
Johannes Schmid committed
92
 *   if (palette != NULL)
93 94
 *     item = gtk_tool_palette_get_drag_item (GTK_TOOL_PALETTE (palette),
 *                                            selection);
Johannes Schmid's avatar
Johannes Schmid committed
95
 *
96
 *   // Do something with item
Johannes Schmid's avatar
Johannes Schmid committed
97 98 99 100 101 102 103 104
 * }
 *
 * GtkWidget *target, palette;
 *
 * palette = gtk_tool_palette_new ();
 * target = gtk_drawing_area_new ();
 *
 * g_signal_connect (G_OBJECT (target), "drag-data-received",
105
 *                   G_CALLBACK (passive_canvas_drag_data_received), NULL);
Johannes Schmid's avatar
Johannes Schmid committed
106 107 108 109 110 111
 * gtk_tool_palette_add_drag_dest (GTK_TOOL_PALETTE (palette), target,
 *                                 GTK_DEST_DEFAULT_ALL,
 *                                 GTK_TOOL_PALETTE_DRAG_ITEMS,
 *                                 GDK_ACTION_COPY);
 * ]|
 *
112 113 114 115
 * # CSS nodes
 *
 * GtkToolPalette has a single CSS node named toolpalette.
 *
116
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
117 118 119 120 121 122 123 124 125
 */

typedef struct _GtkToolItemGroupInfo   GtkToolItemGroupInfo;
typedef struct _GtkToolPaletteDragData GtkToolPaletteDragData;

enum
{
  PROP_NONE,
  PROP_ICON_SIZE,
126
  PROP_ICON_SIZE_SET,
Johannes Schmid's avatar
Johannes Schmid committed
127 128
  PROP_ORIENTATION,
  PROP_TOOLBAR_STYLE,
129
  PROP_HADJUSTMENT,
130 131 132
  PROP_VADJUSTMENT,
  PROP_HSCROLL_POLICY,
  PROP_VSCROLL_POLICY
Johannes Schmid's avatar
Johannes Schmid committed
133 134 135 136 137 138 139 140 141 142 143 144 145
};

enum
{
  CHILD_PROP_NONE,
  CHILD_PROP_EXCLUSIVE,
  CHILD_PROP_EXPAND,
};

struct _GtkToolItemGroupInfo
{
  GtkToolItemGroup *widget;

146
  gulong            notify_collapsed;
147
  guint             pos;
Johannes Schmid's avatar
Johannes Schmid committed
148 149 150 151 152 153
  guint             exclusive : 1;
  guint             expand : 1;
};

struct _GtkToolPalettePrivate
{
154
  GPtrArray* groups;
Johannes Schmid's avatar
Johannes Schmid committed
155 156 157 158 159

  GtkAdjustment        *hadjustment;
  GtkAdjustment        *vadjustment;

  GtkIconSize           icon_size;
160
  gboolean              icon_size_set;
Johannes Schmid's avatar
Johannes Schmid committed
161 162
  GtkOrientation        orientation;
  GtkToolbarStyle       style;
163
  gboolean              style_set;
Johannes Schmid's avatar
Johannes Schmid committed
164 165 166 167

  GtkWidget            *expanding_child;

  GtkSizeGroup         *text_size_group;
168

Johannes Schmid's avatar
Johannes Schmid committed
169
  guint                 drag_source : 2;
170 171 172 173 174

  /* GtkScrollablePolicy needs to be checked when
   * driving the scrollable adjustment values */
  guint hscroll_policy : 1;
  guint vscroll_policy : 1;
Johannes Schmid's avatar
Johannes Schmid committed
175 176 177 178 179 180 181 182 183 184 185 186 187
};

struct _GtkToolPaletteDragData
{
  GtkToolPalette *palette;
  GtkWidget      *item;
};

static GdkAtom dnd_target_atom_item = GDK_NONE;
static GdkAtom dnd_target_atom_group = GDK_NONE;

static const GtkTargetEntry dnd_targets[] =
{
188 189
  { (char *) "application/x-gtk-tool-palette-item", GTK_TARGET_SAME_APP, 0 },
  { (char *) "application/x-gtk-tool-palette-group", GTK_TARGET_SAME_APP, 0 },
Johannes Schmid's avatar
Johannes Schmid committed
190 191
};

192 193 194 195 196 197
static void gtk_tool_palette_set_hadjustment (GtkToolPalette *palette,
                                              GtkAdjustment  *adjustment);
static void gtk_tool_palette_set_vadjustment (GtkToolPalette *palette,
                                              GtkAdjustment  *adjustment);


198
G_DEFINE_TYPE_WITH_CODE (GtkToolPalette,
199 200 201 202 203
                         gtk_tool_palette,
                         GTK_TYPE_CONTAINER,
                         G_ADD_PRIVATE (GtkToolPalette)
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
Johannes Schmid's avatar
Johannes Schmid committed
204 205 206 207

static void
gtk_tool_palette_init (GtkToolPalette *palette)
{
208
  palette->priv = gtk_tool_palette_get_instance_private (palette);
209
  palette->priv->groups = g_ptr_array_sized_new (4);
210
  g_ptr_array_set_free_func (palette->priv->groups, g_free);
Johannes Schmid's avatar
Johannes Schmid committed
211 212

  palette->priv->icon_size = DEFAULT_ICON_SIZE;
213
  palette->priv->icon_size_set = FALSE;
Johannes Schmid's avatar
Johannes Schmid committed
214 215
  palette->priv->orientation = DEFAULT_ORIENTATION;
  palette->priv->style = DEFAULT_TOOLBAR_STYLE;
216
  palette->priv->style_set = FALSE;
Johannes Schmid's avatar
Johannes Schmid committed
217 218

  palette->priv->text_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
219 220 221 222 223 224

  if (dnd_target_atom_item == GDK_NONE)
    {
      dnd_target_atom_item = gdk_atom_intern_static_string (dnd_targets[0].target);
      dnd_target_atom_group = gdk_atom_intern_static_string (dnd_targets[1].target);
    }
225 226

  gtk_widget_set_has_window (GTK_WIDGET (palette), TRUE);
Johannes Schmid's avatar
Johannes Schmid committed
227 228 229 230 231 232 233
}

static void
gtk_tool_palette_reconfigured (GtkToolPalette *palette)
{
  guint i;

234
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
235
    {
236
      GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
237 238
      if (info->widget)
        _gtk_tool_item_group_palette_reconfigured (info->widget);
Johannes Schmid's avatar
Johannes Schmid committed
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
    }

  gtk_widget_queue_resize_no_redraw (GTK_WIDGET (palette));
}

static void
gtk_tool_palette_set_property (GObject      *object,
                               guint         prop_id,
                               const GValue *value,
                               GParamSpec   *pspec)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (object);

  switch (prop_id)
    {
      case PROP_ICON_SIZE:
255
        if (palette->priv->icon_size != g_value_get_enum (value))
Johannes Schmid's avatar
Johannes Schmid committed
256 257 258
          {
            palette->priv->icon_size = g_value_get_enum (value);
            gtk_tool_palette_reconfigured (palette);
259
            g_object_notify_by_pspec (object, pspec);
Johannes Schmid's avatar
Johannes Schmid committed
260 261
          }
        break;
262

263
      case PROP_ICON_SIZE_SET:
264
        if (palette->priv->icon_size_set != g_value_get_boolean (value))
265
          {
266
            palette->priv->icon_size_set = g_value_get_boolean (value);
267
            gtk_tool_palette_reconfigured (palette);
268
            g_object_notify_by_pspec (object, pspec);
269 270
          }
        break;
Johannes Schmid's avatar
Johannes Schmid committed
271 272

      case PROP_ORIENTATION:
273
        if (palette->priv->orientation != g_value_get_enum (value))
Johannes Schmid's avatar
Johannes Schmid committed
274 275
          {
            palette->priv->orientation = g_value_get_enum (value);
276
            _gtk_orientable_set_style_classes (GTK_ORIENTABLE (palette));
Johannes Schmid's avatar
Johannes Schmid committed
277
            gtk_tool_palette_reconfigured (palette);
278
            g_object_notify_by_pspec (object, pspec);
Johannes Schmid's avatar
Johannes Schmid committed
279 280 281 282
          }
        break;

      case PROP_TOOLBAR_STYLE:
283
        if (palette->priv->style != g_value_get_enum (value))
Johannes Schmid's avatar
Johannes Schmid committed
284 285 286
          {
            palette->priv->style = g_value_get_enum (value);
            gtk_tool_palette_reconfigured (palette);
287
            g_object_notify_by_pspec (object, pspec);
Johannes Schmid's avatar
Johannes Schmid committed
288 289 290
          }
        break;

291 292 293 294 295 296 297 298
      case PROP_HADJUSTMENT:
        gtk_tool_palette_set_hadjustment (palette, g_value_get_object (value));
        break;

      case PROP_VADJUSTMENT:
        gtk_tool_palette_set_vadjustment (palette, g_value_get_object (value));
        break;

299
      case PROP_HSCROLL_POLICY:
300 301 302 303 304 305
        if (palette->priv->hscroll_policy != g_value_get_enum (value))
          {
	    palette->priv->hscroll_policy = g_value_get_enum (value);
	    gtk_widget_queue_resize (GTK_WIDGET (palette));
            g_object_notify_by_pspec (object, pspec);
          }
306 307 308
	break;

      case PROP_VSCROLL_POLICY:
309 310 311 312 313 314
        if (palette->priv->vscroll_policy != g_value_get_enum (value))
          {
	    palette->priv->vscroll_policy = g_value_get_enum (value);
	    gtk_widget_queue_resize (GTK_WIDGET (palette));
            g_object_notify_by_pspec (object, pspec);
          }
315 316
	break;

Johannes Schmid's avatar
Johannes Schmid committed
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
gtk_tool_palette_get_property (GObject    *object,
                               guint       prop_id,
                               GValue     *value,
                               GParamSpec *pspec)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (object);

  switch (prop_id)
    {
      case PROP_ICON_SIZE:
        g_value_set_enum (value, gtk_tool_palette_get_icon_size (palette));
        break;
336

337 338 339
      case PROP_ICON_SIZE_SET:
        g_value_set_boolean (value, palette->priv->icon_size_set);
        break;
Johannes Schmid's avatar
Johannes Schmid committed
340 341

      case PROP_ORIENTATION:
342
        g_value_set_enum (value, palette->priv->orientation);
Johannes Schmid's avatar
Johannes Schmid committed
343 344 345 346 347 348
        break;

      case PROP_TOOLBAR_STYLE:
        g_value_set_enum (value, gtk_tool_palette_get_style (palette));
        break;

349 350 351 352 353 354 355 356
      case PROP_HADJUSTMENT:
        g_value_set_object (value, palette->priv->hadjustment);
        break;

      case PROP_VADJUSTMENT:
        g_value_set_object (value, palette->priv->vadjustment);
        break;

357 358 359 360 361 362 363 364
      case PROP_HSCROLL_POLICY:
	g_value_set_enum (value, palette->priv->hscroll_policy);
	break;

      case PROP_VSCROLL_POLICY:
	g_value_set_enum (value, palette->priv->vscroll_policy);
	break;

Johannes Schmid's avatar
Johannes Schmid committed
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
gtk_tool_palette_dispose (GObject *object)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (object);
  guint i;

  if (palette->priv->hadjustment)
    {
      g_object_unref (palette->priv->hadjustment);
      palette->priv->hadjustment = NULL;
    }

  if (palette->priv->vadjustment)
    {
      g_object_unref (palette->priv->vadjustment);
      palette->priv->vadjustment = NULL;
    }

389
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
390
    {
391
      GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
Johannes Schmid's avatar
Johannes Schmid committed
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413

      if (group->notify_collapsed)
        {
          g_signal_handler_disconnect (group->widget, group->notify_collapsed);
          group->notify_collapsed = 0;
        }
    }

  if (palette->priv->text_size_group)
    {
      g_object_unref (palette->priv->text_size_group);
      palette->priv->text_size_group = NULL;
    }

  G_OBJECT_CLASS (gtk_tool_palette_parent_class)->dispose (object);
}

static void
gtk_tool_palette_finalize (GObject *object)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (object);

414
  g_ptr_array_free (palette->priv->groups, TRUE);
Johannes Schmid's avatar
Johannes Schmid committed
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429

  G_OBJECT_CLASS (gtk_tool_palette_parent_class)->finalize (object);
}

static void
gtk_tool_palette_size_request (GtkWidget      *widget,
                               GtkRequisition *requisition)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (widget);
  GtkRequisition child_requisition;
  guint i;

  requisition->width = 0;
  requisition->height = 0;

430
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
431
    {
432
      GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
Johannes Schmid's avatar
Johannes Schmid committed
433 434 435 436

      if (!group->widget)
        continue;

437 438
      gtk_widget_get_preferred_size (GTK_WIDGET (group->widget),
                                     &child_requisition, NULL);
Johannes Schmid's avatar
Johannes Schmid committed
439 440 441 442 443 444 445 446 447 448 449 450 451 452

      if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
        {
          requisition->width = MAX (requisition->width, child_requisition.width);
          requisition->height += child_requisition.height;
        }
      else
        {
          requisition->width += child_requisition.width;
          requisition->height = MAX (requisition->height, child_requisition.height);
        }
    }
}

453
static void
454 455 456 457 458 459 460
gtk_tool_palette_measure (GtkWidget      *widget,
                          GtkOrientation  orientation,
                          int             for_size,
                          int            *minimum,
                          int            *natural,
                          int            *minimum_baseline,
                          int            *natural_baseline)
461 462 463 464 465
{
  GtkRequisition requisition;

  gtk_tool_palette_size_request (widget, &requisition);

466 467 468 469
  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    *minimum = *natural = requisition.width;
  else
    *minimum = *natural = requisition.height;
470 471 472
}


Johannes Schmid's avatar
Johannes Schmid committed
473 474 475 476 477 478 479 480 481 482 483 484
static void
gtk_tool_palette_size_allocate (GtkWidget     *widget,
                                GtkAllocation *allocation)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (widget);
  GtkAdjustment *adjustment = NULL;
  GtkAllocation child_allocation;

  gint n_expand_groups = 0;
  gint remaining_space = 0;
  gint expand_space = 0;

485
  gint total_size, page_size;
Johannes Schmid's avatar
Johannes Schmid committed
486 487 488 489 490 491 492
  gint offset = 0;
  guint i;

  gint min_offset = -1, max_offset = -1;

  gint x;

493
  gint *group_sizes = g_newa (gint, palette->priv->groups->len);
494
  GtkTextDirection direction;
Johannes Schmid's avatar
Johannes Schmid committed
495

496
  direction = gtk_widget_get_direction (widget);
Johannes Schmid's avatar
Johannes Schmid committed
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517

  GTK_WIDGET_CLASS (gtk_tool_palette_parent_class)->size_allocate (widget, allocation);

  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
    {
      adjustment = palette->priv->vadjustment;
      page_size = allocation->height;
    }
  else
    {
      adjustment = palette->priv->hadjustment;
      page_size = allocation->width;
    }

  if (adjustment)
    offset = gtk_adjustment_get_value (adjustment);
  if (GTK_ORIENTATION_HORIZONTAL == palette->priv->orientation &&
      GTK_TEXT_DIR_RTL == direction)
    offset = -offset;

  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
518
    child_allocation.width = allocation->width;
Johannes Schmid's avatar
Johannes Schmid committed
519
  else
520
    child_allocation.height = allocation->height;
Johannes Schmid's avatar
Johannes Schmid committed
521 522 523 524 525 526 527

  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
    remaining_space = allocation->height;
  else
    remaining_space = allocation->width;

  /* figure out the required size of all groups to be able to distribute the
528 529
   * remaining space on allocation
   */
530
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
531
    {
532
      GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
Johannes Schmid's avatar
Johannes Schmid committed
533 534
      gint size;

535 536
      group_sizes[i] = 0;

Johannes Schmid's avatar
Johannes Schmid committed
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
      if (!group->widget)
        continue;

      widget = GTK_WIDGET (group->widget);

      if (gtk_tool_item_group_get_n_items (group->widget))
        {
          if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
            size = _gtk_tool_item_group_get_height_for_width (group->widget, child_allocation.width);
          else
            size = _gtk_tool_item_group_get_width_for_height (group->widget, child_allocation.height);

          if (group->expand && !gtk_tool_item_group_get_collapsed (group->widget))
            n_expand_groups += 1;
        }
      else
        size = 0;

      remaining_space -= size;
      group_sizes[i] = size;

558 559 560
      /* if the widget is currently expanding an offset which allows to
       * display as much of the widget as possible is calculated
       */
Johannes Schmid's avatar
Johannes Schmid committed
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
      if (widget == palette->priv->expanding_child)
        {
          gint limit =
            GTK_ORIENTATION_VERTICAL == palette->priv->orientation ?
            child_allocation.width : child_allocation.height;

          gint real_size;
          guint j;

          min_offset = 0;

          for (j = 0; j < i; ++j)
            min_offset += group_sizes[j];

          max_offset = min_offset + group_sizes[i];

          real_size = _gtk_tool_item_group_get_size_for_limit
            (GTK_TOOL_ITEM_GROUP (widget), limit,
             GTK_ORIENTATION_VERTICAL == palette->priv->orientation,
             FALSE);

          if (size == real_size)
            palette->priv->expanding_child = NULL;
        }
    }

  if (n_expand_groups > 0)
    {
      remaining_space = MAX (0, remaining_space);
      expand_space = remaining_space / n_expand_groups;
    }

  if (max_offset != -1)
    {
      gint limit =
        GTK_ORIENTATION_VERTICAL == palette->priv->orientation ?
        allocation->height : allocation->width;

      offset = MIN (MAX (offset, max_offset - limit), min_offset);
    }

  if (remaining_space > 0)
    offset = 0;

605 606
  x = 0;
  child_allocation.y = 0;
Johannes Schmid's avatar
Johannes Schmid committed
607 608 609 610 611 612 613

  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
    child_allocation.y -= offset;
  else
    x -= offset;

  /* allocate all groups at the calculated positions */
614
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
615
    {
616
      GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
Johannes Schmid's avatar
Johannes Schmid committed
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641

      if (!group->widget)
        continue;

      if (gtk_tool_item_group_get_n_items (group->widget))
        {
          gint size = group_sizes[i];

          if (group->expand && !gtk_tool_item_group_get_collapsed (group->widget))
            {
              size += MIN (expand_space, remaining_space);
              remaining_space -= expand_space;
            }

          if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
            child_allocation.height = size;
          else
            child_allocation.width = size;

          if (GTK_ORIENTATION_HORIZONTAL == palette->priv->orientation &&
              GTK_TEXT_DIR_RTL == direction)
            child_allocation.x = allocation->width - x - child_allocation.width;
          else
            child_allocation.x = x;

642 643
          gtk_widget_size_allocate (GTK_WIDGET (group->widget), &child_allocation);
          gtk_widget_show (GTK_WIDGET (group->widget));
Johannes Schmid's avatar
Johannes Schmid committed
644 645 646 647 648 649 650

          if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
            child_allocation.y += child_allocation.height;
          else
            x += child_allocation.width;
        }
      else
651
        gtk_widget_hide (GTK_WIDGET (group->widget));
Johannes Schmid's avatar
Johannes Schmid committed
652 653 654 655 656 657
    }

  if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
    {
      child_allocation.y += offset;

658
      total_size = child_allocation.y;
Johannes Schmid's avatar
Johannes Schmid committed
659 660 661 662 663
    }
  else
    {
      x += offset;

664
      total_size = x;
Johannes Schmid's avatar
Johannes Schmid committed
665 666 667 668 669
    }

  /* update the scrollbar to match the displayed adjustment */
  if (adjustment)
    {
670 671 672 673
      gdouble lower, upper;

      total_size = MAX (0, total_size);
      page_size = MIN (total_size, page_size);
Johannes Schmid's avatar
Johannes Schmid committed
674 675 676 677

      if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation ||
          GTK_TEXT_DIR_LTR == direction)
        {
678
          lower = 0;
679
          upper = total_size;
Johannes Schmid's avatar
Johannes Schmid committed
680 681 682
        }
      else
        {
683
          lower = page_size - total_size;
684
          upper = page_size;
Johannes Schmid's avatar
Johannes Schmid committed
685 686 687 688

          offset = -offset;
        }

689
      gtk_adjustment_configure (adjustment,
690
                                offset,
691 692 693 694 695
                                lower,
                                upper,
                                page_size * 0.1,
                                page_size * 0.9,
                                page_size);
Johannes Schmid's avatar
Johannes Schmid committed
696 697 698 699 700 701
    }
}

static void
gtk_tool_palette_realize (GtkWidget *widget)
{
702 703
  GtkAllocation allocation;
  GdkWindow *window;
704

705 706 707 708
  gtk_widget_set_realized (widget, TRUE);

  gtk_widget_get_allocation (widget, &allocation);

709 710 711 712 713 714 715 716
  window = gdk_window_new_child (gtk_widget_get_parent_window (widget),
                                 gtk_widget_get_events (widget)
                                 | GDK_VISIBILITY_NOTIFY_MASK
                                 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
                                 | GDK_BUTTON_MOTION_MASK
                                 | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK
                                 | GDK_TOUCH_MASK,
                                 &allocation);
717
  gtk_widget_set_window (widget, window);
718
  gtk_widget_register_window (widget, window);
Johannes Schmid's avatar
Johannes Schmid committed
719 720 721

  gtk_container_forall (GTK_CONTAINER (widget),
                        (GtkCallback) gtk_widget_set_parent_window,
722
                        window);
Johannes Schmid's avatar
Johannes Schmid committed
723 724 725 726 727

  gtk_widget_queue_resize_no_redraw (widget);
}

static void
Johannes Schmid's avatar
Johannes Schmid committed
728
gtk_tool_palette_adjustment_value_changed (GtkAdjustment *adjustment,
Johannes Schmid's avatar
Johannes Schmid committed
729 730
                                           gpointer       data)
{
731
  GtkAllocation allocation;
Johannes Schmid's avatar
Johannes Schmid committed
732
  GtkWidget *widget = GTK_WIDGET (data);
733 734 735

  gtk_widget_get_allocation (widget, &allocation);
  gtk_tool_palette_size_allocate (widget, &allocation);
Johannes Schmid's avatar
Johannes Schmid committed
736 737
}

738 739 740 741 742 743 744 745 746 747 748 749
static gboolean
gtk_tool_palette_draw (GtkWidget *widget,
                       cairo_t   *cr)
{
  gtk_render_background (gtk_widget_get_style_context (widget), cr,
                         0, 0,
                         gtk_widget_get_allocated_width (widget),
                         gtk_widget_get_allocated_height (widget));

  return GTK_WIDGET_CLASS (gtk_tool_palette_parent_class)->draw (widget, cr);
}

Johannes Schmid's avatar
Johannes Schmid committed
750 751 752 753 754
static void
gtk_tool_palette_add (GtkContainer *container,
                      GtkWidget    *child)
{
  GtkToolPalette *palette;
755
  GtkToolItemGroupInfo *info = g_new0(GtkToolItemGroupInfo, 1);
756

Johannes Schmid's avatar
Johannes Schmid committed
757 758 759 760 761
  g_return_if_fail (GTK_IS_TOOL_PALETTE (container));
  g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (child));

  palette = GTK_TOOL_PALETTE (container);

762 763 764
  g_ptr_array_add (palette->priv->groups, info);
  info->pos = palette->priv->groups->len - 1;
  info->widget = g_object_ref_sink (child);
Johannes Schmid's avatar
Johannes Schmid committed
765 766 767 768 769 770 771 772 773 774 775 776 777 778

  gtk_widget_set_parent (child, GTK_WIDGET (palette));
}

static void
gtk_tool_palette_remove (GtkContainer *container,
                         GtkWidget    *child)
{
  GtkToolPalette *palette;
  guint i;

  g_return_if_fail (GTK_IS_TOOL_PALETTE (container));
  palette = GTK_TOOL_PALETTE (container);

779 780
  for (i = 0; i < palette->priv->groups->len; ++i)
    {
781
      GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
782 783 784 785
      if (GTK_WIDGET(info->widget) == child)
        {
          g_object_unref (child);
          gtk_widget_unparent (child);
Johannes Schmid's avatar
Johannes Schmid committed
786

787 788 789
          g_ptr_array_remove_index (palette->priv->groups, i);
        }
    }
Johannes Schmid's avatar
Johannes Schmid committed
790 791 792 793
}

static void
gtk_tool_palette_forall (GtkContainer *container,
Johannes Schmid's avatar
Johannes Schmid committed
794
                         gboolean      internals,
Johannes Schmid's avatar
Johannes Schmid committed
795 796 797 798
                         GtkCallback   callback,
                         gpointer      callback_data)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (container);
799
  guint i, len;
800 801

  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
802
    {
803
      GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
804 805 806

      len = palette->priv->groups->len;

807 808 809
      if (info->widget)
        callback (GTK_WIDGET (info->widget),
                  callback_data);
810 811 812 813

      /* At destroy time, 'callback' results in removing a widget,
       * here we just reset the current index to account for the removed widget. */
      i -= (len - palette->priv->groups->len);
Johannes Schmid's avatar
Johannes Schmid committed
814 815 816 817
    }
}

static GType
Johannes Schmid's avatar
Johannes Schmid committed
818
gtk_tool_palette_child_type (GtkContainer *container)
Johannes Schmid's avatar
Johannes Schmid committed
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
{
  return GTK_TYPE_TOOL_ITEM_GROUP;
}

static void
gtk_tool_palette_set_child_property (GtkContainer *container,
                                     GtkWidget    *child,
                                     guint         prop_id,
                                     const GValue *value,
                                     GParamSpec   *pspec)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (container);

  switch (prop_id)
    {
      case CHILD_PROP_EXCLUSIVE:
835 836
        gtk_tool_palette_set_exclusive (palette, GTK_TOOL_ITEM_GROUP (child), 
          g_value_get_boolean (value));
Johannes Schmid's avatar
Johannes Schmid committed
837 838 839
        break;

      case CHILD_PROP_EXPAND:
840 841
        gtk_tool_palette_set_expand (palette, GTK_TOOL_ITEM_GROUP (child), 
          g_value_get_boolean (value));
Johannes Schmid's avatar
Johannes Schmid committed
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
        break;

      default:
        GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
        break;
    }
}

static void
gtk_tool_palette_get_child_property (GtkContainer *container,
                                     GtkWidget    *child,
                                     guint         prop_id,
                                     GValue       *value,
                                     GParamSpec   *pspec)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (container);

  switch (prop_id)
    {
      case CHILD_PROP_EXCLUSIVE:
862 863
        g_value_set_boolean (value, 
          gtk_tool_palette_get_exclusive (palette, GTK_TOOL_ITEM_GROUP (child)));
Johannes Schmid's avatar
Johannes Schmid committed
864 865 866
        break;

      case CHILD_PROP_EXPAND:
867 868
        g_value_set_boolean (value, 
          gtk_tool_palette_get_expand (palette, GTK_TOOL_ITEM_GROUP (child)));
Johannes Schmid's avatar
Johannes Schmid committed
869 870 871 872 873 874 875 876
        break;

      default:
        GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
        break;
    }
}

877 878 879 880 881 882 883 884 885 886
static void
gtk_tool_palette_screen_changed (GtkWidget *widget,
                                 GdkScreen *previous_screen)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (widget);

  gtk_tool_palette_reconfigured (palette);
}


Johannes Schmid's avatar
Johannes Schmid committed
887 888 889 890 891 892 893 894 895 896 897 898
static void
gtk_tool_palette_class_init (GtkToolPaletteClass *cls)
{
  GObjectClass      *oclass   = G_OBJECT_CLASS (cls);
  GtkWidgetClass    *wclass   = GTK_WIDGET_CLASS (cls);
  GtkContainerClass *cclass   = GTK_CONTAINER_CLASS (cls);

  oclass->set_property        = gtk_tool_palette_set_property;
  oclass->get_property        = gtk_tool_palette_get_property;
  oclass->dispose             = gtk_tool_palette_dispose;
  oclass->finalize            = gtk_tool_palette_finalize;

899
  wclass->measure             = gtk_tool_palette_measure;
Johannes Schmid's avatar
Johannes Schmid committed
900 901
  wclass->size_allocate       = gtk_tool_palette_size_allocate;
  wclass->realize             = gtk_tool_palette_realize;
902
  wclass->draw                = gtk_tool_palette_draw;
Johannes Schmid's avatar
Johannes Schmid committed
903 904 905 906 907 908 909 910

  cclass->add                 = gtk_tool_palette_add;
  cclass->remove              = gtk_tool_palette_remove;
  cclass->forall              = gtk_tool_palette_forall;
  cclass->child_type          = gtk_tool_palette_child_type;
  cclass->set_child_property  = gtk_tool_palette_set_child_property;
  cclass->get_child_property  = gtk_tool_palette_get_child_property;

911
  /* Handle screen-changed so we can update our configuration.
912 913
   */
  wclass->screen_changed      = gtk_tool_palette_screen_changed;
914

915
  g_object_class_override_property (oclass, PROP_ORIENTATION,    "orientation");
916

917 918 919 920
  g_object_class_override_property (oclass, PROP_HADJUSTMENT,    "hadjustment");
  g_object_class_override_property (oclass, PROP_VADJUSTMENT,    "vadjustment");
  g_object_class_override_property (oclass, PROP_HSCROLL_POLICY, "hscroll-policy");
  g_object_class_override_property (oclass, PROP_VSCROLL_POLICY, "vscroll-policy");
921

922
  /**
923 924
   * GtkToolPalette:icon-size:
   *
925 926
   * The size of the icons in a tool palette. When this property is set,
   * it overrides the default setting.
927
   *
928 929
   * This should only be used for special-purpose tool palettes, normal
   * application tool palettes should respect the user preferences for the
930 931
   * size of icons.
   *
932
   * Since: 2.20
933 934 935
   */
  g_object_class_install_property (oclass,
                                   PROP_ICON_SIZE,
Johannes Schmid's avatar
Johannes Schmid committed
936
                                   g_param_spec_enum ("icon-size",
937 938
                                                      P_("Icon size"),
                                                      P_("Size of icons in this tool palette"),
Johannes Schmid's avatar
Johannes Schmid committed
939 940
                                                      GTK_TYPE_ICON_SIZE,
                                                      DEFAULT_ICON_SIZE,
941
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Johannes Schmid's avatar
Johannes Schmid committed
942

943
  /**
Matthias Clasen's avatar
Matthias Clasen committed
944
   * GtkToolPalette:icon-size-set:
945
   *
946
   * Is %TRUE if the #GtkToolPalette:icon-size property has been set.
947
   *
948
   * Since: 2.20
949 950 951 952
   */
  g_object_class_install_property (oclass,
                                   PROP_ICON_SIZE_SET,
                                   g_param_spec_boolean ("icon-size-set",
953 954 955 956
                                                         P_("Icon size set"),
                                                         P_("Whether the icon-size property has been set"),
                                                         FALSE,
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
957

958 959 960 961 962 963 964
  /**
   * GtkToolPalette:toolbar-style:
   *
   * The style of items in the tool palette.
   *
   * Since: 2.20
   */
Johannes Schmid's avatar
Johannes Schmid committed
965 966 967 968 969 970
  g_object_class_install_property (oclass, PROP_TOOLBAR_STYLE,
                                   g_param_spec_enum ("toolbar-style",
                                                      P_("Toolbar Style"),
                                                      P_("Style of items in the tool palette"),
                                                      GTK_TYPE_TOOLBAR_STYLE,
                                                      DEFAULT_TOOLBAR_STYLE,
971
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
972

Johannes Schmid's avatar
Johannes Schmid committed
973

974 975 976 977 978 979 980 981
  /**
   * GtkToolPalette:exclusive:
   *
   * Whether the item group should be the only one that is expanded
   * at a given time.
   *
   * Since: 2.20
   */
Johannes Schmid's avatar
Johannes Schmid committed
982 983 984 985 986
  gtk_container_class_install_child_property (cclass, CHILD_PROP_EXCLUSIVE,
                                              g_param_spec_boolean ("exclusive",
                                                                    P_("Exclusive"),
                                                                    P_("Whether the item group should be the only expanded at a given time"),
                                                                    DEFAULT_CHILD_EXCLUSIVE,
987
                                                                    GTK_PARAM_READWRITE));
Johannes Schmid's avatar
Johannes Schmid committed
988

989 990 991 992 993 994 995 996
  /**
   * GtkToolPalette:expand:
   *
   * Whether the item group should receive extra space when the palette grows.
   * at a given time.
   *
   * Since: 2.20
   */
Johannes Schmid's avatar
Johannes Schmid committed
997 998 999 1000 1001
  gtk_container_class_install_child_property (cclass, CHILD_PROP_EXPAND,
                                              g_param_spec_boolean ("expand",
                                                                    P_("Expand"),
                                                                    P_("Whether the item group should receive extra space when the palette grows"),
                                                                    DEFAULT_CHILD_EXPAND,
1002
                                                                    GTK_PARAM_READWRITE));
1003 1004

  gtk_widget_class_set_css_name (wclass, "toolpalette");
Johannes Schmid's avatar
Johannes Schmid committed
1005 1006 1007 1008 1009 1010 1011
}

/**
 * gtk_tool_palette_new:
 *
 * Creates a new tool palette.
 *
1012
 * Returns: a new #GtkToolPalette
Johannes Schmid's avatar
Johannes Schmid committed
1013
 *
1014
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1015 1016 1017 1018 1019 1020 1021 1022 1023
 */
GtkWidget*
gtk_tool_palette_new (void)
{
  return g_object_new (GTK_TYPE_TOOL_PALETTE, NULL);
}

/**
 * gtk_tool_palette_set_icon_size:
1024
 * @palette: a #GtkToolPalette
1025 1026
 * @icon_size: (type int): the #GtkIconSize that icons in the tool
 *     palette shall have
Johannes Schmid's avatar
Johannes Schmid committed
1027 1028 1029
 *
 * Sets the size of icons in the tool palette.
 *
1030
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1031 1032 1033 1034 1035
 */
void
gtk_tool_palette_set_icon_size (GtkToolPalette *palette,
                                GtkIconSize     icon_size)
{
1036
  GtkToolPalettePrivate *priv;
1037

Johannes Schmid's avatar
Johannes Schmid committed
1038
  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1039 1040 1041
  g_return_if_fail (icon_size != GTK_ICON_SIZE_INVALID);

  priv = palette->priv;
1042

1043 1044
  if (!priv->icon_size_set)
    {
1045
      priv->icon_size_set = TRUE;
1046 1047
      g_object_notify (G_OBJECT (palette), "icon-size-set");
    }
Johannes Schmid's avatar
Johannes Schmid committed
1048

1049 1050
  if (priv->icon_size == icon_size)
    return;
1051

1052 1053
  priv->icon_size = icon_size;
  g_object_notify (G_OBJECT (palette), "icon-size");
1054

1055
  gtk_tool_palette_reconfigured (palette);
1056

1057 1058 1059
  gtk_widget_queue_resize (GTK_WIDGET (palette));
}

Johannes Schmid's avatar
Johannes Schmid committed
1060
/**
1061
 * gtk_tool_palette_unset_icon_size:
1062
 * @palette: a #GtkToolPalette
1063
 *
1064 1065
 * Unsets the tool palette icon size set with gtk_tool_palette_set_icon_size(),
 * so that user preferences will be used to determine the icon size.
1066
 *
1067
 * Since: 2.20
1068 1069 1070 1071 1072 1073
 */
void
gtk_tool_palette_unset_icon_size (GtkToolPalette *palette)
{
  GtkToolPalettePrivate* priv = palette->priv;
  GtkIconSize size;
1074

1075
  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1076

1077 1078
  if (palette->priv->icon_size_set)
    {
1079
      size = DEFAULT_ICON_SIZE;
1080

1081 1082 1083
      if (size != palette->priv->icon_size)
      {
        gtk_tool_palette_set_icon_size (palette, size);
1084
        g_object_notify (G_OBJECT (palette), "icon-size");
1085
      }
1086

1087
      priv->icon_size_set = FALSE;
1088
      g_object_notify (G_OBJECT (palette), "icon-size-set");
1089 1090 1091 1092
    }
}

/* Set the "toolbar-style" property and do appropriate things.
1093 1094
 * GtkToolbar does this by emitting a signal instead of just
 * calling a function...
1095 1096
 */
static void
1097 1098
gtk_tool_palette_change_style (GtkToolPalette  *palette,
                               GtkToolbarStyle  style)
1099 1100
{
  GtkToolPalettePrivate* priv = palette->priv;
1101

1102 1103 1104
  if (priv->style != style)
    {
      priv->style = style;
1105

1106
      gtk_tool_palette_reconfigured (palette);
1107

1108 1109 1110 1111 1112 1113 1114 1115
      gtk_widget_queue_resize (GTK_WIDGET (palette));
      g_object_notify (G_OBJECT (palette), "toolbar-style");
    }
}


/**
 * gtk_tool_palette_set_style:
1116 1117
 * @palette: a #GtkToolPalette
 * @style: the #GtkToolbarStyle that items in the tool palette shall have
Johannes Schmid's avatar
Johannes Schmid committed
1118 1119 1120
 *
 * Sets the style (text, icons or both) of items in the tool palette.
 *
1121
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1122 1123 1124 1125 1126 1127 1128
 */
void
gtk_tool_palette_set_style (GtkToolPalette  *palette,
                            GtkToolbarStyle  style)
{
  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));

1129
  palette->priv->style_set = TRUE;
1130 1131 1132 1133 1134 1135
  gtk_tool_palette_change_style (palette, style);
}


/**
 * gtk_tool_palette_unset_style:
1136 1137 1138 1139
 * @palette: a #GtkToolPalette
 *
 * Unsets a toolbar style set with gtk_tool_palette_set_style(),
 * so that user preferences will be used to determine the toolbar style.
1140
 *
1141 1142
 * Since: 2.20
 */
1143 1144 1145 1146 1147
void
gtk_tool_palette_unset_style (GtkToolPalette *palette)
{
  GtkToolPalettePrivate* priv = palette->priv;
  GtkToolbarStyle style;
1148

1149
  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1150

1151 1152
  if (priv->style_set)
    {
1153
      style = DEFAULT_TOOLBAR_STYLE;
1154

1155 1156
      if (style != priv->style)
        gtk_tool_palette_change_style (palette, style);
1157

1158 1159
      priv->style_set = FALSE;
    }
Johannes Schmid's avatar
Johannes Schmid committed
1160 1161 1162 1163
}

/**
 * gtk_tool_palette_get_icon_size:
1164
 * @palette: a #GtkToolPalette
Johannes Schmid's avatar
Johannes Schmid committed
1165
 *
1166 1167
 * Gets the size of icons in the tool palette.
 * See gtk_tool_palette_set_icon_size().
1168
 *
1169
 * Returns: (type int): the #GtkIconSize of icons in the tool palette
Johannes Schmid's avatar
Johannes Schmid committed
1170
 *
1171
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1172 1173 1174 1175 1176
 */
GtkIconSize
gtk_tool_palette_get_icon_size (GtkToolPalette *palette)
{
  g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_ICON_SIZE);
1177

Johannes Schmid's avatar
Johannes Schmid committed
1178 1179 1180 1181 1182
  return palette->priv->icon_size;
}

/**
 * gtk_tool_palette_get_style:
1183
 * @palette: a #GtkToolPalette
Johannes Schmid's avatar
Johannes Schmid committed
1184 1185 1186 1187 1188
 *
 * Gets the style (icons, text or both) of items in the tool palette.
 *
 * Returns: the #GtkToolbarStyle of items in the tool palette.
 *
1189
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1190 1191 1192 1193 1194
 */
GtkToolbarStyle
gtk_tool_palette_get_style (GtkToolPalette *palette)
{
  g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_TOOLBAR_STYLE);
1195

Johannes Schmid's avatar
Johannes Schmid committed
1196 1197 1198
  return palette->priv->style;
}

1199 1200 1201
static gint
gtk_tool_palette_compare_groups (gconstpointer a,
                                 gconstpointer b)
1202 1203 1204 1205 1206 1207 1208
{
  const GtkToolItemGroupInfo *group_a = a;
  const GtkToolItemGroupInfo *group_b = b;

  return group_a->pos - group_b->pos;
}

Johannes Schmid's avatar
Johannes Schmid committed
1209 1210
/**
 * gtk_tool_palette_set_group_position:
1211 1212 1213
 * @palette: a #GtkToolPalette
 * @group: a #GtkToolItemGroup which is a child of palette
 * @position: a new index for group
Johannes Schmid's avatar
Johannes Schmid committed
1214 1215 1216 1217 1218
 *
 * Sets the position of the group as an index of the tool palette.
 * If position is 0 the group will become the first child, if position is
 * -1 it will become the last child.
 *
1219
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1220 1221
 */
void
1222 1223 1224
gtk_tool_palette_set_group_position (GtkToolPalette   *palette,
                                     GtkToolItemGroup *group,
                                     gint             position)
Johannes Schmid's avatar
Johannes Schmid committed
1225
{
1226
  GtkToolItemGroupInfo *group_new;
1227
  GtkToolItemGroupInfo *group_old;
Johannes Schmid's avatar
Johannes Schmid committed
1228 1229 1230 1231 1232 1233 1234
  gint old_position;

  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
  g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));
  g_return_if_fail (position >= -1);

  if (-1 == position)
1235
    position = palette->priv->groups->len - 1;
Johannes Schmid's avatar
Johannes Schmid committed
1236

1237
  g_return_if_fail ((guint) position < palette->priv->groups->len);
Johannes Schmid's avatar
Johannes Schmid committed
1238

1239
  group_new = g_ptr_array_index (palette->priv->groups, position);
1240

1241
  if (GTK_TOOL_ITEM_GROUP (group) == group_new->widget)
Johannes Schmid's avatar
Johannes Schmid committed
1242 1243
    return;

1244
  old_position = gtk_tool_palette_get_group_position (palette, group);
Johannes Schmid's avatar
Johannes Schmid committed
1245 1246
  g_return_if_fail (old_position >= 0);

1247
  group_old = g_ptr_array_index (palette->priv->groups, old_position);
Johannes Schmid's avatar
Johannes Schmid committed
1248

1249 1250
  group_new->pos = position;
  group_old->pos = old_position;
Johannes Schmid's avatar
Johannes Schmid committed
1251

1252
  g_ptr_array_sort (palette->priv->groups, gtk_tool_palette_compare_groups);
Johannes Schmid's avatar
Johannes Schmid committed
1253 1254 1255 1256 1257 1258

  gtk_widget_queue_resize (GTK_WIDGET (palette));
}

static void
gtk_tool_palette_group_notify_collapsed (GtkToolItemGroup *group,
Johannes Schmid's avatar
Johannes Schmid committed
1259
                                         GParamSpec       *pspec,
Johannes Schmid's avatar
Johannes Schmid committed
1260 1261 1262 1263 1264 1265 1266 1267
                                         gpointer          data)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (data);
  guint i;

  if (gtk_tool_item_group_get_collapsed (group))
    return;

1268
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
1269
    {
1270 1271
      GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
      GtkToolItemGroup *current_group = info->widget;
Johannes Schmid's avatar
Johannes Schmid committed
1272 1273

      if (current_group && current_group != group)
1274
        gtk_tool_item_group_set_collapsed (current_group, TRUE);
Johannes Schmid's avatar
Johannes Schmid committed
1275 1276 1277 1278 1279
    }
}

/**
 * gtk_tool_palette_set_exclusive:
1280 1281 1282
 * @palette: a #GtkToolPalette
 * @group: a #GtkToolItemGroup which is a child of palette
 * @exclusive: whether the group should be exclusive or not
Johannes Schmid's avatar
Johannes Schmid committed
1283
 *
1284 1285
 * Sets whether the group should be exclusive or not.
 * If an exclusive group is expanded all other groups are collapsed.
Johannes Schmid's avatar
Johannes Schmid committed
1286
 *
1287
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1288 1289
 */
void
1290 1291 1292
gtk_tool_palette_set_exclusive (GtkToolPalette   *palette,
                                GtkToolItemGroup *group,
                                gboolean          exclusive)
Johannes Schmid's avatar
Johannes Schmid committed
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302
{
  GtkToolItemGroupInfo *group_info;
  gint position;

  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
  g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));

  position = gtk_tool_palette_get_group_position (palette, group);
  g_return_if_fail (position >= 0);

1303
  group_info = g_ptr_array_index (palette->priv->groups, position);
Johannes Schmid's avatar
Johannes Schmid committed
1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326

  if (exclusive == group_info->exclusive)
    return;

  group_info->exclusive = exclusive;

  if (group_info->exclusive != (0 != group_info->notify_collapsed))
    {
      if (group_info->exclusive)
        {
          group_info->notify_collapsed =
            g_signal_connect (group, "notify::collapsed",
                              G_CALLBACK (gtk_tool_palette_group_notify_collapsed),
                              palette);
        }
      else
        {
          g_signal_handler_disconnect (group, group_info->notify_collapsed);
          group_info->notify_collapsed = 0;
        }
    }

  gtk_tool_palette_group_notify_collapsed (group_info->widget, NULL, palette);
1327
  gtk_widget_child_notify (GTK_WIDGET (group), "exclusive");
Johannes Schmid's avatar
Johannes Schmid committed
1328 1329 1330 1331
}

/**
 * gtk_tool_palette_set_expand:
1332 1333 1334
 * @palette: a #GtkToolPalette
 * @group: a #GtkToolItemGroup which is a child of palette
 * @expand: whether the group should be given extra space
Johannes Schmid's avatar
Johannes Schmid committed
1335 1336 1337
 *
 * Sets whether the group should be given extra space.
 *
1338
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1339 1340
 */
void
1341 1342
gtk_tool_palette_set_expand (GtkToolPalette   *palette,
                             GtkToolItemGroup *group,
Johannes Schmid's avatar
Johannes Schmid committed
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353
                             gboolean        expand)
{
  GtkToolItemGroupInfo *group_info;
  gint position;

  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
  g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group));

  position = gtk_tool_palette_get_group_position (palette, group);
  g_return_if_fail (position >= 0);

1354
  group_info = g_ptr_array_index (palette->priv->groups, position);
Johannes Schmid's avatar
Johannes Schmid committed
1355 1356 1357 1358 1359

  if (expand != group_info->expand)
    {
      group_info->expand = expand;
      gtk_widget_queue_resize (GTK_WIDGET (palette));
1360
      gtk_widget_child_notify (GTK_WIDGET (group), "expand");
Johannes Schmid's avatar
Johannes Schmid committed
1361 1362 1363 1364 1365
    }
}

/**
 * gtk_tool_palette_get_group_position:
1366 1367
 * @palette: a #GtkToolPalette
 * @group: a #GtkToolItemGroup
Johannes Schmid's avatar
Johannes Schmid committed
1368
 *
1369 1370
 * Gets the position of @group in @palette as index.
 * See gtk_tool_palette_set_group_position().
Johannes Schmid's avatar
Johannes Schmid committed
1371
 *
1372
 * Returns: the index of group or -1 if @group is not a child of @palette
Johannes Schmid's avatar
Johannes Schmid committed
1373
 *
1374
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1375 1376
 */
gint
1377 1378
gtk_tool_palette_get_group_position (GtkToolPalette   *palette,
                                     GtkToolItemGroup *group)
Johannes Schmid's avatar
Johannes Schmid committed
1379 1380 1381 1382 1383 1384
{
  guint i;

  g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), -1);
  g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), -1);

1385 1386 1387 1388 1389 1390
  for (i = 0; i < palette->priv->groups->len; ++i)
    {
      GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
      if ((gpointer) group == info->widget)
        return i;
    }
Johannes Schmid's avatar
Johannes Schmid committed
1391 1392 1393 1394 1395 1396

  return -1;
}

/**
 * gtk_tool_palette_get_exclusive:
1397 1398
 * @palette: a #GtkToolPalette
 * @group: a #GtkToolItemGroup which is a child of palette
Johannes Schmid's avatar
Johannes Schmid committed
1399
 *
1400 1401
 * Gets whether @group is exclusive or not.
 * See gtk_tool_palette_set_exclusive().
Johannes Schmid's avatar
Johannes Schmid committed
1402
 *
1403
 * Returns: %TRUE if @group is exclusive
Johannes Schmid's avatar
Johannes Schmid committed
1404
 *
1405
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1406 1407
 */
gboolean
1408 1409
gtk_tool_palette_get_exclusive (GtkToolPalette   *palette,
                                GtkToolItemGroup *group)
Johannes Schmid's avatar
Johannes Schmid committed
1410 1411
{
  gint position;
1412
  GtkToolItemGroupInfo *info;
Johannes Schmid's avatar
Johannes Schmid committed
1413 1414 1415 1416 1417 1418 1419

  g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXCLUSIVE);
  g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXCLUSIVE);

  position = gtk_tool_palette_get_group_position (palette, group);
  g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXCLUSIVE);

1420
  info = g_ptr_array_index (palette->priv->groups, position);
1421

1422
  return info->exclusive;
Johannes Schmid's avatar
Johannes Schmid committed
1423 1424 1425 1426
}

/**
 * gtk_tool_palette_get_expand:
1427 1428
 * @palette: a #GtkToolPalette
 * @group: a #GtkToolItemGroup which is a child of palette
Johannes Schmid's avatar
Johannes Schmid committed
1429
 *
1430 1431
 * Gets whether group should be given extra space.
 * See gtk_tool_palette_set_expand().
Johannes Schmid's avatar
Johannes Schmid committed
1432
 *
1433
 * Returns: %TRUE if group should be given extra space, %FALSE otherwise
Johannes Schmid's avatar
Johannes Schmid committed
1434
 *
1435
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1436 1437
 */
gboolean
1438 1439
gtk_tool_palette_get_expand (GtkToolPalette   *palette,
                             GtkToolItemGroup *group)
Johannes Schmid's avatar
Johannes Schmid committed
1440 1441
{
  gint position;
1442
  GtkToolItemGroupInfo *info;
Johannes Schmid's avatar
Johannes Schmid committed
1443 1444 1445 1446 1447 1448 1449

  g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXPAND);
  g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXPAND);

  position = gtk_tool_palette_get_group_position (palette, group);
  g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXPAND);

1450
  info = g_ptr_array_index (palette->priv->groups, position);
1451

1452
  return info->expand;
Johannes Schmid's avatar
Johannes Schmid committed
1453 1454 1455 1456
}

/**
 * gtk_tool_palette_get_drop_item:
1457 1458 1459
 * @palette: a #GtkToolPalette
 * @x: the x position
 * @y: the y position
Johannes Schmid's avatar
Johannes Schmid committed
1460
 *
1461 1462
 * Gets the item at position (x, y).
 * See gtk_tool_palette_get_drop_group().
Johannes Schmid's avatar
Johannes Schmid committed
1463
 *
1464
 * Returns: (nullable) (transfer none): the #GtkToolItem at position or %NULL if there is no such item
Johannes Schmid's avatar
Johannes Schmid committed
1465
 *
1466
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1467 1468 1469 1470 1471 1472
 */
GtkToolItem*
gtk_tool_palette_get_drop_item (GtkToolPalette *palette,
                                gint            x,
                                gint            y)
{
1473
  GtkAllocation allocation;
1474 1475
  GtkToolItemGroup *group = gtk_tool_palette_get_drop_group (palette, x, y);
  GtkWidget *widget = GTK_WIDGET (group);
Johannes Schmid's avatar
Johannes Schmid committed
1476 1477

  if (group)
1478 1479 1480 1481 1482 1483
    {
      gtk_widget_get_allocation (widget, &allocation);
      return gtk_tool_item_group_get_drop_item (group,
                                                x - allocation.x,
                                                y - allocation.y);
    }
Johannes Schmid's avatar
Johannes Schmid committed
1484 1485 1486 1487 1488 1489

  return NULL;
}

/**
 * gtk_tool_palette_get_drop_group:
1490 1491 1492
 * @palette: a #GtkToolPalette
 * @x: the x position
 * @y: the y position
Johannes Schmid's avatar
Johannes Schmid committed
1493 1494 1495
 *
 * Gets the group at position (x, y).
 *
1496 1497
 * Returns: (nullable) (transfer none): the #GtkToolItemGroup at position
 * or %NULL if there is no such group
Johannes Schmid's avatar
Johannes Schmid committed
1498
 *
1499
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1500
 */
1501
GtkToolItemGroup*
Johannes Schmid's avatar
Johannes Schmid committed
1502 1503 1504 1505
gtk_tool_palette_get_drop_group (GtkToolPalette *palette,
                                 gint            x,
                                 gint            y)
{
1506
  GtkAllocation allocation;
Johannes Schmid's avatar
Johannes Schmid committed
1507 1508 1509 1510
  guint i;

  g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL);

1511
  gtk_widget_get_allocation (GTK_WIDGET (palette), &allocation);
Johannes Schmid's avatar
Johannes Schmid committed
1512

1513 1514
  g_return_val_if_fail (x >= 0 && x < allocation.width, NULL);
  g_return_val_if_fail (y >= 0 && y < allocation.height, NULL);
Johannes Schmid's avatar