gtktoolpalette.c 59.1 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 #GtkToolItem<!-- -->s 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
 * #GtkToolItem<!-- -->s 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 57 58 59 60 61 62 63 64 65 66 67
 *
 * |[
 * 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);
 *
 * item = gtk_tool_button_new_from_stock (GTK_STOCK_OK);
 * gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1);
 * ]|
 *
68 69 70 71 72
 * 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
73 74 75 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;
 *
 *   /<!-- -->* Get the dragged item *<!-- -->/
89 90
 *   palette = gtk_widget_get_ancestor (gtk_drag_get_source_widget (context),
 *                                      GTK_TYPE_TOOL_PALETTE);
Johannes Schmid's avatar
Johannes Schmid committed
91
 *   if (palette != NULL)
92 93
 *     item = gtk_tool_palette_get_drag_item (GTK_TOOL_PALETTE (palette),
 *                                            selection);
Johannes Schmid's avatar
Johannes Schmid committed
94 95 96 97 98 99 100 101 102 103
 *
 *   /<!-- -->* Do something with item *<!-- -->/
 * }
 *
 * GtkWidget *target, palette;
 *
 * palette = gtk_tool_palette_new ();
 * target = gtk_drawing_area_new ();
 *
 * g_signal_connect (G_OBJECT (target), "drag-data-received",
104
 *                   G_CALLBACK (passive_canvas_drag_data_received), NULL);
Johannes Schmid's avatar
Johannes Schmid committed
105 106 107 108 109 110
 * gtk_tool_palette_add_drag_dest (GTK_TOOL_PALETTE (palette), target,
 *                                 GTK_DEST_DEFAULT_ALL,
 *                                 GTK_TOOL_PALETTE_DRAG_ITEMS,
 *                                 GDK_ACTION_COPY);
 * ]|
 *
111
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
112 113 114 115 116 117 118 119 120
 */

typedef struct _GtkToolItemGroupInfo   GtkToolItemGroupInfo;
typedef struct _GtkToolPaletteDragData GtkToolPaletteDragData;

enum
{
  PROP_NONE,
  PROP_ICON_SIZE,
121
  PROP_ICON_SIZE_SET,
Johannes Schmid's avatar
Johannes Schmid committed
122 123
  PROP_ORIENTATION,
  PROP_TOOLBAR_STYLE,
124
  PROP_HADJUSTMENT,
125 126 127
  PROP_VADJUSTMENT,
  PROP_HSCROLL_POLICY,
  PROP_VSCROLL_POLICY
Johannes Schmid's avatar
Johannes Schmid committed
128 129 130 131 132 133 134 135 136 137 138 139 140
};

enum
{
  CHILD_PROP_NONE,
  CHILD_PROP_EXCLUSIVE,
  CHILD_PROP_EXPAND,
};

struct _GtkToolItemGroupInfo
{
  GtkToolItemGroup *widget;

141
  gulong            notify_collapsed;
142
  guint             pos;
Johannes Schmid's avatar
Johannes Schmid committed
143 144 145 146 147 148
  guint             exclusive : 1;
  guint             expand : 1;
};

struct _GtkToolPalettePrivate
{
149
  GPtrArray* groups;
Johannes Schmid's avatar
Johannes Schmid committed
150 151 152 153 154

  GtkAdjustment        *hadjustment;
  GtkAdjustment        *vadjustment;

  GtkIconSize           icon_size;
155
  gboolean              icon_size_set;
Johannes Schmid's avatar
Johannes Schmid committed
156 157
  GtkOrientation        orientation;
  GtkToolbarStyle       style;
158
  gboolean              style_set;
Johannes Schmid's avatar
Johannes Schmid committed
159 160 161 162

  GtkWidget            *expanding_child;

  GtkSizeGroup         *text_size_group;
163

164 165
  GtkSettings          *settings;
  gulong                settings_connection;
Johannes Schmid's avatar
Johannes Schmid committed
166 167

  guint                 drag_source : 2;
168 169 170 171 172

