1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
3  *
4  * gimplist.c
5  * Copyright (C) 2001-2016 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 <stdlib.h>
24 #include <string.h> /* strcmp */
25 
26 #include <glib-object.h>
27 
28 #include "core-types.h"
29 
30 #include "gimp-memsize.h"
31 #include "gimplist.h"
32 
33 
34 enum
35 {
36   PROP_0,
37   PROP_UNIQUE_NAMES,
38   PROP_SORT_FUNC,
39   PROP_APPEND
40 };
41 
42 
43 static void         gimp_list_finalize           (GObject                 *object);
44 static void         gimp_list_set_property       (GObject                 *object,
45                                                   guint                    property_id,
46                                                   const GValue            *value,
47                                                   GParamSpec              *pspec);
48 static void         gimp_list_get_property       (GObject                 *object,
49                                                   guint                    property_id,
50                                                   GValue                  *value,
51                                                   GParamSpec              *pspec);
52 
53 static gint64       gimp_list_get_memsize        (GimpObject              *object,
54                                                   gint64                  *gui_size);
55 
56 static void         gimp_list_add                (GimpContainer           *container,
57                                                   GimpObject              *object);
58 static void         gimp_list_remove             (GimpContainer           *container,
59                                                   GimpObject              *object);
60 static void         gimp_list_reorder            (GimpContainer           *container,
61                                                   GimpObject              *object,
62                                                   gint                     new_index);
63 static void         gimp_list_clear              (GimpContainer           *container);
64 static gboolean     gimp_list_have               (GimpContainer           *container,
65                                                   GimpObject              *object);
66 static void         gimp_list_foreach            (GimpContainer           *container,
67                                                   GFunc                    func,
68                                                   gpointer                 user_data);
69 static GimpObject * gimp_list_search             (GimpContainer           *container,
70                                                   GimpContainerSearchFunc  func,
71                                                   gpointer                 user_data);
72 static gboolean     gimp_list_get_unique_names   (GimpContainer           *container);
73 static GimpObject * gimp_list_get_child_by_name  (GimpContainer           *container,
74                                                   const gchar             *name);
75 static GimpObject * gimp_list_get_child_by_index (GimpContainer           *container,
76                                                   gint                     index);
77 static gint         gimp_list_get_child_index    (GimpContainer           *container,
78                                                   GimpObject              *object);
79 
80 static void         gimp_list_uniquefy_name      (GimpList                *gimp_list,
81                                                   GimpObject              *object);
82 static void         gimp_list_object_renamed     (GimpObject              *object,
83                                                   GimpList                *list);
84 
85 
G_DEFINE_TYPE(GimpList,gimp_list,GIMP_TYPE_CONTAINER)86 G_DEFINE_TYPE (GimpList, gimp_list, GIMP_TYPE_CONTAINER)
87 
88 #define parent_class gimp_list_parent_class
89 
90 
91 static void
92 gimp_list_class_init (GimpListClass *klass)
93 {
94   GObjectClass       *object_class      = G_OBJECT_CLASS (klass);
95   GimpObjectClass    *gimp_object_class = GIMP_OBJECT_CLASS (klass);
96   GimpContainerClass *container_class   = GIMP_CONTAINER_CLASS (klass);
97 
98   object_class->finalize              = gimp_list_finalize;
99   object_class->set_property          = gimp_list_set_property;
100   object_class->get_property          = gimp_list_get_property;
101 
102   gimp_object_class->get_memsize      = gimp_list_get_memsize;
103 
104   container_class->add                = gimp_list_add;
105   container_class->remove             = gimp_list_remove;
106   container_class->reorder            = gimp_list_reorder;
107   container_class->clear              = gimp_list_clear;
108   container_class->have               = gimp_list_have;
109   container_class->foreach            = gimp_list_foreach;
110   container_class->search             = gimp_list_search;
111   container_class->get_unique_names   = gimp_list_get_unique_names;
112   container_class->get_child_by_name  = gimp_list_get_child_by_name;
113   container_class->get_child_by_index = gimp_list_get_child_by_index;
114   container_class->get_child_index    = gimp_list_get_child_index;
115 
116   g_object_class_install_property (object_class, PROP_UNIQUE_NAMES,
117                                    g_param_spec_boolean ("unique-names",
118                                                          NULL, NULL,
119                                                          FALSE,
120                                                          GIMP_PARAM_READWRITE |
121                                                          G_PARAM_CONSTRUCT_ONLY));
122 
123   g_object_class_install_property (object_class, PROP_SORT_FUNC,
124                                    g_param_spec_pointer ("sort-func",
125                                                          NULL, NULL,
126                                                          GIMP_PARAM_READWRITE |
127                                                          G_PARAM_CONSTRUCT));
128 
129   g_object_class_install_property (object_class, PROP_APPEND,
130                                    g_param_spec_boolean ("append",
131                                                          NULL, NULL,
132                                                          FALSE,
133                                                          GIMP_PARAM_READWRITE |
134                                                          G_PARAM_CONSTRUCT));
135 }
136 
137 static void
gimp_list_init(GimpList * list)138 gimp_list_init (GimpList *list)
139 {
140   list->queue        = g_queue_new ();
141   list->unique_names = FALSE;
142   list->sort_func    = NULL;
143   list->append       = FALSE;
144 }
145 
146 static void
gimp_list_finalize(GObject * object)147 gimp_list_finalize (GObject *object)
148 {
149   GimpList *list = GIMP_LIST (object);
150 
151   if (list->queue)
152     {
153       g_queue_free (list->queue);
154       list->queue = NULL;
155     }
156 
157   G_OBJECT_CLASS (parent_class)->finalize (object);
158 }
159 
160 static void
gimp_list_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)161 gimp_list_set_property (GObject      *object,
162                         guint         property_id,
163                         const GValue *value,
164                         GParamSpec   *pspec)
165 {
166   GimpList *list = GIMP_LIST (object);
167 
168   switch (property_id)
169     {
170     case PROP_UNIQUE_NAMES:
171       list->unique_names = g_value_get_boolean (value);
172       break;
173     case PROP_SORT_FUNC:
174       gimp_list_set_sort_func (list, g_value_get_pointer (value));
175       break;
176     case PROP_APPEND:
177       list->append = g_value_get_boolean (value);
178       break;
179 
180     default:
181       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
182       break;
183     }
184 }
185 
186 static void
gimp_list_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)187 gimp_list_get_property (GObject    *object,
188                         guint       property_id,
189                         GValue     *value,
190                         GParamSpec *pspec)
191 {
192   GimpList *list = GIMP_LIST (object);
193 
194   switch (property_id)
195     {
196     case PROP_UNIQUE_NAMES:
197       g_value_set_boolean (value, list->unique_names);
198       break;
199     case PROP_SORT_FUNC:
200       g_value_set_pointer (value, list->sort_func);
201       break;
202     case PROP_APPEND:
203       g_value_set_boolean (value, list->append);
204       break;
205 
206     default:
207       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
208       break;
209     }
210 }
211 
212 static gint64
gimp_list_get_memsize(GimpObject * object,gint64 * gui_size)213 gimp_list_get_memsize (GimpObject *object,
214                        gint64     *gui_size)
215 {
216   GimpList *list    = GIMP_LIST (object);
217   gint64    memsize = 0;
218 
219   if (gimp_container_get_policy (GIMP_CONTAINER (list)) ==
220       GIMP_CONTAINER_POLICY_STRONG)
221     {
222       memsize += gimp_g_queue_get_memsize_foreach (list->queue,
223                                                    (GimpMemsizeFunc) gimp_object_get_memsize,
224                                                    gui_size);
225     }
226   else
227     {
228       memsize += gimp_g_queue_get_memsize (list->queue, 0);
229     }
230 
231   return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
232                                                                   gui_size);
233 }
234 
235 static gint
gimp_list_sort_func(gconstpointer a,gconstpointer b,gpointer user_data)236 gimp_list_sort_func (gconstpointer a,
237                      gconstpointer b,
238                      gpointer      user_data)
239 {
240   GCompareFunc func = user_data;
241 
242   return func (a, b);
243 }
244 
245 static void
gimp_list_add(GimpContainer * container,GimpObject * object)246 gimp_list_add (GimpContainer *container,
247                GimpObject    *object)
248 {
249   GimpList *list = GIMP_LIST (container);
250 
251   if (list->unique_names)
252     gimp_list_uniquefy_name (list, object);
253 
254   if (list->unique_names || list->sort_func)
255     g_signal_connect (object, "name-changed",
256                       G_CALLBACK (gimp_list_object_renamed),
257                       list);
258 
259   if (list->sort_func)
260     {
261       g_queue_insert_sorted (list->queue, object, gimp_list_sort_func,
262                              list->sort_func);
263     }
264   else if (list->append)
265     {
266       g_queue_push_tail (list->queue, object);
267     }
268   else
269     {
270       g_queue_push_head (list->queue, object);
271     }
272 
273   GIMP_CONTAINER_CLASS (parent_class)->add (container, object);
274 }
275 
276 static void
gimp_list_remove(GimpContainer * container,GimpObject * object)277 gimp_list_remove (GimpContainer *container,
278                   GimpObject    *object)
279 {
280   GimpList *list = GIMP_LIST (container);
281 
282   if (list->unique_names || list->sort_func)
283     g_signal_handlers_disconnect_by_func (object,
284                                           gimp_list_object_renamed,
285                                           list);
286 
287   g_queue_remove (list->queue, object);
288 
289   GIMP_CONTAINER_CLASS (parent_class)->remove (container, object);
290 }
291 
292 static void
gimp_list_reorder(GimpContainer * container,GimpObject * object,gint new_index)293 gimp_list_reorder (GimpContainer *container,
294                    GimpObject    *object,
295                    gint           new_index)
296 {
297   GimpList *list = GIMP_LIST (container);
298 
299   g_queue_remove (list->queue, object);
300 
301   if (new_index == gimp_container_get_n_children (container) - 1)
302     g_queue_push_tail (list->queue, object);
303   else
304     g_queue_push_nth (list->queue, object, new_index);
305 }
306 
307 static void
gimp_list_clear(GimpContainer * container)308 gimp_list_clear (GimpContainer *container)
309 {
310   GimpList *list = GIMP_LIST (container);
311 
312   while (g_queue_peek_head (list->queue))
313     gimp_container_remove (container, g_queue_peek_head (list->queue));
314 }
315 
316 static gboolean
gimp_list_have(GimpContainer * container,GimpObject * object)317 gimp_list_have (GimpContainer *container,
318                 GimpObject    *object)
319 {
320   GimpList *list = GIMP_LIST (container);
321 
322   return g_queue_find (list->queue, object) ? TRUE : FALSE;
323 }
324 
325 static void
gimp_list_foreach(GimpContainer * container,GFunc func,gpointer user_data)326 gimp_list_foreach (GimpContainer *container,
327                    GFunc          func,
328                    gpointer       user_data)
329 {
330   GimpList *list = GIMP_LIST (container);
331 
332   g_queue_foreach (list->queue, func, user_data);
333 }
334 
335 static GimpObject *
gimp_list_search(GimpContainer * container,GimpContainerSearchFunc func,gpointer user_data)336 gimp_list_search (GimpContainer           *container,
337                   GimpContainerSearchFunc  func,
338                   gpointer                 user_data)
339 {
340   GimpList *list = GIMP_LIST (container);
341   GList    *iter;
342 
343   iter = list->queue->head;
344 
345   while (iter)
346     {
347       GimpObject *object = iter->data;
348 
349       iter = g_list_next (iter);
350 
351       if (func (object, user_data))
352         return object;
353     }
354 
355   return NULL;
356 }
357 
358 static gboolean
gimp_list_get_unique_names(GimpContainer * container)359 gimp_list_get_unique_names (GimpContainer *container)
360 {
361   GimpList *list = GIMP_LIST (container);
362 
363   return list->unique_names;
364 }
365 
366 static GimpObject *
gimp_list_get_child_by_name(GimpContainer * container,const gchar * name)367 gimp_list_get_child_by_name (GimpContainer *container,
368                              const gchar   *name)
369 {
370   GimpList *list = GIMP_LIST (container);
371   GList    *glist;
372 
373   for (glist = list->queue->head; glist; glist = g_list_next (glist))
374     {
375       GimpObject *object = glist->data;
376 
377       if (! strcmp (gimp_object_get_name (object), name))
378         return object;
379     }
380 
381   return NULL;
382 }
383 
384 static GimpObject *
gimp_list_get_child_by_index(GimpContainer * container,gint index)385 gimp_list_get_child_by_index (GimpContainer *container,
386                               gint           index)
387 {
388   GimpList *list = GIMP_LIST (container);
389 
390   return g_queue_peek_nth (list->queue, index);
391 }
392 
393 static gint
gimp_list_get_child_index(GimpContainer * container,GimpObject * object)394 gimp_list_get_child_index (GimpContainer *container,
395                            GimpObject    *object)
396 {
397   GimpList *list = GIMP_LIST (container);
398 
399   return g_queue_index (list->queue, (gpointer) object);
400 }
401 
402 /**
403  * gimp_list_new:
404  * @children_type: the #GType of objects the list is going to hold
405  * @unique_names:  if the list should ensure that all its children
406  *                 have unique names.
407  *
408  * Creates a new #GimpList object. Since #GimpList is a #GimpContainer
409  * implementation, it holds GimpObjects. Thus @children_type must be
410  * GIMP_TYPE_OBJECT or a type derived from it.
411  *
412  * The returned list has the #GIMP_CONTAINER_POLICY_STRONG.
413  *
414  * Return value: a new #GimpList object
415  **/
416 GimpContainer *
gimp_list_new(GType children_type,gboolean unique_names)417 gimp_list_new (GType    children_type,
418                gboolean unique_names)
419 {
420   GimpList *list;
421 
422   g_return_val_if_fail (g_type_is_a (children_type, GIMP_TYPE_OBJECT), NULL);
423 
424   list = g_object_new (GIMP_TYPE_LIST,
425                        "children-type", children_type,
426                        "policy",        GIMP_CONTAINER_POLICY_STRONG,
427                        "unique-names",  unique_names ? TRUE : FALSE,
428                        NULL);
429 
430   /* for debugging purposes only */
431   gimp_object_set_static_name (GIMP_OBJECT (list), g_type_name (children_type));
432 
433   return GIMP_CONTAINER (list);
434 }
435 
436 /**
437  * gimp_list_new_weak:
438  * @children_type: the #GType of objects the list is going to hold
439  * @unique_names:  if the list should ensure that all its children
440  *                 have unique names.
441  *
442  * Creates a new #GimpList object. Since #GimpList is a #GimpContainer
443  * implementation, it holds GimpObjects. Thus @children_type must be
444  * GIMP_TYPE_OBJECT or a type derived from it.
445  *
446  * The returned list has the #GIMP_CONTAINER_POLICY_WEAK.
447  *
448  * Return value: a new #GimpList object
449  **/
450 GimpContainer *
gimp_list_new_weak(GType children_type,gboolean unique_names)451 gimp_list_new_weak (GType    children_type,
452                     gboolean unique_names)
453 {
454   GimpList *list;
455 
456   g_return_val_if_fail (g_type_is_a (children_type, GIMP_TYPE_OBJECT), NULL);
457 
458   list = g_object_new (GIMP_TYPE_LIST,
459                        "children-type", children_type,
460                        "policy",        GIMP_CONTAINER_POLICY_WEAK,
461                        "unique-names",  unique_names ? TRUE : FALSE,
462                        NULL);
463 
464   /* for debugging purposes only */
465   gimp_object_set_static_name (GIMP_OBJECT (list), g_type_name (children_type));
466 
467   return GIMP_CONTAINER (list);
468 }
469 
470 /**
471  * gimp_list_reverse:
472  * @list: a #GimpList
473  *
474  * Reverses the order of elements in a #GimpList.
475  **/
476 void
gimp_list_reverse(GimpList * list)477 gimp_list_reverse (GimpList *list)
478 {
479   g_return_if_fail (GIMP_IS_LIST (list));
480 
481   if (gimp_container_get_n_children (GIMP_CONTAINER (list)) > 1)
482     {
483       gimp_container_freeze (GIMP_CONTAINER (list));
484       g_queue_reverse (list->queue);
485       gimp_container_thaw (GIMP_CONTAINER (list));
486     }
487 }
488 
489 /**
490  * gimp_list_set_sort_func:
491  * @list:      a #GimpList
492  * @sort_func: a #GCompareFunc
493  *
494  * Sorts the elements of @list using gimp_list_sort() and remembers the
495  * passed @sort_func in order to keep the list ordered across inserting
496  * or renaming children.
497  **/
498 void
gimp_list_set_sort_func(GimpList * list,GCompareFunc sort_func)499 gimp_list_set_sort_func (GimpList     *list,
500                          GCompareFunc  sort_func)
501 {
502   g_return_if_fail (GIMP_IS_LIST (list));
503 
504   if (sort_func != list->sort_func)
505     {
506       if (sort_func)
507         gimp_list_sort (list, sort_func);
508 
509       list->sort_func = sort_func;
510       g_object_notify (G_OBJECT (list), "sort-func");
511     }
512 }
513 
514 /**
515  * gimp_list_get_sort_func:
516  * @list: a #GimpList
517  *
518  * Returns the @list's sort function, see gimp_list_set_sort_func().
519  *
520  * Return Value: The @list's sort function.
521  **/
522 GCompareFunc
gimp_list_get_sort_func(GimpList * list)523 gimp_list_get_sort_func (GimpList*list)
524 {
525   g_return_val_if_fail (GIMP_IS_LIST (list), NULL);
526 
527   return list->sort_func;
528 }
529 
530 /**
531  * gimp_list_sort:
532  * @list: a #GimpList
533  * @sort_func: a #GCompareFunc
534  *
535  * Sorts the elements of a #GimpList according to the given @sort_func.
536  * See g_list_sort() for a detailed description of this function.
537  **/
538 void
gimp_list_sort(GimpList * list,GCompareFunc sort_func)539 gimp_list_sort (GimpList     *list,
540                 GCompareFunc  sort_func)
541 {
542   g_return_if_fail (GIMP_IS_LIST (list));
543   g_return_if_fail (sort_func != NULL);
544 
545   if (gimp_container_get_n_children (GIMP_CONTAINER (list)) > 1)
546     {
547       gimp_container_freeze (GIMP_CONTAINER (list));
548       g_queue_sort (list->queue, gimp_list_sort_func, sort_func);
549       gimp_container_thaw (GIMP_CONTAINER (list));
550     }
551 }
552 
553 /**
554  * gimp_list_sort_by_name:
555  * @list: a #GimpList
556  *
557  * Sorts the #GimpObject elements of a #GimpList by their names.
558  **/
559 void
gimp_list_sort_by_name(GimpList * list)560 gimp_list_sort_by_name (GimpList *list)
561 {
562   g_return_if_fail (GIMP_IS_LIST (list));
563 
564   gimp_list_sort (list, (GCompareFunc) gimp_object_name_collate);
565 }
566 
567 
568 /*  private functions  */
569 
570 static void
gimp_list_uniquefy_name(GimpList * gimp_list,GimpObject * object)571 gimp_list_uniquefy_name (GimpList   *gimp_list,
572                          GimpObject *object)
573 {
574   gchar *name = (gchar *) gimp_object_get_name (object);
575   GList *list;
576 
577   if (! name)
578     return;
579 
580   for (list = gimp_list->queue->head; list; list = g_list_next (list))
581     {
582       GimpObject  *object2 = list->data;
583       const gchar *name2   = gimp_object_get_name (object2);
584 
585       if (object != object2 &&
586           name2             &&
587           ! strcmp (name, name2))
588         break;
589     }
590 
591   if (list)
592     {
593       gchar *ext;
594       gchar *new_name   = NULL;
595       gint   unique_ext = 0;
596 
597       name = g_strdup (name);
598 
599       ext = strrchr (name, '#');
600 
601       if (ext)
602         {
603           gchar ext_str[8];
604 
605           unique_ext = atoi (ext + 1);
606 
607           g_snprintf (ext_str, sizeof (ext_str), "%d", unique_ext);
608 
609           /*  check if the extension really is of the form "#<n>"  */
610           if (! strcmp (ext_str, ext + 1))
611             {
612               if (ext > name && *(ext - 1) == ' ')
613                 ext--;
614 
615               *ext = '\0';
616             }
617           else
618             {
619               unique_ext = 0;
620             }
621         }
622 
623       do
624         {
625           unique_ext++;
626 
627           g_free (new_name);
628 
629           new_name = g_strdup_printf ("%s #%d", name, unique_ext);
630 
631           for (list = gimp_list->queue->head; list; list = g_list_next (list))
632             {
633               GimpObject  *object2 = list->data;
634               const gchar *name2   = gimp_object_get_name (object2);
635 
636               if (object != object2 &&
637                   name2             &&
638                   ! strcmp (new_name, name2))
639                 break;
640             }
641         }
642       while (list);
643 
644       g_free (name);
645 
646       gimp_object_take_name (object, new_name);
647     }
648 }
649 
650 static void
gimp_list_object_renamed(GimpObject * object,GimpList * list)651 gimp_list_object_renamed (GimpObject *object,
652                           GimpList   *list)
653 {
654   if (list->unique_names)
655     {
656       g_signal_handlers_block_by_func (object,
657                                        gimp_list_object_renamed,
658                                        list);
659 
660       gimp_list_uniquefy_name (list, object);
661 
662       g_signal_handlers_unblock_by_func (object,
663                                          gimp_list_object_renamed,
664                                          list);
665     }
666 
667   if (list->sort_func)
668     {
669       GList *glist;
670       gint   old_index;
671       gint   new_index = 0;
672 
673       old_index = g_queue_index (list->queue, object);
674 
675       for (glist = list->queue->head; glist; glist = g_list_next (glist))
676         {
677           GimpObject *object2 = GIMP_OBJECT (glist->data);
678 
679           if (object == object2)
680             continue;
681 
682           if (list->sort_func (object, object2) > 0)
683             new_index++;
684           else
685             break;
686         }
687 
688       if (new_index != old_index)
689         gimp_container_reorder (GIMP_CONTAINER (list), object, new_index);
690     }
691 }
692