gimpcontainer.c 31.3 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
 *
Michael Natterer's avatar
Michael Natterer committed
4 5 6
 * gimpcontainer.c
 * Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10 11 12 13 14 15 16 17
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 20 21 22
 */

#include "config.h"

23
#include <gio/gio.h>
24
#include <gegl.h>
25

26 27
#include "libgimpconfig/gimpconfig.h"

Michael Natterer's avatar
Michael Natterer committed
28
#include "core-types.h"
29

30
#include "gimp.h"
31
#include "gimp-memsize.h"
32
#include "gimpcontainer.h"
33
#include "gimpmarshal.h"
34 35


Michael Natterer's avatar
Michael Natterer committed
36 37 38
/* #define DEBUG_CONTAINER */

#ifdef DEBUG_CONTAINER
Hans Breuer's avatar
updated  
Hans Breuer committed
39
#define D(stmnt) stmnt
Michael Natterer's avatar
Michael Natterer committed
40
#else
Hans Breuer's avatar
updated  
Hans Breuer committed
41
#define D(stmnt)
Michael Natterer's avatar
Michael Natterer committed
42 43 44
#endif


45 46 47 48
enum
{
  ADD,
  REMOVE,
49
  REORDER,
50 51
  FREEZE,
  THAW,
52 53 54
  LAST_SIGNAL
};

55 56 57 58 59 60 61
enum
{
  PROP_0,
  PROP_CHILDREN_TYPE,
  PROP_POLICY
};

62

63 64 65 66 67 68 69 70 71 72 73
typedef struct
{
  gchar     *signame;
  GCallback  callback;
  gpointer   callback_data;

  GQuark     quark;  /*  used to attach the signal id's of child signals  */
} GimpContainerHandler;

struct _GimpContainerPriv
{
74 75 76 77 78 79
  GType                children_type;
  GimpContainerPolicy  policy;
  gint                 n_children;

  GList               *handlers;
  gint                 freeze_count;
80 81 82
};


83 84
/*  local function prototypes  */

85
static void   gimp_container_config_iface_init   (GimpConfigInterface *iface);
86

87
static void       gimp_container_dispose         (GObject          *object);
88

89 90 91 92 93 94 95 96
static void       gimp_container_set_property    (GObject          *object,
                                                  guint             property_id,
                                                  const GValue     *value,
                                                  GParamSpec       *pspec);
static void       gimp_container_get_property    (GObject          *object,
                                                  guint             property_id,
                                                  GValue           *value,
                                                  GParamSpec       *pspec);
97

98 99
static gint64     gimp_container_get_memsize     (GimpObject       *object,
                                                  gint64           *gui_size);
100

101 102 103 104 105
static void       gimp_container_real_add        (GimpContainer    *container,
                                                  GimpObject       *object);
static void       gimp_container_real_remove     (GimpContainer    *container,
                                                  GimpObject       *object);

106 107 108 109 110 111 112
static gboolean   gimp_container_serialize       (GimpConfig       *config,
                                                  GimpConfigWriter *writer,
                                                  gpointer          data);
static gboolean   gimp_container_deserialize     (GimpConfig       *config,
                                                  GScanner         *scanner,
                                                  gint              nest_level,
                                                  gpointer          data);
113

114
static void   gimp_container_disconnect_callback (GimpObject       *object,
115
                                                  gpointer          data);
116 117


118 119
G_DEFINE_TYPE_WITH_CODE (GimpContainer, gimp_container, GIMP_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
120
                                                gimp_container_config_iface_init))
121

122
#define parent_class gimp_container_parent_class
123

124
static guint container_signals[LAST_SIGNAL] = { 0, };
125 126 127


static void
128
gimp_container_class_init (GimpContainerClass *klass)
129
{
130 131
  GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
  GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
132 133

  container_signals[ADD] =
134
    g_signal_new ("add",
135 136 137 138 139 140 141
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpContainerClass, add),
                  NULL, NULL,
                  gimp_marshal_VOID__OBJECT,
                  G_TYPE_NONE, 1,
                  GIMP_TYPE_OBJECT);
142 143

  container_signals[REMOVE] =