  /* 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
173 174 175 176 177 178 179 180 181 182 183 184 185
};

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[] =
{
186 187
  { "application/x-gtk-tool-palette-item", GTK_TARGET_SAME_APP, 0 },
  { "application/x-gtk-tool-palette-group", GTK_TARGET_SAME_APP, 0 },
Johannes Schmid's avatar
Johannes Schmid committed
188 189
};

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


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

static void
gtk_tool_palette_init (GtkToolPalette *palette)
{
  palette->priv = G_TYPE_INSTANCE_GET_PRIVATE (palette,
                                               GTK_TYPE_TOOL_PALETTE,
                                               GtkToolPalettePrivate);

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 219 220 221 222 223 224 225

  palette->priv->text_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
}

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

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

  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:
        if ((guint) g_value_get_enum (value) != palette->priv->icon_size)
          {
            palette->priv->icon_size = g_value_get_enum (value);
            gtk_tool_palette_reconfigured (palette);
          }
        break;
253

254 255 256 257 258 259 260
      case PROP_ICON_SIZE_SET:
        if ((guint) g_value_get_enum (value) != palette->priv->icon_size)
          {
            palette->priv->icon_size_set = g_value_get_enum (value);
            gtk_tool_palette_reconfigured (palette);
          }
        break;
Johannes Schmid's avatar
Johannes Schmid committed
261 262 263 264 265

      case PROP_ORIENTATION:
        if ((guint) g_value_get_enum (value) != palette->priv->orientation)
          {
            palette->priv->orientation = g_value_get_enum (value);
266
            _gtk_orientable_set_style_classes (GTK_ORIENTABLE (palette));
Johannes Schmid's avatar
Johannes Schmid committed
267 268 269 270 271 272 273 274 275 276 277 278
            gtk_tool_palette_reconfigured (palette);
          }
        break;

      case PROP_TOOLBAR_STYLE:
        if ((guint) g_value_get_enum (value) != palette->priv->style)
          {
            palette->priv->style = g_value_get_enum (value);
            gtk_tool_palette_reconfigured (palette);
          }
        break;

279 280 281 282 283 284 285 286
      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;

287 288 289 290 291 292 293 294 295 296
      case PROP_HSCROLL_POLICY:
	palette->priv->hscroll_policy = g_value_get_enum (value);
	gtk_widget_queue_resize (GTK_WIDGET (palette));
	break;

      case PROP_VSCROLL_POLICY:
	palette->priv->vscroll_policy = g_value_get_enum (value);
	gtk_widget_queue_resize (GTK_WIDGET (palette));
	break;

Johannes Schmid's avatar
Johannes Schmid committed
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
      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;
316

317 318 319
      case PROP_ICON_SIZE_SET:
        g_value_set_boolean (value, palette->priv->icon_size_set);
        break;
Johannes Schmid's avatar
Johannes Schmid committed
320 321

      case PROP_ORIENTATION:
322
        g_value_set_enum (value, palette->priv->orientation);
Johannes Schmid's avatar
Johannes Schmid committed
323 324 325 326 327 328
        break;

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

329 330 331 332 333 334 335 336
      case PROP_HADJUSTMENT:
        g_value_set_object (value, palette->priv->hadjustment);
        break;

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

337 338 339 340 341 342 343 344
      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
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
      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;
    }

369
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
370
    {
371
      GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
Johannes Schmid's avatar
Johannes Schmid committed
372 373 374 375 376 377 378 379 380 381 382 383 384 385

      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;
    }

386 387 388 389 390 391 392 393
  if (palette->priv->settings_connection > 0)
    {
      g_signal_handler_disconnect (palette->priv->settings, palette->priv->settings_connection);
      palette->priv->settings_connection = 0;
    }

  g_clear_object (&palette->priv->settings);

Johannes Schmid's avatar
Johannes Schmid committed
394 395 396 397 398 399 400 401
  G_OBJECT_CLASS (gtk_tool_palette_parent_class)->dispose (object);
}

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

402
  g_ptr_array_free (palette->priv->groups, TRUE);
Johannes Schmid's avatar
Johannes Schmid committed
403 404 405 406 407 408 409 410 411 412

  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;
413
  guint border_width;
Johannes Schmid's avatar
Johannes Schmid committed
414 415
  guint i;

416 417
  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));

Johannes Schmid's avatar
Johannes Schmid committed
418 419 420
  requisition->width = 0;
  requisition->height = 0;

421
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
422
    {
423
      GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
Johannes Schmid's avatar
Johannes Schmid committed
424 425 426 427

      if (!group->widget)
        continue;

428 429
      gtk_widget_get_preferred_size (GTK_WIDGET (group->widget),
                                     &child_requisition, NULL);
Johannes Schmid's avatar
Johannes Schmid committed
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446

      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);
        }
    }

  requisition->width += border_width * 2;
  requisition->height += border_width * 2;
}

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
static void
gtk_tool_palette_get_preferred_width (GtkWidget *widget,
				      gint      *minimum,
				      gint      *natural)
{
  GtkRequisition requisition;

  gtk_tool_palette_size_request (widget, &requisition);

  *minimum = *natural = requisition.width;
}

static void
gtk_tool_palette_get_preferred_height (GtkWidget *widget,
				       gint      *minimum,
				       gint      *natural)
{
  GtkRequisition requisition;

  gtk_tool_palette_size_request (widget, &requisition);

  *minimum = *natural = requisition.height;
}


Johannes Schmid's avatar
Johannes Schmid committed
472 473 474 475 476 477 478 479 480 481 482 483
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;

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

  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
  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
497
  direction = gtk_widget_get_direction (widget);
Johannes Schmid's avatar
Johannes Schmid committed
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528

  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)
    child_allocation.width = allocation->width - border_width * 2;
  else
    child_allocation.height = allocation->height - border_width * 2;

  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
529 530
   * remaining space on allocation
   */
531
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
532
    {
533
      GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
Johannes Schmid's avatar
Johannes Schmid committed
534 535
      gint size;

536 537
      group_sizes[i] = 0;

Johannes Schmid's avatar
Johannes Schmid committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
      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;

559 560 561
      /* 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
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 605 606 607 608 609 610 611 612 613 614
      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;

  x = border_width;
  child_allocation.y = border_width;

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

  /* allocate all groups at the calculated positions */
615
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
616
    {
617
      GtkToolItemGroupInfo *group = g_ptr_array_index (palette->priv->groups, i);
Johannes Schmid's avatar
Johannes Schmid committed
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
      GtkWidget *widget;

      if (!group->widget)
        continue;

      widget = GTK_WIDGET (group->widget);

      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;

          gtk_widget_size_allocate (widget, &child_allocation);
          gtk_widget_show (widget);

          if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation)
            child_allocation.y += child_allocation.height;
          else
            x += child_allocation.width;
        }
      else
        gtk_widget_hide (widget);
    }

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

