1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
3  *
4  * gimpcontainer.c
5  * Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
6  *
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  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * 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 #include "config.h"
22 
23 #include <gio/gio.h>
24 #include <gegl.h>
25 
26 #include "libgimpconfig/gimpconfig.h"
27 
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 
36 /* #define DEBUG_CONTAINER */
37 
38 #ifdef DEBUG_CONTAINER
39 #define D(stmnt) stmnt
40 #else
41 #define D(stmnt)
42 #endif
43 
44 
45 enum
46 {
47   ADD,
48   REMOVE,
49   REORDER,
50   FREEZE,
51   THAW,
52   LAST_SIGNAL
53 };
54 
55 enum
56 {
57   PROP_0,
58   PROP_CHILDREN_TYPE,
59   PROP_POLICY
60 };
61 
62 
63 typedef struct
64 {
65   gchar     *signame;
66   GCallback  callback;
67   gpointer   callback_data;
68 
69   GQuark     quark;  /*  used to attach the signal id's of child signals  */
70 } GimpContainerHandler;
71 
72 struct _GimpContainerPrivate
73 {
74   GType                children_type;
75   GimpContainerPolicy  policy;
76   gint                 n_children;
77 
78   GList               *handlers;
79   gint                 freeze_count;
80 };
81 
82 
83 /*  local function prototypes  */
84 
85 static void   gimp_container_config_iface_init   (GimpConfigInterface *iface);
86 
87 static void       gimp_container_dispose         (GObject          *object);
88 
89 static void       gimp_container_set_property    (GObject          *object,
90                                                   guint             property_id,
91                                                   const GValue     *value,
92                                                   GParamSpec       *pspec);
93 static void       gimp_container_get_property    (GObject          *object,
94                                                   guint             property_id,
95                                                   GValue           *value,
96                                                   GParamSpec       *pspec);
97 
98 static gint64     gimp_container_get_memsize     (GimpObject       *object,
99                                                   gint64           *gui_size);
100 
101 static void       gimp_container_real_add        (GimpContainer    *container,
102                                                   GimpObject       *object);
103 static void       gimp_container_real_remove     (GimpContainer    *container,
104                                                   GimpObject       *object);
105 
106 static gboolean   gimp_container_serialize       (GimpConfig       *config,
107                                                   GimpConfigWriter *writer,
108                                                   gpointer          data);
109 static gboolean   gimp_container_deserialize     (GimpConfig       *config,
110                                                   GScanner         *scanner,
111                                                   gint              nest_level,
112                                                   gpointer          data);
113 
114 static void   gimp_container_disconnect_callback (GimpObject       *object,
115                                                   gpointer          data);
116 
117 static void       gimp_container_free_handler    (GimpContainer    *container,
118                                                   GimpContainerHandler *handler);
119 
120 
121 G_DEFINE_TYPE_WITH_CODE (GimpContainer, gimp_container, GIMP_TYPE_OBJECT,
122                          G_ADD_PRIVATE (GimpContainer)
123                          G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
124                                                 gimp_container_config_iface_init))
125 
126 #define parent_class gimp_container_parent_class
127 
128 static guint container_signals[LAST_SIGNAL] = { 0, };
129 
130 
131 static void
gimp_container_class_init(GimpContainerClass * klass)132 gimp_container_class_init (GimpContainerClass *klass)
133 {
134   GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
135   GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
136 
137   container_signals[ADD] =
138     g_signal_new ("add",
139                   G_TYPE_FROM_CLASS (klass),
140                   G_SIGNAL_RUN_FIRST,
141                   G_STRUCT_OFFSET (GimpContainerClass, add),
142                   NULL, NULL,
143                   gimp_marshal_VOID__OBJECT,
144                   G_TYPE_NONE, 1,
145                   GIMP_TYPE_OBJECT);
146 
147   container_signals[REMOVE] =
148     g_signal_new ("remove",
149                   G_TYPE_FROM_CLASS (klass),
150                   G_SIGNAL_RUN_FIRST,
151                   G_STRUCT_OFFSET (GimpContainerClass, remove),
152                   NULL, NULL,
153                   gimp_marshal_VOID__OBJECT,
154                   G_TYPE_NONE, 1,
155                   GIMP_TYPE_OBJECT);
156 
157   container_signals[REORDER] =
158     g_signal_new ("reorder",
159                   G_TYPE_FROM_CLASS (klass),
160                   G_SIGNAL_RUN_FIRST,
161                   G_STRUCT_OFFSET (GimpContainerClass, reorder),
162                   NULL, NULL,
163                   gimp_marshal_VOID__OBJECT_INT,
164                   G_TYPE_NONE, 2,
165                   GIMP_TYPE_OBJECT,
166                   G_TYPE_INT);
167 
168   container_signals[FREEZE] =
169     g_signal_new ("freeze",
170                   G_TYPE_FROM_CLASS (klass),
171                   G_SIGNAL_RUN_LAST,
172                   G_STRUCT_OFFSET (GimpContainerClass, freeze),
173                   NULL, NULL,
174                   gimp_marshal_VOID__VOID,
175                   G_TYPE_NONE, 0);
176 
177   container_signals[THAW] =
178     g_signal_new ("thaw",
179                   G_TYPE_FROM_CLASS (klass),
180                   G_SIGNAL_RUN_LAST,
181                   G_STRUCT_OFFSET (GimpContainerClass, thaw),
182                   NULL, NULL,
183                   gimp_marshal_VOID__VOID,
184                   G_TYPE_NONE, 0);
185 
186   object_class->dispose          = gimp_container_dispose;
187   object_class->set_property     = gimp_container_set_property;
188   object_class->get_property     = gimp_container_get_property;
189 
190   gimp_object_class->get_memsize = gimp_container_get_memsize;
191 
192   klass->add                     = gimp_container_real_add;
193   klass->remove                  = gimp_container_real_remove;
194   klass->reorder                 = NULL;
195   klass->freeze                  = NULL;
196   klass->thaw                    = NULL;
197 
198   klass->clear                   = NULL;
199   klass->have                    = NULL;
200   klass->foreach                 = NULL;
201   klass->search                  = NULL;
202   klass->get_unique_names        = NULL;
203   klass->get_child_by_name       = NULL;
204   klass->get_child_by_index      = NULL;
205   klass->get_child_index         = NULL;
206 
207   g_object_class_install_property (object_class, PROP_CHILDREN_TYPE,
208                                    g_param_spec_gtype ("children-type",
209                                                        NULL, NULL,
210                                                        GIMP_TYPE_OBJECT,
211                                                        GIMP_PARAM_READWRITE |
212                                                        G_PARAM_CONSTRUCT_ONLY));
213 
214   g_object_class_install_property (object_class, PROP_POLICY,
215                                    g_param_spec_enum ("policy",
216                                                       NULL, NULL,
217                                                       GIMP_TYPE_CONTAINER_POLICY,
218                                                       GIMP_CONTAINER_POLICY_STRONG,
219                                                       GIMP_PARAM_READWRITE |
220                                                       G_PARAM_CONSTRUCT_ONLY));
221 }
222 
223 static void
gimp_container_config_iface_init(GimpConfigInterface * iface)224 gimp_container_config_iface_init (GimpConfigInterface *iface)
225 {
226   iface->serialize   = gimp_container_serialize;
227   iface->deserialize = gimp_container_deserialize;
228 }
229 
230 static void
gimp_container_init(GimpContainer * container)231 gimp_container_init (GimpContainer *container)
232 {
233   container->priv = gimp_container_get_instance_private (container);
234   container->priv->handlers      = NULL;
235   container->priv->freeze_count  = 0;
236 
237   container->priv->children_type = G_TYPE_NONE;
238   container->priv->policy        = GIMP_CONTAINER_POLICY_STRONG;
239   container->priv->n_children    = 0;
240 }
241 
242 static void
gimp_container_dispose(GObject * object)243 gimp_container_dispose (GObject *object)
244 {
245   GimpContainer *container = GIMP_CONTAINER (object);
246 
247   gimp_container_clear (container);
248 
249   while (container->priv->handlers)
250     gimp_container_remove_handler (container,
251                                    ((GimpContainerHandler *)
252                                     container->priv->handlers->data)->quark);
253 
254   if (container->priv->children_type != G_TYPE_NONE)
255     {
256       g_type_class_unref (g_type_class_peek (container->priv->children_type));
257       container->priv->children_type = G_TYPE_NONE;
258     }
259 
260   G_OBJECT_CLASS (parent_class)->dispose (object);
261 }
262 
263 static void
gimp_container_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)264 gimp_container_set_property (GObject      *object,
265                              guint         property_id,
266                              const GValue *value,
267                              GParamSpec   *pspec)
268 {
269   GimpContainer *container = GIMP_CONTAINER (object);
270 
271   switch (property_id)
272     {
273     case PROP_CHILDREN_TYPE:
274       container->priv->children_type = g_value_get_gtype (value);
275       g_type_class_ref (container->priv->children_type);
276       break;
277     case PROP_POLICY:
278       container->priv->policy = g_value_get_enum (value);
279       break;
280     default:
281       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
282       break;
283     }
284 }
285 
286 static void
gimp_container_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)287 gimp_container_get_property (GObject    *object,
288                              guint       property_id,
289                              GValue     *value,
290                              GParamSpec *pspec)
291 {
292   GimpContainer *container = GIMP_CONTAINER (object);
293 
294   switch (property_id)
295     {
296     case PROP_CHILDREN_TYPE:
297       g_value_set_gtype (value, container->priv->children_type);
298       break;
299     case PROP_POLICY:
300       g_value_set_enum (value, container->priv->policy);
301       break;
302     default:
303       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
304       break;
305     }
306 }
307 
308 static gint64
gimp_container_get_memsize(GimpObject * object,gint64 * gui_size)309 gimp_container_get_memsize (GimpObject *object,
310                             gint64     *gui_size)
311 {
312   GimpContainer *container = GIMP_CONTAINER (object);
313   gint64         memsize   = 0;
314   GList         *list;
315 
316   for (list = container->priv->handlers; list; list = g_list_next (list))
317     {
318       GimpContainerHandler *handler = list->data;
319 
320       memsize += (sizeof (GList) +
321                   sizeof (GimpContainerHandler) +
322                   gimp_string_get_memsize (handler->signame));
323     }
324 
325   return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
326                                                                   gui_size);
327 }
328 
329 static void
gimp_container_real_add(GimpContainer * container,GimpObject * object)330 gimp_container_real_add (GimpContainer *container,
331                          GimpObject    *object)
332 {
333   container->priv->n_children++;
334 }
335 
336 static void
gimp_container_real_remove(GimpContainer * container,GimpObject * object)337 gimp_container_real_remove (GimpContainer *container,
338                             GimpObject    *object)
339 {
340   container->priv->n_children--;
341 }
342 
343 
344 typedef struct
345 {
346   GimpConfigWriter *writer;
347   gpointer          data;
348   gboolean          success;
349 } SerializeData;
350 
351 static void
gimp_container_serialize_foreach(GObject * object,SerializeData * serialize_data)352 gimp_container_serialize_foreach (GObject       *object,
353                                   SerializeData *serialize_data)
354 {
355   GimpConfigInterface *config_iface;
356   const gchar         *name;
357 
358   config_iface = GIMP_CONFIG_GET_INTERFACE (object);
359 
360   if (! config_iface)
361     serialize_data->success = FALSE;
362 
363   if (! serialize_data->success)
364     return;
365 
366   gimp_config_writer_open (serialize_data->writer,
367                            g_type_name (G_TYPE_FROM_INSTANCE (object)));
368 
369   name = gimp_object_get_name (object);
370 
371   if (name)
372     gimp_config_writer_string (serialize_data->writer, name);
373   else
374     gimp_config_writer_print (serialize_data->writer, "NULL", 4);
375 
376   serialize_data->success = config_iface->serialize (GIMP_CONFIG (object),
377                                                      serialize_data->writer,
378                                                      serialize_data->data);
379   gimp_config_writer_close (serialize_data->writer);
380 }
381 
382 static gboolean
gimp_container_serialize(GimpConfig * config,GimpConfigWriter * writer,gpointer data)383 gimp_container_serialize (GimpConfig       *config,
384                           GimpConfigWriter *writer,
385                           gpointer          data)
386 {
387   GimpContainer *container = GIMP_CONTAINER (config);
388   SerializeData  serialize_data;
389 
390   serialize_data.writer  = writer;
391   serialize_data.data    = data;
392   serialize_data.success = TRUE;
393 
394   gimp_container_foreach (container,
395                           (GFunc) gimp_container_serialize_foreach,
396                           &serialize_data);
397 
398   return serialize_data.success;
399 }
400 
401 static gboolean
gimp_container_deserialize(GimpConfig * config,GScanner * scanner,gint nest_level,gpointer data)402 gimp_container_deserialize (GimpConfig *config,
403                             GScanner   *scanner,
404                             gint        nest_level,
405                             gpointer    data)
406 {
407   GimpContainer *container = GIMP_CONTAINER (config);
408   GTokenType     token;
409 
410   token = G_TOKEN_LEFT_PAREN;
411 
412   while (g_scanner_peek_next_token (scanner) == token)
413     {
414       token = g_scanner_get_next_token (scanner);
415 
416       switch (token)
417         {
418         case G_TOKEN_LEFT_PAREN:
419           token = G_TOKEN_IDENTIFIER;
420           break;
421 
422         case G_TOKEN_IDENTIFIER:
423           {
424             GimpObject *child     = NULL;
425             GType       type;
426             gchar      *name      = NULL;
427             gboolean    add_child = FALSE;
428 
429             type = g_type_from_name (scanner->value.v_identifier);
430 
431             if (! type)
432               {
433                 g_scanner_error (scanner,
434                                  "unable to determine type of '%s'",
435                                  scanner->value.v_identifier);
436                 return FALSE;
437               }
438 
439             if (! g_type_is_a (type, container->priv->children_type))
440               {
441                 g_scanner_error (scanner,
442                                  "'%s' is not a subclass of '%s'",
443                                  scanner->value.v_identifier,
444                                  g_type_name (container->priv->children_type));
445                 return FALSE;
446               }
447 
448             if (! g_type_is_a (type, GIMP_TYPE_CONFIG))
449               {
450                 g_scanner_error (scanner,
451                                  "'%s' does not implement GimpConfigInterface",
452                                  scanner->value.v_identifier);
453                 return FALSE;
454               }
455 
456             if (! gimp_scanner_parse_string (scanner, &name))
457               {
458                 token = G_TOKEN_STRING;
459                 break;
460               }
461 
462             if (! name)
463               name = g_strdup ("");
464 
465             if (gimp_container_get_unique_names (container))
466               child = gimp_container_get_child_by_name (container, name);
467 
468             if (! child)
469               {
470                 if (GIMP_IS_GIMP (data))
471                   child = g_object_new (type, "gimp", data, NULL);
472                 else
473                   child = g_object_new (type, NULL);
474 
475                 add_child = TRUE;
476               }
477 
478             /*  always use the deserialized name. while it normally
479              *  doesn't make a difference there are obscure case like
480              *  template migration.
481              */
482             gimp_object_take_name (child, name);
483 
484             if (! GIMP_CONFIG_GET_INTERFACE (child)->deserialize (GIMP_CONFIG (child),
485                                                                   scanner,
486                                                                   nest_level + 1,
487                                                                   NULL))
488               {
489                 if (add_child)
490                   g_object_unref (child);
491 
492                 /*  warning should be already set by child  */
493                 return FALSE;
494               }
495 
496             if (add_child)
497               {
498                 gimp_container_add (container, child);
499 
500                 if (container->priv->policy == GIMP_CONTAINER_POLICY_STRONG)
501                   g_object_unref (child);
502               }
503           }
504           token = G_TOKEN_RIGHT_PAREN;
505           break;
506 
507         case G_TOKEN_RIGHT_PAREN:
508           token = G_TOKEN_LEFT_PAREN;
509           break;
510 
511         default: /* do nothing */
512           break;
513         }
514     }
515 
516   return gimp_config_deserialize_return (scanner, token, nest_level);
517 }
518 
519 static void
gimp_container_disconnect_callback(GimpObject * object,gpointer data)520 gimp_container_disconnect_callback (GimpObject *object,
521                                     gpointer    data)
522 {
523   GimpContainer *container = GIMP_CONTAINER (data);
524 
525   gimp_container_remove (container, object);
526 }
527 
528 static void
gimp_container_free_handler_foreach_func(GimpObject * object,GimpContainerHandler * handler)529 gimp_container_free_handler_foreach_func (GimpObject           *object,
530                                           GimpContainerHandler *handler)
531 {
532   gulong handler_id;
533 
534   handler_id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (object),
535                                                      handler->quark));
536 
537   if (handler_id)
538     {
539       g_signal_handler_disconnect (object, handler_id);
540 
541       g_object_set_qdata (G_OBJECT (object), handler->quark, NULL);
542     }
543 }
544 
545 static void
gimp_container_free_handler(GimpContainer * container,GimpContainerHandler * handler)546 gimp_container_free_handler (GimpContainer        *container,
547                              GimpContainerHandler *handler)
548 {
549   D (g_print ("%s: id = %d\n", G_STRFUNC, handler->quark));
550 
551   gimp_container_foreach (container,
552                           (GFunc) gimp_container_free_handler_foreach_func,
553                           handler);
554 
555   g_free (handler->signame);
556   g_slice_free (GimpContainerHandler, handler);
557 }
558 
559 GType
gimp_container_get_children_type(GimpContainer * container)560 gimp_container_get_children_type (GimpContainer *container)
561 {
562   g_return_val_if_fail (GIMP_IS_CONTAINER (container), G_TYPE_NONE);
563 
564   return container->priv->children_type;
565 }
566 
567 GimpContainerPolicy
gimp_container_get_policy(GimpContainer * container)568 gimp_container_get_policy (GimpContainer *container)
569 {
570   g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0);
571 
572   return container->priv->policy;
573 }
574 
575 gint
gimp_container_get_n_children(GimpContainer * container)576 gimp_container_get_n_children (GimpContainer *container)
577 {
578   g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0);
579 
580   return container->priv->n_children;
581 }
582 
583 gboolean
gimp_container_add(GimpContainer * container,GimpObject * object)584 gimp_container_add (GimpContainer *container,
585                     GimpObject    *object)
586 {
587   GList *list;
588   gint   n_children;
589 
590   g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
591   g_return_val_if_fail (object != NULL, FALSE);
592   g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object,
593                                                     container->priv->children_type),
594                         FALSE);
595 
596   if (gimp_container_have (container, object))
597     {
598       g_warning ("%s: container %p already contains object %p",
599                  G_STRFUNC, container, object);
600       return FALSE;
601     }
602 
603   for (list = container->priv->handlers; list; list = g_list_next (list))
604     {
605       GimpContainerHandler *handler = list->data;
606       gulong                handler_id;
607 
608       handler_id = g_signal_connect (object,
609                                      handler->signame,
610                                      handler->callback,
611                                      handler->callback_data);
612 
613       g_object_set_qdata (G_OBJECT (object), handler->quark,
614                           GUINT_TO_POINTER (handler_id));
615     }
616 
617   switch (container->priv->policy)
618     {
619     case GIMP_CONTAINER_POLICY_STRONG:
620       g_object_ref (object);
621       break;
622 
623     case GIMP_CONTAINER_POLICY_WEAK:
624       g_signal_connect (object, "disconnect",
625                         G_CALLBACK (gimp_container_disconnect_callback),
626                         container);
627       break;
628     }
629 
630   n_children = container->priv->n_children;
631 
632   g_signal_emit (container, container_signals[ADD], 0, object);
633 
634   if (n_children == container->priv->n_children)
635     {
636       g_warning ("%s: GimpContainer::add() implementation did not "
637                  "chain up. Please report this at https://www.gimp.org/bugs/",
638                  G_STRFUNC);
639 
640       container->priv->n_children++;
641     }
642 
643   return TRUE;
644 }
645 
646 gboolean
gimp_container_remove(GimpContainer * container,GimpObject * object)647 gimp_container_remove (GimpContainer *container,
648                        GimpObject    *object)
649 {
650   GList *list;
651   gint   n_children;
652 
653   g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
654   g_return_val_if_fail (object != NULL, FALSE);
655   g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object,
656                                                     container->priv->children_type),
657                         FALSE);
658 
659   if (! gimp_container_have (container, object))
660     {
661       g_warning ("%s: container %p does not contain object %p",
662                  G_STRFUNC, container, object);
663       return FALSE;
664     }
665 
666   for (list = container->priv->handlers; list; list = g_list_next (list))
667     {
668       GimpContainerHandler *handler = list->data;
669       gulong                handler_id;
670 
671       handler_id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (object),
672                                                          handler->quark));
673 
674       if (handler_id)
675         {
676           g_signal_handler_disconnect (object, handler_id);
677 
678           g_object_set_qdata (G_OBJECT (object), handler->quark, NULL);
679         }
680     }
681 
682   n_children = container->priv->n_children;
683 
684   g_signal_emit (container, container_signals[REMOVE], 0, object);
685 
686   if (n_children == container->priv->n_children)
687     {
688       g_warning ("%s: GimpContainer::remove() implementation did not "
689                  "chain up. Please report this at https://www.gimp.org/bugs/",
690                  G_STRFUNC);
691 
692       container->priv->n_children--;
693     }
694 
695   switch (container->priv->policy)
696     {
697     case GIMP_CONTAINER_POLICY_STRONG:
698       g_object_unref (object);
699       break;
700 
701     case GIMP_CONTAINER_POLICY_WEAK:
702       g_signal_handlers_disconnect_by_func (object,
703                                             gimp_container_disconnect_callback,
704                                             container);
705       break;
706     }
707 
708   return TRUE;
709 }
710 
711 gboolean
gimp_container_insert(GimpContainer * container,GimpObject * object,gint index)712 gimp_container_insert (GimpContainer *container,
713                        GimpObject    *object,
714                        gint           index)
715 {
716   g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
717   g_return_val_if_fail (object != NULL, FALSE);
718   g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object,
719                                                     container->priv->children_type),
720                         FALSE);
721 
722   g_return_val_if_fail (index >= -1 &&
723                         index <= container->priv->n_children, FALSE);
724 
725   if (gimp_container_have (container, object))
726     {
727       g_warning ("%s: container %p already contains object %p",
728                  G_STRFUNC, container, object);
729       return FALSE;
730     }
731 
732   if (gimp_container_add (container, object))
733     {
734       return gimp_container_reorder (container, object, index);
735     }
736 
737   return FALSE;
738 }
739 
740 gboolean
gimp_container_reorder(GimpContainer * container,GimpObject * object,gint new_index)741 gimp_container_reorder (GimpContainer *container,
742                         GimpObject    *object,
743                         gint           new_index)
744 {
745   gint index;
746 
747   g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
748   g_return_val_if_fail (object != NULL, FALSE);
749   g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object,
750                                                     container->priv->children_type),
751                         FALSE);
752 
753   g_return_val_if_fail (new_index >= -1 &&
754                         new_index < container->priv->n_children, FALSE);
755 
756   if (new_index == -1)
757     new_index = container->priv->n_children - 1;
758 
759   index = gimp_container_get_child_index (container, object);
760 
761   if (index == -1)
762     {
763       g_warning ("%s: container %p does not contain object %p",
764                  G_STRFUNC, container, object);
765       return FALSE;
766     }
767 
768   if (index != new_index)
769     g_signal_emit (container, container_signals[REORDER], 0,
770                    object, new_index);
771 
772   return TRUE;
773 }
774 
775 void
gimp_container_freeze(GimpContainer * container)776 gimp_container_freeze (GimpContainer *container)
777 {
778   g_return_if_fail (GIMP_IS_CONTAINER (container));
779 
780   container->priv->freeze_count++;
781 
782   if (container->priv->freeze_count == 1)
783     g_signal_emit (container, container_signals[FREEZE], 0);
784 }
785 
786 void
gimp_container_thaw(GimpContainer * container)787 gimp_container_thaw (GimpContainer *container)
788 {
789   g_return_if_fail (GIMP_IS_CONTAINER (container));
790 
791   if (container->priv->freeze_count > 0)
792     container->priv->freeze_count--;
793 
794   if (container->priv->freeze_count == 0)
795     g_signal_emit (container, container_signals[THAW], 0);
796 }
797 
798 gboolean
gimp_container_frozen(GimpContainer * container)799 gimp_container_frozen (GimpContainer *container)
800 {
801   g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
802 
803   return (container->priv->freeze_count > 0) ? TRUE : FALSE;
804 }
805 
806 gint
gimp_container_freeze_count(GimpContainer * container)807 gimp_container_freeze_count (GimpContainer *container)
808 {
809   g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0);
810 
811   return container->priv->freeze_count;
812 }
813 
814 void
gimp_container_clear(GimpContainer * container)815 gimp_container_clear (GimpContainer *container)
816 {
817   g_return_if_fail (GIMP_IS_CONTAINER (container));
818 
819   if (container->priv->n_children > 0)
820     {
821       gimp_container_freeze (container);
822       GIMP_CONTAINER_GET_CLASS (container)->clear (container);
823       gimp_container_thaw (container);
824     }
825 }
826 
827 gboolean
gimp_container_is_empty(GimpContainer * container)828 gimp_container_is_empty (GimpContainer *container)
829 {
830   g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
831 
832   return (container->priv->n_children == 0);
833 }
834 
835 gboolean
gimp_container_have(GimpContainer * container,GimpObject * object)836 gimp_container_have (GimpContainer *container,
837                      GimpObject    *object)
838 {
839   g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
840 
841   if (container->priv->n_children < 1)
842     return FALSE;
843 
844   return GIMP_CONTAINER_GET_CLASS (container)->have (container, object);
845 }
846 
847 void
gimp_container_foreach(GimpContainer * container,GFunc func,gpointer user_data)848 gimp_container_foreach (GimpContainer *container,
849                         GFunc          func,
850                         gpointer       user_data)
851 {
852   g_return_if_fail (GIMP_IS_CONTAINER (container));
853   g_return_if_fail (func != NULL);
854 
855   if (container->priv->n_children > 0)
856     GIMP_CONTAINER_GET_CLASS (container)->foreach (container, func, user_data);
857 }
858 
859 GimpObject *
gimp_container_search(GimpContainer * container,GimpContainerSearchFunc func,gpointer user_data)860 gimp_container_search (GimpContainer           *container,
861                        GimpContainerSearchFunc  func,
862                        gpointer                 user_data)
863 {
864   g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
865   g_return_val_if_fail (func != NULL, NULL);
866 
867   if (container->priv->n_children > 0)
868     {
869       return GIMP_CONTAINER_GET_CLASS (container)->search (container,
870                                                            func, user_data);
871     }
872 
873   return NULL;
874 }
875 
876 gboolean
gimp_container_get_unique_names(GimpContainer * container)877 gimp_container_get_unique_names (GimpContainer *container)
878 {
879   g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE);
880 
881   if (GIMP_CONTAINER_GET_CLASS (container)->get_unique_names)
882     return GIMP_CONTAINER_GET_CLASS (container)->get_unique_names (container);
883 
884   return FALSE;
885 }
886 
887 GimpObject *
gimp_container_get_child_by_name(GimpContainer * container,const gchar * name)888 gimp_container_get_child_by_name (GimpContainer *container,
889                                   const gchar   *name)
890 {
891   g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
892 
893   if (!name)
894     return NULL;
895 
896   return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_name (container,
897                                                                   name);
898 }
899 
900 GimpObject *
gimp_container_get_child_by_index(GimpContainer * container,gint index)901 gimp_container_get_child_by_index (GimpContainer *container,
902                                    gint           index)
903 {
904   g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
905 
906   if (index < 0 || index >= container->priv->n_children)
907     return NULL;
908 
909   return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_index (container,
910                                                                    index);
911 }
912 
913 /**
914  * gimp_container_get_first_child:
915  * @container: a #GimpContainer
916  *
917  * Return value: the first child object stored in @container or %NULL if the
918  *               container is empty
919  */
920 GimpObject *
gimp_container_get_first_child(GimpContainer * container)921 gimp_container_get_first_child (GimpContainer *container)
922 {
923   g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
924 
925   if (container->priv->n_children > 0)
926     return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_index (container,
927                                                                      0);
928 
929   return NULL;
930 }
931 
932 /**
933  * gimp_container_get_last_child:
934  * @container: a #GimpContainer
935  *
936  * Return value: the last child object stored in @container or %NULL if the
937  *               container is empty
938  */
939 GimpObject *
gimp_container_get_last_child(GimpContainer * container)940 gimp_container_get_last_child (GimpContainer *container)
941 {
942   g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
943 
944   if (container->priv->n_children > 0)
945     return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_index (container,
946                                                                      container->priv->n_children - 1);
947 
948   return NULL;
949 }
950 
951 gint
gimp_container_get_child_index(GimpContainer * container,GimpObject * object)952 gimp_container_get_child_index (GimpContainer *container,
953                                 GimpObject    *object)
954 {
955   g_return_val_if_fail (GIMP_IS_CONTAINER (container), -1);
956   g_return_val_if_fail (object != NULL, -1);
957   g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object,
958                                                     container->priv->children_type),
959                         -1);
960 
961   return GIMP_CONTAINER_GET_CLASS (container)->get_child_index (container,
962                                                                 object);
963 }
964 
965 GimpObject *
gimp_container_get_neighbor_of(GimpContainer * container,GimpObject * object)966 gimp_container_get_neighbor_of (GimpContainer *container,
967                                 GimpObject    *object)
968 {
969   gint index;
970 
971   g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
972   g_return_val_if_fail (GIMP_IS_OBJECT (object), NULL);
973 
974   index = gimp_container_get_child_index (container, object);
975 
976   if (index != -1)
977     {
978       GimpObject *new;
979 
980       new = gimp_container_get_child_by_index (container, index + 1);
981 
982       if (! new && index > 0)
983         new = gimp_container_get_child_by_index (container, index - 1);
984 
985       return new;
986     }
987 
988   return NULL;
989 }
990 
991 static void
gimp_container_get_name_array_foreach_func(GimpObject * object,gchar *** iter)992 gimp_container_get_name_array_foreach_func (GimpObject   *object,
993                                             gchar      ***iter)
994 {
995   gchar **array = *iter;
996 
997   *array = g_strdup (gimp_object_get_name (object));
998   (*iter)++;
999 }
1000 
1001 gchar **
gimp_container_get_name_array(GimpContainer * container,gint * length)1002 gimp_container_get_name_array (GimpContainer *container,
1003                                gint          *length)
1004 {
1005   gchar **names;
1006   gchar **iter;
1007 
1008   g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
1009   g_return_val_if_fail (length != NULL, NULL);
1010 
1011   *length = gimp_container_get_n_children (container);
1012   if (*length == 0)
1013     return NULL;
1014 
1015   names = iter = g_new (gchar *, *length);
1016 
1017   gimp_container_foreach (container,
1018                           (GFunc) gimp_container_get_name_array_foreach_func,
1019                           &iter);
1020 
1021   return names;
1022 }
1023 
1024 static void
gimp_container_add_handler_foreach_func(GimpObject * object,GimpContainerHandler * handler)1025 gimp_container_add_handler_foreach_func (GimpObject           *object,
1026                                          GimpContainerHandler *handler)
1027 {
1028   gulong handler_id;
1029 
1030   handler_id = g_signal_connect (object,
1031                                  handler->signame,
1032                                  handler->callback,
1033                                  handler->callback_data);
1034 
1035   g_object_set_qdata (G_OBJECT (object), handler->quark,
1036                       GUINT_TO_POINTER (handler_id));
1037 }
1038 
1039 GQuark
gimp_container_add_handler(GimpContainer * container,const gchar * signame,GCallback callback,gpointer callback_data)1040 gimp_container_add_handler (GimpContainer *container,
1041                             const gchar   *signame,
1042                             GCallback      callback,
1043                             gpointer       callback_data)
1044 {
1045   GimpContainerHandler *handler;
1046   gchar                *key;
1047 
1048   static gint           handler_id = 0;
1049 
1050   g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0);
1051   g_return_val_if_fail (signame != NULL, 0);
1052   g_return_val_if_fail (callback != NULL, 0);
1053 
1054   if (! g_str_has_prefix (signame, "notify::"))
1055     g_return_val_if_fail (g_signal_lookup (signame,
1056                                            container->priv->children_type), 0);
1057 
1058   handler = g_slice_new0 (GimpContainerHandler);
1059 
1060   /*  create a unique key for this handler  */
1061   key = g_strdup_printf ("%s-%d", signame, handler_id++);
1062 
1063   handler->signame       = g_strdup (signame);
1064   handler->callback      = callback;
1065   handler->callback_data = callback_data;
1066   handler->quark         = g_quark_from_string (key);
1067 
1068   D (g_print ("%s: key = %s, id = %d\n", G_STRFUNC, key, handler->quark));
1069 
1070   g_free (key);
1071 
1072   container->priv->handlers = g_list_prepend (container->priv->handlers, handler);
1073 
1074   gimp_container_foreach (container,
1075                           (GFunc) gimp_container_add_handler_foreach_func,
1076                           handler);
1077 
1078   return handler->quark;
1079 }
1080 
1081 void
gimp_container_remove_handler(GimpContainer * container,GQuark id)1082 gimp_container_remove_handler (GimpContainer *container,
1083                                GQuark         id)
1084 {
1085   GimpContainerHandler *handler;
1086   GList                *list;
1087 
1088   g_return_if_fail (GIMP_IS_CONTAINER (container));
1089   g_return_if_fail (id != 0);
1090 
1091   for (list = container->priv->handlers; list; list = g_list_next (list))
1092     {
1093       handler = (GimpContainerHandler *) list->data;
1094 
1095       if (handler->quark == id)
1096         break;
1097     }
1098 
1099   if (! list)
1100     {
1101       g_warning ("%s: tried to remove handler which unknown id %d",
1102                  G_STRFUNC, id);
1103       return;
1104     }
1105 
1106   gimp_container_free_handler (container, handler);
1107 
1108   container->priv->handlers = g_list_delete_link (container->priv->handlers,
1109                                                   list);
1110 }
1111 
1112 void
gimp_container_remove_handlers_by_func(GimpContainer * container,GCallback callback,gpointer callback_data)1113 gimp_container_remove_handlers_by_func (GimpContainer *container,
1114                                         GCallback      callback,
1115                                         gpointer       callback_data)
1116 {
1117   GList *list;
1118 
1119   g_return_if_fail (GIMP_IS_CONTAINER (container));
1120   g_return_if_fail (callback != NULL);
1121 
1122   list = container->priv->handlers;
1123 
1124   while (list)
1125     {
1126       GimpContainerHandler *handler = list->data;
1127       GList                *next    = g_list_next (list);
1128 
1129       if (handler->callback      == callback &&
1130           handler->callback_data == callback_data)
1131         {
1132           gimp_container_free_handler (container, handler);
1133 
1134           container->priv->handlers = g_list_delete_link (
1135             container->priv->handlers, list);
1136         }
1137 
1138       list = next;
1139     }
1140 }
1141 
1142 void
gimp_container_remove_handlers_by_data(GimpContainer * container,gpointer callback_data)1143 gimp_container_remove_handlers_by_data (GimpContainer *container,
1144                                         gpointer       callback_data)
1145 {
1146   GList *list;
1147 
1148   g_return_if_fail (GIMP_IS_CONTAINER (container));
1149 
1150   list = container->priv->handlers;
1151 
1152   while (list)
1153     {
1154       GimpContainerHandler *handler = list->data;
1155       GList                *next    = g_list_next (list);
1156 
1157       if (handler->callback_data == callback_data)
1158         {
1159           gimp_container_free_handler (container, handler);
1160 
1161           container->priv->handlers = g_list_delete_link (
1162             container->priv->handlers, list);
1163         }
1164 
1165       list = next;
1166     }
1167 }
1168