144
    g_signal_new ("remove",
145 146 147 148 149 150 151
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpContainerClass, remove),
                  NULL, NULL,
                  gimp_marshal_VOID__OBJECT,
                  G_TYPE_NONE, 1,
                  GIMP_TYPE_OBJECT);
152

153
  container_signals[REORDER] =
154
    g_signal_new ("reorder",
155 156 157 158 159 160 161 162
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpContainerClass, reorder),
                  NULL, NULL,
                  gimp_marshal_VOID__OBJECT_INT,
                  G_TYPE_NONE, 2,
                  GIMP_TYPE_OBJECT,
                  G_TYPE_INT);
163

164
  container_signals[FREEZE] =
165
    g_signal_new ("freeze",
166 167 168 169 170 171
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GimpContainerClass, freeze),
                  NULL, NULL,
                  gimp_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
172 173

  container_signals[THAW] =
174
    g_signal_new ("thaw",
175 176 177 178 179 180
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GimpContainerClass, thaw),
                  NULL, NULL,
                  gimp_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
181

182 183 184 185 186
  object_class->dispose          = gimp_container_dispose;
  object_class->set_property     = gimp_container_set_property;
  object_class->get_property     = gimp_container_get_property;

  gimp_object_class->get_memsize = gimp_container_get_memsize;
187

188 189
  klass->add                     = gimp_container_real_add;
  klass->remove                  = gimp_container_real_remove;
190 191 192
  klass->reorder                 = NULL;
  klass->freeze                  = NULL;
  klass->thaw                    = NULL;
193

194
  klass->clear                   = NULL;
195 196 197 198 199
  klass->have                    = NULL;
  klass->foreach                 = NULL;
  klass->get_child_by_name       = NULL;
  klass->get_child_by_index      = NULL;
  klass->get_child_index         = NULL;
200

201
  g_object_class_install_property (object_class, PROP_CHILDREN_TYPE,
202 203 204 205 206
                                   g_param_spec_gtype ("children-type",
                                                       NULL, NULL,
                                                       GIMP_TYPE_OBJECT,
                                                       GIMP_PARAM_READWRITE |
                                                       G_PARAM_CONSTRUCT_ONLY));
207

208
  g_object_class_install_property (object_class, PROP_POLICY,
209
                                   g_param_spec_enum ("policy",
210 211 212
                                                      NULL, NULL,
                                                      GIMP_TYPE_CONTAINER_POLICY,
                                                      GIMP_CONTAINER_POLICY_STRONG,
213 214
                                                      GIMP_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT_ONLY));
215 216

  g_type_class_add_private (klass, sizeof (GimpContainerPriv));
217 218
}

219 220 221 222 223 224 225
static void
gimp_container_config_iface_init (GimpConfigInterface *iface)
{
  iface->serialize   = gimp_container_serialize;
  iface->deserialize = gimp_container_deserialize;
}

226 227 228
static void
gimp_container_init (GimpContainer *container)
{
229 230 231
  container->priv = G_TYPE_INSTANCE_GET_PRIVATE (container,
                                                 GIMP_TYPE_CONTAINER,
                                                 GimpContainerPriv);
232 233
  container->priv->handlers      = NULL;
  container->priv->freeze_count  = 0;
234

235 236 237
  container->priv->children_type = G_TYPE_NONE;
  container->priv->policy        = GIMP_CONTAINER_POLICY_STRONG;
  container->priv->n_children    = 0;
238 239 240
}

static void
241
gimp_container_dispose (GObject *object)
242
{
Michael Natterer's avatar
Michael Natterer committed
243
  GimpContainer *container = GIMP_CONTAINER (object);
244

245 246
  gimp_container_clear (container);

247
  while (container->priv->handlers)
248 249
    gimp_container_remove_handler (container,
                                   ((GimpContainerHandler *)
250
                                    container->priv->handlers->data)->quark);
251

252
  if (container->priv->children_type != G_TYPE_NONE)
253
    {
254 255
      g_type_class_unref (g_type_class_peek (container->priv->children_type));
      container->priv->children_type = G_TYPE_NONE;
256 257
    }

258
  G_OBJECT_CLASS (parent_class)->dispose (object);
259 260
}