663
      total_size = child_allocation.y;
Johannes Schmid's avatar
Johannes Schmid committed
664 665 666 667 668 669
    }
  else
    {
      x += border_width;
      x += offset;

670
      total_size = x;
Johannes Schmid's avatar
Johannes Schmid committed
671 672 673 674 675
    }

  /* update the scrollbar to match the displayed adjustment */
  if (adjustment)
    {
676 677 678 679
      gdouble lower, upper;

      total_size = MAX (0, total_size);
      page_size = MIN (total_size, page_size);
Johannes Schmid's avatar
Johannes Schmid committed
680 681 682 683

      if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation ||
          GTK_TEXT_DIR_LTR == direction)
        {
684
          lower = 0;
685
          upper = total_size;
Johannes Schmid's avatar
Johannes Schmid committed
686 687 688
        }
      else
        {
689
          lower = page_size - total_size;
690
          upper = page_size;
Johannes Schmid's avatar
Johannes Schmid committed
691 692 693 694

          offset = -offset;
        }

695
      gtk_adjustment_configure (adjustment,
696
                                offset,
697 698 699 700 701
                                lower,
                                upper,
                                page_size * 0.1,
                                page_size * 0.9,
                                page_size);
Johannes Schmid's avatar
Johannes Schmid committed
702 703 704 705 706 707
    }
}

static void
gtk_tool_palette_realize (GtkWidget *widget)
{
708 709
  GtkAllocation allocation;
  GdkWindow *window;
Johannes Schmid's avatar
Johannes Schmid committed
710
  GdkWindowAttr attributes;
711
  gint attributes_mask;
712 713
  guint border_width;

714 715
  gtk_widget_set_realized (widget, TRUE);

716
  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
Johannes Schmid's avatar
Johannes Schmid committed
717

718 719
  gtk_widget_get_allocation (widget, &allocation);

Johannes Schmid's avatar
Johannes Schmid committed
720
  attributes.window_type = GDK_WINDOW_CHILD;
721 722 723 724
  attributes.x = allocation.x + border_width;
  attributes.y = allocation.y + border_width;
  attributes.width = allocation.width - border_width * 2;
  attributes.height = allocation.height - border_width * 2;
Johannes Schmid's avatar
Johannes Schmid committed
725 726
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
727 728 729
  attributes.event_mask = gtk_widget_get_events (widget)
                         | GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK
                         | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
730 731 732
                         | GDK_BUTTON_MOTION_MASK
                         | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK
                         | GDK_TOUCH_MASK;
733
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
Johannes Schmid's avatar
Johannes Schmid committed
734

735 736
  window = gdk_window_new (gtk_widget_get_parent_window (widget),
                           &attributes, attributes_mask);
737
  gtk_widget_set_window (widget, window);
738
  gtk_widget_register_window (widget, window);
Johannes Schmid's avatar
Johannes Schmid committed
739

740 741
  gtk_style_context_set_background (gtk_widget_get_style_context (widget),
                                    window);
Johannes Schmid's avatar
Johannes Schmid committed
742 743 744

  gtk_container_forall (GTK_CONTAINER (widget),
                        (GtkCallback) gtk_widget_set_parent_window,
745
                        window);
Johannes Schmid's avatar
Johannes Schmid committed
746 747 748 749 750

  gtk_widget_queue_resize_no_redraw (widget);
}

static void
Johannes Schmid's avatar
Johannes Schmid committed
751
gtk_tool_palette_adjustment_value_changed (GtkAdjustment *adjustment,
Johannes Schmid's avatar
Johannes Schmid committed
752 753
                                           gpointer       data)
{
754
  GtkAllocation allocation;
Johannes Schmid's avatar
Johannes Schmid committed
755
  GtkWidget *widget = GTK_WIDGET (data);
756 757 758

  gtk_widget_get_allocation (widget, &allocation);
  gtk_tool_palette_size_allocate (widget, &allocation);
Johannes Schmid's avatar
Johannes Schmid committed
759 760 761 762 763 764 765
}

static void
gtk_tool_palette_add (GtkContainer *container,
                      GtkWidget    *child)
{
  GtkToolPalette *palette;
766
  GtkToolItemGroupInfo *info = g_new0(GtkToolItemGroupInfo, 1);
767

Johannes Schmid's avatar
Johannes Schmid committed
768 769 770 771 772
  g_return_if_fail (GTK_IS_TOOL_PALETTE (container));
  g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (child));

  palette = GTK_TOOL_PALETTE (container);

773 774 775
  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
776 777 778 779 780 781 782 783 784 785 786 787 788 789

  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);

790 791
  for (i = 0; i < palette->priv->groups->len; ++i)
    {
792
      GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
793 794 795 796
      if (GTK_WIDGET(info->widget) == child)
        {
          g_object_unref (child);
          gtk_widget_unparent (child);
Johannes Schmid's avatar
Johannes Schmid committed
797

798 799 800
          g_ptr_array_remove_index (palette->priv->groups, i);
        }
    }
Johannes Schmid's avatar
Johannes Schmid committed
801 802 803 804
}

static void
gtk_tool_palette_forall (GtkContainer *container,
Johannes Schmid's avatar
Johannes Schmid committed
805
                         gboolean      internals,
Johannes Schmid's avatar
Johannes Schmid committed
806 807 808 809
                         GtkCallback   callback,
                         gpointer      callback_data)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (container);
810
  guint i, len;
811 812

  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
813
    {
814
      GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
815 816 817

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

818 819 820
      if (info->widget)
        callback (GTK_WIDGET (info->widget),
                  callback_data);
821 822 823 824

      /* 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
825 826 827 828
    }
}

static GType
Johannes Schmid's avatar
Johannes Schmid committed
829
gtk_tool_palette_child_type (GtkContainer *container)
Johannes Schmid's avatar
Johannes Schmid committed
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
{
  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:
846 847
        gtk_tool_palette_set_exclusive (palette, GTK_TOOL_ITEM_GROUP (child), 
          g_value_get_boolean (value));
Johannes Schmid's avatar
Johannes Schmid committed
848 849 850
        break;

      case CHILD_PROP_EXPAND:
851 852
        gtk_tool_palette_set_expand (palette, GTK_TOOL_ITEM_GROUP (child), 
          g_value_get_boolean (value));
Johannes Schmid's avatar
Johannes Schmid committed
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
        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:
873 874
        g_value_set_boolean (value, 
          gtk_tool_palette_get_exclusive (palette, GTK_TOOL_ITEM_GROUP (child)));
Johannes Schmid's avatar
Johannes Schmid committed
875 876 877
        break;

      case CHILD_PROP_EXPAND:
878 879
        g_value_set_boolean (value, 
          gtk_tool_palette_get_expand (palette, GTK_TOOL_ITEM_GROUP (child)));
Johannes Schmid's avatar
Johannes Schmid committed
880 881 882 883 884 885 886 887
        break;

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

888 889 890 891
static void
style_change_notify (GtkToolPalette *palette)
{
  GtkToolPalettePrivate* priv = palette->priv;
892

893 894 895 896 897 898 899 900 901 902 903 904
  if (!priv->style_set)
    {
      /* pretend it was set, then unset, thus reverting to new default */
      priv->style_set = TRUE;
      gtk_tool_palette_unset_style (palette);
    }
}

static void
icon_size_change_notify (GtkToolPalette *palette)
{
  GtkToolPalettePrivate* priv = palette->priv;
905

906 907 908 909 910 911 912 913 914 915 916 917 918
  if (!priv->icon_size_set)
    {
      /* pretend it was set, then unset, thus reverting to new default */
      priv->icon_size_set = TRUE;
      gtk_tool_palette_unset_icon_size (palette);
    }
}

static void
gtk_tool_palette_settings_change_notify (GtkSettings      *settings,
                                         const GParamSpec *pspec,
                                         GtkToolPalette   *palette)
{
919
  if (strcmp (pspec->name, "gtk-toolbar-style") == 0)
920
    style_change_notify (palette);
921
  else if (strcmp (pspec->name, "gtk-toolbar-icon-size") == 0)
922 923 924 925 926 927 928 929 930 931 932
    icon_size_change_notify (palette);
}

static void
gtk_tool_palette_screen_changed (GtkWidget *widget,
                                 GdkScreen *previous_screen)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (widget);
  GtkToolPalettePrivate* priv = palette->priv;
  GtkSettings *old_settings = priv->settings;
  GtkSettings *settings;
933

934 935 936 937
  if (gtk_widget_has_screen (GTK_WIDGET (palette)))
    settings = gtk_widget_get_settings (GTK_WIDGET (palette));
  else
    settings = NULL;
938

939 940
  if (settings == old_settings)
    return;
941

942 943 944
  if (old_settings)
  {
    g_signal_handler_disconnect (old_settings, priv->settings_connection);
945
    priv->settings_connection = 0;
946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
    g_object_unref (old_settings);
  }

  if (settings)
  {
    priv->settings_connection =
      g_signal_connect (settings, "notify",
                        G_CALLBACK (gtk_tool_palette_settings_change_notify),
                        palette);
    priv->settings = g_object_ref (settings);
  }
  else
    priv->settings = NULL;

  gtk_tool_palette_reconfigured (palette);
}


Johannes Schmid's avatar
Johannes Schmid committed
964 965 966 967 968 969 970 971 972 973 974 975
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;

976 977
  wclass->get_preferred_width = gtk_tool_palette_get_preferred_width;
  wclass->get_preferred_height= gtk_tool_palette_get_preferred_height;