261 262 263 264 265 266
static void
gimp_container_set_property (GObject      *object,
                             guint         property_id,
                             const GValue *value,
                             GParamSpec   *pspec)
{
Michael Natterer's avatar
Michael Natterer committed
267
  GimpContainer *container = GIMP_CONTAINER (object);
268 269 270 271

  switch (property_id)
    {
    case PROP_CHILDREN_TYPE:
272 273
      container->priv->children_type = g_value_get_gtype (value);
      g_type_class_ref (container->priv->children_type);
274 275
      break;
    case PROP_POLICY:
276
      container->priv->policy = g_value_get_enum (value);
277 278 279 280 281 282 283 284 285 286 287 288 289
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_container_get_property (GObject    *object,
                             guint       property_id,
                             GValue     *value,
                             GParamSpec *pspec)
{
Michael Natterer's avatar
Michael Natterer committed
290
  GimpContainer *container = GIMP_CONTAINER (object);
291 292 293 294

  switch (property_id)
    {
    case PROP_CHILDREN_TYPE:
295
      g_value_set_gtype (value, container->priv->children_type);
296 297
      break;
    case PROP_POLICY:
298
      g_value_set_enum (value, container->priv->policy);
299 300 301 302 303 304 305
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

306
static gint64
307
gimp_container_get_memsize (GimpObject *object,
308
                            gint64     *gui_size)
309
{
Michael Natterer's avatar
Michael Natterer committed
310 311
  GimpContainer *container = GIMP_CONTAINER (object);
  gint64         memsize   = 0;
312 313
  GList         *list;

314
  for (list = container->priv->handlers; list; list = g_list_next (list))
315
    {
316
      GimpContainerHandler *handler = list->data;
317 318 319

      memsize += (sizeof (GList) +
                  sizeof (GimpContainerHandler) +
320
                  gimp_string_get_memsize (handler->signame));
321 322
    }

323 324
  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
325 326
}

327 328 329 330
static void
gimp_container_real_add (GimpContainer *container,
                         GimpObject    *object)
{
331
  container->priv->n_children++;
332 333 334 335 336 337
}

static void
gimp_container_real_remove (GimpContainer *container,
                            GimpObject    *object)
{
338
  container->priv->n_children--;
339 340 341
}


342 343
typedef struct
{
344 345 346
  GimpConfigWriter *writer;
  gpointer          data;
  gboolean          success;
347 348 349 350 351 352 353
} SerializeData;

static void
gimp_container_serialize_foreach (GObject       *object,
                                  SerializeData *serialize_data)
{
  GimpConfigInterface *config_iface;
354
  const gchar         *name;
355

356
  config_iface = GIMP_CONFIG_GET_INTERFACE (object);
357 358 359 360 361 362 363

  if (! config_iface)
    serialize_data->success = FALSE;

  if (! serialize_data->success)
    return;

364
  gimp_config_writer_open (serialize_data->writer,
365
                           g_type_name (G_TYPE_FROM_INSTANCE (object)));
366

367
  name = gimp_object_get_name (object);
368 369

  if (name)
370
    gimp_config_writer_string (serialize_data->writer, name);
371
  else
372
    gimp_config_writer_print (serialize_data->writer, "NULL", 4);
373

374
  serialize_data->success = config_iface->serialize (GIMP_CONFIG (object),
375
                                                     serialize_data->writer,
376
                                                     serialize_data->data);
377
  gimp_config_writer_close (serialize_data->writer);
378 379 380
}

static gboolean
381
gimp_container_serialize (GimpConfig       *config,
382 383
                          GimpConfigWriter *writer,
                          gpointer          data)
384
{
385
  GimpContainer *container = GIMP_CONTAINER (config);
386 387
  SerializeData  serialize_data;

388 389 390
  serialize_data.writer  = writer;
  serialize_data.data    = data;
  serialize_data.success = TRUE;
391 392 393 394 395 396 397 398 399

  gimp_container_foreach (container,
                          (GFunc) gimp_container_serialize_foreach,
                          &serialize_data);

  return serialize_data.success;
}

static gboolean
400 401 402 403
gimp_container_deserialize (GimpConfig *config,
                            GScanner   *scanner,
                            gint        nest_level,
                            gpointer    data)
404
{
405
  GimpContainer *container = GIMP_CONTAINER (config);
406
  GTokenType     token;
407

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
  token = G_TOKEN_LEFT_PAREN;

  while (g_scanner_peek_next_token (scanner) == token)
    {
      token = g_scanner_get_next_token (scanner);

      switch (token)
        {
        case G_TOKEN_LEFT_PAREN:
          token = G_TOKEN_IDENTIFIER;
          break;

        case G_TOKEN_IDENTIFIER:
          {
            GimpObject *child;
            GType       type;
424 425
            gchar      *name      = NULL;
            gboolean    add_child = FALSE;
426 427 428 429 430 431

            type = g_type_from_name (scanner->value.v_identifier);

            if (! type)
              {
                g_scanner_error (scanner,
432
                                 "unable to determine type of '%s'",
433 434 435 436
                                 scanner->value.v_identifier);
                return FALSE;
              }

437
            if (! g_type_is_a (type, container->priv->children_type))
438 439
              {
                g_scanner_error (scanner,
440
                                 "'%s' is not a subclass of '%s'",
441
                                 scanner->value.v_identifier,
442
                                 g_type_name (container->priv->children_type));
443 444 445
                return FALSE;
              }

446
            if (! g_type_is_a (type, GIMP_TYPE_CONFIG))
447 448
              {
                g_scanner_error (scanner,
449
                                 "'%s' does not implement GimpConfigInterface",
450 451 452 453 454 455 456 457 458 459
                                 scanner->value.v_identifier);
                return FALSE;
              }

            if (! gimp_scanner_parse_string (scanner, &name))
              {
                token = G_TOKEN_STRING;
                break;
              }

460 461 462
            if (! name)
              name = g_strdup ("");

463 464
            child = gimp_container_get_child_by_name (container, name);

465
            if (! child)
466 467
              {
                if (GIMP_IS_GIMP (data))
468
                  child = g_object_new (type, "gimp", data, NULL);
469
                else
470
                  child = g_object_new (type, NULL);
471

472
                add_child = TRUE;
473
              }
474 475 476 477 478 479

            /*  always use the deserialized name. while it normally
             *  doesn't make a difference there are obscure case like
             *  template migration.
             */
            gimp_object_take_name (child, name);
480

481 482 483
            if (! GIMP_CONFIG_GET_INTERFACE (child)->deserialize (GIMP_CONFIG (child),
                                                                  scanner,
                                                                  nest_level + 1,
484
                                                                  NULL))
485
              {
486 487 488
                if (add_child)
                  g_object_unref (child);

489 490 491
                /*  warning should be already set by child  */
                return FALSE;
              }
492 493 494 495 496

            if (add_child)
              {
                gimp_container_add (container, child);

497
                if (container->priv->policy == GIMP_CONTAINER_POLICY_STRONG)
498 499
                  g_object_unref (child);
              }
500 501 502 503 504 505 506 507 508 509 510 511 512
          }
          token = G_TOKEN_RIGHT_PAREN;
          break;

        case G_TOKEN_RIGHT_PAREN:
          token = G_TOKEN_LEFT_PAREN;
          break;

        default: /* do nothing */
          break;
        }
    }

513
  return gimp_config_deserialize_return (scanner, token, nest_level);
514 515
}

516
static void
517
gimp_container_disconnect_callback (GimpObject *object,
518
                                    gpointer    data)
519
{
520
  GimpContainer *container = GIMP_CONTAINER (data);
521

522
  gimp_container_remove (container, object);
523 524
}

525
GType
526
gimp_container_get_children_type (GimpContainer *container)
527
{
Michael Natterer's avatar
Michael Natterer committed
528 529
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), G_TYPE_NONE);

530
  return container->priv->children_type;
531 532 533
}

GimpContainerPolicy
534
gimp_container_get_policy (GimpContainer *container)
535
{
Michael Natterer's avatar
Michael Natterer committed
536 537
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0);

538
  return container->priv->policy;
539 540 541
}

gint
542
gimp_container_get_n_children (GimpContainer *container)
543
{
Michael Natterer's avatar
Michael Natterer committed
544 545
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0);

546
  return container->priv->n_children;
547 548 549 550
}