Johannes Schmid's avatar
Johannes Schmid committed
978 979 980 981 982 983 984 985 986 987
  wclass->size_allocate       = gtk_tool_palette_size_allocate;
  wclass->realize             = gtk_tool_palette_realize;

  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;

988 989 990
  /* Handle screen-changed so we can update our GtkSettings.
   */
  wclass->screen_changed      = gtk_tool_palette_screen_changed;
991

992
  g_object_class_override_property (oclass, PROP_ORIENTATION,    "orientation");
993

994 995 996 997
  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");
998

999
  /**
1000 1001 1002
   * GtkToolPalette:icon-size:
   *
   * The size of the icons in a tool palette is normally determined by
1003
   * the #GtkSettings:gtk-toolbar-icon-size setting. When this property is set,
1004
   * it overrides the setting.
1005
   *
1006 1007
   * This should only be used for special-purpose tool palettes, normal
   * application tool palettes should respect the user preferences for the
1008 1009
   * size of icons.
   *
1010
   * Since: 2.20
1011 1012 1013
   */
  g_object_class_install_property (oclass,
                                   PROP_ICON_SIZE,
Johannes Schmid's avatar
Johannes Schmid committed
1014
                                   g_param_spec_enum ("icon-size",
1015 1016
                                                      P_("Icon size"),
                                                      P_("Size of icons in this tool palette"),
Johannes Schmid's avatar
Johannes Schmid committed
1017 1018
                                                      GTK_TYPE_ICON_SIZE,
                                                      DEFAULT_ICON_SIZE,
1019
                                                      GTK_PARAM_READWRITE));
Johannes Schmid's avatar
Johannes Schmid committed
1020

1021
  /**
Matthias Clasen's avatar
Matthias Clasen committed
1022
   * GtkToolPalette:icon-size-set:
1023
   *
1024
   * Is %TRUE if the #GtkToolPalette:icon-size property has been set.
1025
   *
1026
   * Since: 2.20
1027 1028 1029 1030 1031 1032 1033
   */
  g_object_class_install_property (oclass,
                                   PROP_ICON_SIZE_SET,
                                   g_param_spec_boolean ("icon-size-set",
                                                      P_("Icon size set"),
                                                      P_("Whether the icon-size property has been set"),
                                                      FALSE,
1034
                                                      GTK_PARAM_READWRITE));
1035

1036 1037 1038 1039 1040 1041 1042
  /**
   * GtkToolPalette:toolbar-style:
   *
   * The style of items in the tool palette.
   *
   * Since: 2.20
   */
Johannes Schmid's avatar
Johannes Schmid committed
1043 1044 1045 1046 1047 1048
  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,
1049
                                                      GTK_PARAM_READWRITE));
1050

Johannes Schmid's avatar
Johannes Schmid committed
1051

1052 1053 1054 1055 1056 1057 1058 1059
  /**
   * 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
1060 1061 1062 1063 1064
  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,
1065
                                                                    GTK_PARAM_READWRITE));
Johannes Schmid's avatar
Johannes Schmid committed
1066

1067 1068 1069 1070 1071 1072 1073 1074
  /**
   * 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
1075 1076 1077 1078 1079
  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,
1080
                                                                    GTK_PARAM_READWRITE));
Johannes Schmid's avatar
Johannes Schmid committed
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092

  g_type_class_add_private (cls, sizeof (GtkToolPalettePrivate));

  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);
}

/**
 * gtk_tool_palette_new:
 *
 * Creates a new tool palette.
 *
1093
 * Returns: a new #GtkToolPalette
Johannes Schmid's avatar
Johannes Schmid committed
1094
 *
1095
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1096 1097 1098 1099 1100 1101 1102 1103 1104
 */
GtkWidget*
gtk_tool_palette_new (void)
{
  return g_object_new (GTK_TYPE_TOOL_PALETTE, NULL);
}

/**
 * gtk_tool_palette_set_icon_size:
1105
 * @palette: a #GtkToolPalette
1106 1107
 * @icon_size: (type int): the #GtkIconSize that icons in the tool
 *     palette shall have
Johannes Schmid's avatar
Johannes Schmid committed
1108 1109 1110
 *
 * Sets the size of icons in the tool palette.
 *
1111
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1112 1113 1114 1115 1116
 */
void
gtk_tool_palette_set_icon_size (GtkToolPalette *palette,
                                GtkIconSize     icon_size)
{
1117
  GtkToolPalettePrivate *priv;
1118

Johannes Schmid's avatar
Johannes Schmid committed
1119
  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1120 1121 1122
  g_return_if_fail (icon_size != GTK_ICON_SIZE_INVALID);

  priv = palette->priv;
1123

1124 1125
  if (!priv->icon_size_set)
    {
1126
      priv->icon_size_set = TRUE;
1127 1128
      g_object_notify (G_OBJECT (palette), "icon-size-set");
    }
Johannes Schmid's avatar
Johannes Schmid committed
1129

1130 1131
  if (priv->icon_size == icon_size)
    return;
1132

1133 1134
  priv->icon_size = icon_size;
  g_object_notify (G_OBJECT (palette), "icon-size");
1135

1136
  gtk_tool_palette_reconfigured (palette);
1137

1138 1139 1140 1141 1142 1143 1144 1145
  gtk_widget_queue_resize (GTK_WIDGET (palette));
}