gboolean
gimp_container_add (GimpContainer *container,
551
                    GimpObject    *object)
552
{
553 554
  GList *list;
  gint   n_children;
555 556 557

  g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
  g_return_val_if_fail (object != NULL, FALSE);
558
  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object,
559
                                                    container->priv->children_type),
560
                        FALSE);
561

562 563
  if (gimp_container_have (container, object))
    {
564
      g_warning ("%s: container %p already contains object %p",
565
                 G_STRFUNC, container, object);
566 567
      return FALSE;
    }
568

569
  for (list = container->priv->handlers; list; list = g_list_next (list))
570
    {
571 572
      GimpContainerHandler *handler = list->data;
      gulong                handler_id;
573

574
      handler_id = g_signal_connect (object,
575 576 577
                                     handler->signame,
                                     handler->callback,
                                     handler->callback_data);
578

579
      g_object_set_qdata (G_OBJECT (object), handler->quark,
580
                          GUINT_TO_POINTER (handler_id));
581 582
    }

583
  switch (container->priv->policy)
584 585
    {
    case GIMP_CONTAINER_POLICY_STRONG:
586
      g_object_ref (object);
587 588 589
      break;

    case GIMP_CONTAINER_POLICY_WEAK:
590
      g_signal_connect (object, "disconnect",
591 592
                        G_CALLBACK (gimp_container_disconnect_callback),
                        container);
593 594 595
      break;
    }