static GtkSettings *
toolpalette_get_settings (GtkToolPalette *palette)
{
  GtkToolPalettePrivate *priv = palette->priv;
  return priv->settings;
Johannes Schmid's avatar
Johannes Schmid committed
1146 1147 1148
}

/**
1149
 * gtk_tool_palette_unset_icon_size:
1150
 * @palette: a #GtkToolPalette
1151
 *
1152 1153
 * 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.
1154
 *
1155
 * Since: 2.20
1156 1157 1158 1159 1160 1161
 */
void
gtk_tool_palette_unset_icon_size (GtkToolPalette *palette)
{
  GtkToolPalettePrivate* priv = palette->priv;
  GtkIconSize size;
1162

1163
  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1164

1165 1166 1167
  if (palette->priv->icon_size_set)
    {
      GtkSettings *settings = toolpalette_get_settings (palette);
1168

1169 1170 1171 1172 1173 1174 1175 1176
      if (settings)
        {
          g_object_get (settings,
            "gtk-toolbar-icon-size", &size,
            NULL);
        }
      else
        size = DEFAULT_ICON_SIZE;
1177

1178 1179 1180
      if (size != palette->priv->icon_size)
      {
        gtk_tool_palette_set_icon_size (palette, size);
1181
        g_object_notify (G_OBJECT (palette), "icon-size");
1182
	    }
1183

1184
      priv->icon_size_set = FALSE;
1185
      g_object_notify (G_OBJECT (palette), "icon-size-set");
1186 1187 1188 1189
    }
}

/* Set the "toolbar-style" property and do appropriate things.
1190 1191
 * GtkToolbar does this by emitting a signal instead of just
 * calling a function...
1192 1193
 */
static void
1194 1195
gtk_tool_palette_change_style (GtkToolPalette  *palette,
                               GtkToolbarStyle  style)
1196 1197
{
  GtkToolPalettePrivate* priv = palette->priv;
1198

1199 1200 1201
  if (priv->style != style)
    {
      priv->style = style;
1202

1203
      gtk_tool_palette_reconfigured (palette);
1204

1205 1206 1207 1208 1209 1210 1211 1212
      gtk_widget_queue_resize (GTK_WIDGET (palette));
      g_object_notify (G_OBJECT (palette), "toolbar-style");
    }
}


/**
 * gtk_tool_palette_set_style:
1213 1214
 * @palette: a #GtkToolPalette
 * @style: the #GtkToolbarStyle that items in the tool palette shall have
Johannes Schmid's avatar
Johannes Schmid committed
1215 1216 1217
 *
 * Sets the style (text, icons or both) of items in the tool palette.
 *
1218
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1219 1220 1221 1222 1223 1224 1225
 */
void
gtk_tool_palette_set_style (GtkToolPalette  *palette,
                            GtkToolbarStyle  style)
{
  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));

1226
  palette->priv->style_set = TRUE;
1227 1228 1229 1230 1231 1232
  gtk_tool_palette_change_style (palette, style);
}


/**
 * gtk_tool_palette_unset_style:
1233 1234 1235 1236
 * @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.
1237
 *
1238 1239
 * Since: 2.20
 */
1240 1241 1242 1243 1244
void
gtk_tool_palette_unset_style (GtkToolPalette *palette)
{
  GtkToolPalettePrivate* priv = palette->priv;
  GtkToolbarStyle style;
1245

1246
  g_return_if_fail (GTK_IS_TOOL_PALETTE (palette));
1247

1248 1249 1250
  if (priv->style_set)
    {
      GtkSettings *settings = toolpalette_get_settings (palette);
1251

1252
      if (settings)
1253 1254 1255
        g_object_get (settings,
                      "gtk-toolbar-style", &style,
                      NULL);
1256 1257
      else
        style = DEFAULT_TOOLBAR_STYLE;
1258

1259 1260
      if (style != priv->style)
        gtk_tool_palette_change_style (palette, style);
1261

1262 1263
      priv->style_set = FALSE;
    }
Johannes Schmid's avatar
Johannes Schmid committed
1264 1265 1266 1267
}

/**
 * gtk_tool_palette_get_icon_size:
1268
 * @palette: a #GtkToolPalette
Johannes Schmid's avatar
Johannes Schmid committed
1269
 *
1270 1271
 * Gets the size of icons in the tool palette.
 * See gtk_tool_palette_set_icon_size().
1272
 *
1273
 * Returns: (type int): the #GtkIconSize of icons in the tool palette
Johannes Schmid's avatar
Johannes Schmid committed
1274
 *
1275
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1276 1277 1278 1279 1280
 */
GtkIconSize
gtk_tool_palette_get_icon_size (GtkToolPalette *palette)
{
  g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_ICON_SIZE);
1281

Johannes Schmid's avatar
Johannes Schmid committed
1282 1283 1284 1285 1286
  return palette->priv->icon_size;
}

/**
 * gtk_tool_palette_get_style:
1287
 * @palette: a #GtkToolPalette
Johannes Schmid's avatar
Johannes Schmid committed
1288 1289 1290 1291 1292
 *
 * Gets the style (icons, text or both) of items in the tool palette.
 *
 * Returns: the #GtkToolbarStyle of items in the tool palette.
 *
1293
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1294 1295 1296 1297 1298
 */
GtkToolbarStyle
gtk_tool_palette_get_style (GtkToolPalette *palette)
{
  g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_TOOLBAR_STYLE);
1299

Johannes Schmid's avatar
Johannes Schmid committed
1300 1301 1302
  return palette->priv->style;
}

1303 1304 1305
static gint
gtk_tool_palette_compare_groups (gconstpointer a,
                                 gconstpointer b)
1306 1307 1308 1309 1310 1311 1312
{
  const GtkToolItemGroupInfo *group_a = a;
  const GtkToolItemGroupInfo *group_b = b;

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

Johannes Schmid's avatar
Johannes Schmid committed
1313 1314
/**
 * gtk_tool_palette_set_group_position:
1315 1316 1317
 * @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
1318 1319 1320 1321 1322
 *
 * 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.
 *
1323
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1324 1325
 */
void
1326 1327 1328
gtk_tool_palette_set_group_position (GtkToolPalette   *palette,
                                     GtkToolItemGroup *group,
                                     gint             position)
Johannes Schmid's avatar
Johannes Schmid committed
1329
{
1330
  GtkToolItemGroupInfo *group_new;
1331
  GtkToolItemGroupInfo *group_old;
Johannes Schmid's avatar
Johannes Schmid committed
1332 1333 1334 1335 1336 1337 1338
  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)
1339
    position = palette->priv->groups->len - 1;
Johannes Schmid's avatar
Johannes Schmid committed
1340

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

1343
  group_new = g_ptr_array_index (palette->priv->groups, position);
1344

1345
  if (GTK_TOOL_ITEM_GROUP (group) == group_new->widget)
Johannes Schmid's avatar
Johannes Schmid committed
1346 1347
    return;

1348
  old_position = gtk_tool_palette_get_group_position (palette, group);
Johannes Schmid's avatar
Johannes Schmid committed
1349 1350
  g_return_if_fail (old_position >= 0);

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

1353 1354
  group_new->pos = position;
  group_old->pos = old_position;
Johannes Schmid's avatar
Johannes Schmid committed
1355

1356
  g_ptr_array_sort (palette->priv->groups, gtk_tool_palette_compare_groups);
Johannes Schmid's avatar
Johannes Schmid committed
1357 1358 1359 1360 1361 1362

  gtk_widget_queue_resize (GTK_WIDGET (palette));
}

static void
gtk_tool_palette_group_notify_collapsed (GtkToolItemGroup *group,
Johannes Schmid's avatar
Johannes Schmid committed
1363
                                         GParamSpec       *pspec,
Johannes Schmid's avatar
Johannes Schmid committed
1364 1365 1366 1367 1368 1369 1370 1371
                                         gpointer          data)
{
  GtkToolPalette *palette = GTK_TOOL_PALETTE (data);
  guint i;

  if (gtk_tool_item_group_get_collapsed (group))
    return;

1372
  for (i = 0; i < palette->priv->groups->len; ++i)
Johannes Schmid's avatar
Johannes Schmid committed
1373
    {
1374 1375
      GtkToolItemGroupInfo *info = g_ptr_array_index (palette->priv->groups, i);
      GtkToolItemGroup *current_group = info->widget;
Johannes Schmid's avatar
Johannes Schmid committed
1376 1377

      if (current_group && current_group != group)
1378
        gtk_tool_item_group_set_collapsed (current_group, TRUE);
Johannes Schmid's avatar
Johannes Schmid committed
1379 1380 1381 1382 1383
    }
}

/**
 * gtk_tool_palette_set_exclusive:
1384 1385 1386
 * @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
1387
 *
1388 1389
 * 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
1390
 *
1391
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1392 1393
 */
void
1394 1395 1396
gtk_tool_palette_set_exclusive (GtkToolPalette   *palette,
                                GtkToolItemGroup *group,
                                gboolean          exclusive)
Johannes Schmid's avatar
Johannes Schmid committed
1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
{
  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);

1407
  group_info = g_ptr_array_index (palette->priv->groups, position);
Johannes Schmid's avatar
Johannes Schmid committed
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430

  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);
1431
  gtk_widget_child_notify (GTK_WIDGET (group), "exclusive");
Johannes Schmid's avatar
Johannes Schmid committed
1432 1433 1434 1435
}

/**
 * gtk_tool_palette_set_expand:
1436 1437 1438
 * @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
1439 1440 1441
 *
 * Sets whether the group should be given extra space.
 *
1442
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1443 1444
 */
void
1445 1446
gtk_tool_palette_set_expand (GtkToolPalette   *palette,
                             GtkToolItemGroup *group,
Johannes Schmid's avatar
Johannes Schmid committed
1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457
                             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);

1458
  group_info = g_ptr_array_index (palette->priv->groups, position);