596
  n_children = container->priv->n_children;
597

598
  g_signal_emit (container, container_signals[ADD], 0, object);
599

600
  if (n_children == container->priv->n_children)
601 602
    {
      g_warning ("%s: GimpContainer::add() implementation did not "
603
                 "chain up. Please report this at https://www.gimp.org/bugs/",
604 605
                 G_STRFUNC);

606
      container->priv->n_children++;
607 608
    }

609 610 611 612 613
  return TRUE;
}

gboolean
gimp_container_remove (GimpContainer *container,
614
                       GimpObject    *object)
615
{
616 617
  GList *list;
  gint   n_children;
618 619 620

  g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
  g_return_val_if_fail (object != NULL, FALSE);
621
  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object,
622
                                                    container->priv->children_type),
623
                        FALSE);
624

625 626
  if (! gimp_container_have (container, object))
    {
627
      g_warning ("%s: container %p does not contain object %p",
628
                 G_STRFUNC, container, object);
629 630
      return FALSE;
    }
631

632
  for (list = container->priv->handlers; list; list = g_list_next (list))
633
    {
634 635
      GimpContainerHandler *handler = list->data;
      gulong                handler_id;
636

637 638
      handler_id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (object),
                                                         handler->quark));
639 640

      if (handler_id)
641 642
        {
          g_signal_handler_disconnect (object, handler_id);
643

644 645
          g_object_set_qdata (G_OBJECT (object), handler->quark, NULL);
        }
646 647
    }

648
  n_children = container->priv->n_children;
649 650

  g_signal_emit (container, container_signals[REMOVE], 0, object);
651

652
  if (n_children == container->priv->n_children)
653 654
    {
      g_warning ("%s: GimpContainer::remove() implementation did not "
655
                 "chain up. Please report this at https://www.gimp.org/bugs/",
656 657
                 G_STRFUNC);

658
      container->priv->n_children--;
659
    }
660

661
  switch (container->priv->policy)
662 663
    {
    case GIMP_CONTAINER_POLICY_STRONG:
664
      g_object_unref (object);
665 666 667
      break;

    case GIMP_CONTAINER_POLICY_WEAK:
668
      g_signal_handlers_disconnect_by_func (object,
669 670
                                            gimp_container_disconnect_callback,
                                            container);
671 672 673 674 675 676
      break;
    }

  return TRUE;
}

Michael Natterer's avatar
Michael Natterer committed
677 678
gboolean
gimp_container_insert (GimpContainer *container,
679 680
                       GimpObject    *object,
                       gint           index)
Michael Natterer's avatar
Michael Natterer committed
681 682 683
{
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
  g_return_val_if_fail (object != NULL, FALSE);
684
  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object,
685
                                                    container->priv->children_type),
686
                        FALSE);
Michael Natterer's avatar
Michael Natterer committed
687 688

  g_return_val_if_fail (index >= -1 &&
689
                        index <= container->priv->n_children, FALSE);
Michael Natterer's avatar
Michael Natterer committed
690 691 692

  if (gimp_container_have (container, object))
    {
693
      g_warning ("%s: container %p already contains object %p",
694
                 G_STRFUNC, container, object);
Michael Natterer's avatar
Michael Natterer committed
695 696 697 698 699 700 701 702 703 704 705
      return FALSE;
    }

  if (gimp_container_add (container, object))
    {
      return gimp_container_reorder (container, object, index);
    }

  return FALSE;
}

706 707
gboolean
gimp_container_reorder (GimpContainer *container,
708 709
                        GimpObject    *object,
                        gint           new_index)
710
{
711 712
  gint index;

713 714
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
  g_return_val_if_fail (object != NULL, FALSE);
715
  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object,
716
                                                    container->priv->children_type),
717
                        FALSE);
718 719

  g_return_val_if_fail (new_index >= -1 &&
720
                        new_index < container->priv->n_children, FALSE);
721

722 723 724 725 726 727
  if (new_index == -1)
    new_index = container->priv->n_children - 1;

  index = gimp_container_get_child_index (container, object);

  if (index == -1)
728
    {
729
      g_warning ("%s: container %p does not contain object %p",
730
                 G_STRFUNC, container, object);
731 732 733
      return FALSE;
    }

734 735 736
  if (index != new_index)
    g_signal_emit (container, container_signals[REORDER], 0,
                   object, new_index);
737 738 739 740

  return TRUE;
}

741 742 743 744 745
void
gimp_container_freeze (GimpContainer *container)
{
  g_return_if_fail (GIMP_IS_CONTAINER (container));

746
  container->priv->freeze_count++;
747

748
  if (container->priv->freeze_count == 1)
749
    g_signal_emit (container, container_signals[FREEZE], 0);
750 751 752 753 754 755 756
}

void
gimp_container_thaw (GimpContainer *container)
{
  g_return_if_fail (GIMP_IS_CONTAINER (container));

757 758
  if (container->priv->freeze_count > 0)
    container->priv->freeze_count--;
759

760
  if (container->priv->freeze_count == 0)
761
    g_signal_emit (container, container_signals[THAW], 0);
762 763 764 765 766 767 768
}

gboolean
gimp_container_frozen (GimpContainer *container)
{
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);

769
  return (container->priv->freeze_count > 0) ? TRUE : FALSE;
770 771
}

772 773 774 775 776 777 778 779
gint
gimp_container_freeze_count (GimpContainer *container)
{
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0);

  return container->priv->freeze_count;
}

780 781 782 783 784
void
gimp_container_clear (GimpContainer *container)
{
  g_return_if_fail (GIMP_IS_CONTAINER (container));

785
  if (container->priv->n_children > 0)
786 787 788 789 790 791 792
    {
      gimp_container_freeze (container);
      GIMP_CONTAINER_GET_CLASS (container)->clear (container);
      gimp_container_thaw (container);
    }
}

793
gboolean
794
gimp_container_is_empty (GimpContainer *container)
795 796 797
{
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);

798
  return (container->priv->n_children == 0);
799 800
}

801
gboolean
802 803
gimp_container_have (GimpContainer *container,
                     GimpObject    *object)
804
{
805
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
806

807
  if (container->priv->n_children < 1)
808 809
    return FALSE;

810
  return GIMP_CONTAINER_GET_CLASS (container)->have (container, object);
811 812 813
}

void
814 815 816
gimp_container_foreach (GimpContainer *container,
                        GFunc          func,
                        gpointer       user_data)
817 818 819 820
{
  g_return_if_fail (GIMP_IS_CONTAINER (container));
  g_return_if_fail (func != NULL);

821
  if (container->priv->n_children > 0)
822
    GIMP_CONTAINER_GET_CLASS (container)->foreach (container, func, user_data);
823 824
}

825
GimpObject *
826 827
gimp_container_get_child_by_name (GimpContainer *container,
                                  const gchar   *name)
828 829
{
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
830 831 832

  if (!name)
    return NULL;
833

834
  return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_name (container,
835
                                                                  name);
836 837 838
}

GimpObject *
839 840
gimp_container_get_child_by_index (GimpContainer *container,
                                   gint           index)
841 842
{
  g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
843

844
  if (index < 0 || index >= container->priv->n_children)
845
    return NULL;