Johannes Schmid's avatar
Johannes Schmid committed
1459 1460 1461 1462 1463

  if (expand != group_info->expand)
    {
      group_info->expand = expand;
      gtk_widget_queue_resize (GTK_WIDGET (palette));
1464
      gtk_widget_child_notify (GTK_WIDGET (group), "expand");
Johannes Schmid's avatar
Johannes Schmid committed
1465 1466 1467 1468 1469
    }
}

/**
 * gtk_tool_palette_get_group_position:
1470 1471
 * @palette: a #GtkToolPalette
 * @group: a #GtkToolItemGroup
Johannes Schmid's avatar
Johannes Schmid committed
1472
 *
1473 1474
 * Gets the position of @group in @palette as index.
 * See gtk_tool_palette_set_group_position().
Johannes Schmid's avatar
Johannes Schmid committed
1475
 *
1476
 * Returns: the index of group or -1 if @group is not a child of @palette
Johannes Schmid's avatar
Johannes Schmid committed
1477
 *
1478
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1479 1480
 */
gint
1481 1482
gtk_tool_palette_get_group_position (GtkToolPalette   *palette,
                                     GtkToolItemGroup *group)
Johannes Schmid's avatar
Johannes Schmid committed
1483 1484 1485 1486 1487 1488
{
  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);

1489 1490 1491 1492 1493 1494
  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
1495 1496 1497 1498 1499 1500

  return -1;
}

/**
 * gtk_tool_palette_get_exclusive:
1501 1502
 * @palette: a #GtkToolPalette
 * @group: a #GtkToolItemGroup which is a child of palette
Johannes Schmid's avatar
Johannes Schmid committed
1503
 *
1504 1505
 * Gets whether @group is exclusive or not.
 * See gtk_tool_palette_set_exclusive().
Johannes Schmid's avatar
Johannes Schmid committed
1506
 *
1507
 * Returns: %TRUE if @group is exclusive
Johannes Schmid's avatar
Johannes Schmid committed
1508
 *
1509
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1510 1511
 */
gboolean
1512 1513
gtk_tool_palette_get_exclusive (GtkToolPalette   *palette,
                                GtkToolItemGroup *group)
Johannes Schmid's avatar
Johannes Schmid committed
1514 1515
{
  gint position;
1516
  GtkToolItemGroupInfo *info;
Johannes Schmid's avatar
Johannes Schmid committed
1517 1518 1519 1520 1521 1522 1523

  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);

1524
  info = g_ptr_array_index (palette->priv->groups, position);
1525

1526
  return info->exclusive;
Johannes Schmid's avatar
Johannes Schmid committed
1527 1528 1529 1530
}

/**
 * gtk_tool_palette_get_expand:
1531 1532
 * @palette: a #GtkToolPalette
 * @group: a #GtkToolItemGroup which is a child of palette
Johannes Schmid's avatar
Johannes Schmid committed
1533
 *
1534 1535
 * Gets whether group should be given extra space.
 * See gtk_tool_palette_set_expand().
Johannes Schmid's avatar
Johannes Schmid committed
1536
 *
1537
 * Returns: %TRUE if group should be given extra space, %FALSE otherwise
Johannes Schmid's avatar
Johannes Schmid committed
1538
 *
1539
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1540 1541
 */
gboolean
1542 1543
gtk_tool_palette_get_expand (GtkToolPalette   *palette,
                             GtkToolItemGroup *group)
Johannes Schmid's avatar
Johannes Schmid committed
1544 1545
{
  gint position;
1546
  GtkToolItemGroupInfo *info;
Johannes Schmid's avatar
Johannes Schmid committed
1547 1548 1549 1550 1551 1552 1553

  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);

1554
  info = g_ptr_array_index (palette->priv->groups, position);
1555

1556
  return info->expand;
Johannes Schmid's avatar
Johannes Schmid committed
1557 1558 1559 1560
}

/**
 * gtk_tool_palette_get_drop_item:
1561 1562 1563
 * @palette: a #GtkToolPalette
 * @x: the x position
 * @y: the y position
Johannes Schmid's avatar
Johannes Schmid committed
1564
 *
1565 1566
 * Gets the item at position (x, y).
 * See gtk_tool_palette_get_drop_group().
Johannes Schmid's avatar
Johannes Schmid committed
1567
 *
1568
 * Returns: (transfer none): the #GtkToolItem at position or %NULL if there is no such item
Johannes Schmid's avatar
Johannes Schmid committed
1569
 *
1570
 * Since: 2.20
Johannes Schmid's avatar
Johannes Schmid committed
1571 1572 1573 1574 1575 1576
 */
GtkToolItem*
gtk_tool_palette_get_drop_item (GtkToolPalette *palette,
                                gint            x,
                                gint            y)
{
1577
  GtkAllocation allocation;
1578 1579
  GtkToolItemGroup *group = gtk_tool_palette_get_drop_group (palette, x, y);
  GtkWidget *widget = GTK_WIDGET (group);
Johannes Schmid's avatar
Johannes Schmid committed
1580 1581

  if (group)
1582 1583 1584 1585 1586 1587
    {
      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
1588 1589 1590 1591 1592 1593

  return NULL;
}

/**
 * gtk_tool_palette_get_drop_group: