1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
3  *
4  * gimpviewable.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 <string.h>
24 
25 #include <cairo.h>
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27 #include <gegl.h>
28 
29 #include "libgimpcolor/gimpcolor.h"
30 #include "libgimpmath/gimpmath.h"
31 #include "libgimpconfig/gimpconfig.h"
32 
33 #include "core-types.h"
34 
35 #include "gimp-memsize.h"
36 #include "gimpcontainer.h"
37 #include "gimpcontext.h"
38 #include "gimpmarshal.h"
39 #include "gimptempbuf.h"
40 #include "gimpviewable.h"
41 
42 #include "icons/Color/gimp-core-pixbufs.c"
43 
44 
45 enum
46 {
47   PROP_0,
48   PROP_STOCK_ID, /* compat */
49   PROP_ICON_NAME,
50   PROP_ICON_PIXBUF,
51   PROP_FROZEN
52 };
53 
54 enum
55 {
56   INVALIDATE_PREVIEW,
57   SIZE_CHANGED,
58   EXPANDED_CHANGED,
59   ANCESTRY_CHANGED,
60   LAST_SIGNAL
61 };
62 
63 
64 typedef struct _GimpViewablePrivate GimpViewablePrivate;
65 
66 struct _GimpViewablePrivate
67 {
68   gchar        *icon_name;
69   GdkPixbuf    *icon_pixbuf;
70   gint          freeze_count;
71   gboolean      invalidate_pending;
72   gboolean      size_changed_prending;
73   GimpViewable *parent;
74   gint          depth;
75 
76   GimpTempBuf  *preview_temp_buf;
77   GdkPixbuf    *preview_pixbuf;
78 };
79 
80 #define GET_PRIVATE(viewable) ((GimpViewablePrivate *) gimp_viewable_get_instance_private ((GimpViewable *) (viewable)))
81 
82 
83 static void    gimp_viewable_config_iface_init (GimpConfigInterface *iface);
84 
85 static void    gimp_viewable_finalize               (GObject        *object);
86 static void    gimp_viewable_set_property           (GObject        *object,
87                                                      guint           property_id,
88                                                      const GValue   *value,
89                                                      GParamSpec     *pspec);
90 static void    gimp_viewable_get_property           (GObject        *object,
91                                                      guint           property_id,
92                                                      GValue         *value,
93                                                      GParamSpec     *pspec);
94 
95 static gint64  gimp_viewable_get_memsize             (GimpObject    *object,
96                                                       gint64        *gui_size);
97 
98 static void    gimp_viewable_real_invalidate_preview (GimpViewable  *viewable);
99 static void    gimp_viewable_real_ancestry_changed   (GimpViewable  *viewable);
100 
101 static GdkPixbuf * gimp_viewable_real_get_new_pixbuf (GimpViewable  *viewable,
102                                                       GimpContext   *context,
103                                                       gint           width,
104                                                       gint           height);
105 static void    gimp_viewable_real_get_preview_size   (GimpViewable  *viewable,
106                                                       gint           size,
107                                                       gboolean       popup,
108                                                       gboolean       dot_for_dot,
109                                                       gint          *width,
110                                                       gint          *height);
111 static gboolean gimp_viewable_real_get_popup_size    (GimpViewable  *viewable,
112                                                       gint           width,
113                                                       gint           height,
114                                                       gboolean       dot_for_dot,
115                                                       gint          *popup_width,
116                                                       gint          *popup_height);
117 static gchar * gimp_viewable_real_get_description    (GimpViewable  *viewable,
118                                                       gchar        **tooltip);
119 static gboolean gimp_viewable_real_is_name_editable  (GimpViewable  *viewable);
120 static GimpContainer * gimp_viewable_real_get_children (GimpViewable *viewable);
121 
122 static gboolean gimp_viewable_serialize_property     (GimpConfig    *config,
123                                                       guint          property_id,
124                                                       const GValue  *value,
125                                                       GParamSpec    *pspec,
126                                                       GimpConfigWriter *writer);
127 static gboolean gimp_viewable_deserialize_property   (GimpConfig       *config,
128                                                       guint             property_id,
129                                                       GValue           *value,
130                                                       GParamSpec       *pspec,
131                                                       GScanner         *scanner,
132                                                       GTokenType       *expected);
133 
134 
135 G_DEFINE_TYPE_WITH_CODE (GimpViewable, gimp_viewable, GIMP_TYPE_OBJECT,
136                          G_ADD_PRIVATE (GimpViewable)
137                          G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
138                                                 gimp_viewable_config_iface_init))
139 
140 #define parent_class gimp_viewable_parent_class
141 
142 static guint viewable_signals[LAST_SIGNAL] = { 0 };
143 
144 
145 static void
gimp_viewable_class_init(GimpViewableClass * klass)146 gimp_viewable_class_init (GimpViewableClass *klass)
147 {
148   GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
149   GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
150 
151   viewable_signals[INVALIDATE_PREVIEW] =
152     g_signal_new ("invalidate-preview",
153                   G_TYPE_FROM_CLASS (klass),
154                   G_SIGNAL_RUN_FIRST,
155                   G_STRUCT_OFFSET (GimpViewableClass, invalidate_preview),
156                   NULL, NULL,
157                   gimp_marshal_VOID__VOID,
158                   G_TYPE_NONE, 0);
159 
160   viewable_signals[SIZE_CHANGED] =
161     g_signal_new ("size-changed",
162                   G_TYPE_FROM_CLASS (klass),
163                   G_SIGNAL_RUN_FIRST,
164                   G_STRUCT_OFFSET (GimpViewableClass, size_changed),
165                   NULL, NULL,
166                   gimp_marshal_VOID__VOID,
167                   G_TYPE_NONE, 0);
168 
169   viewable_signals[EXPANDED_CHANGED] =
170     g_signal_new ("expanded-changed",
171                   G_TYPE_FROM_CLASS (klass),
172                   G_SIGNAL_RUN_FIRST,
173                   G_STRUCT_OFFSET (GimpViewableClass, expanded_changed),
174                   NULL, NULL,
175                   gimp_marshal_VOID__VOID,
176                   G_TYPE_NONE, 0);
177 
178   viewable_signals[ANCESTRY_CHANGED] =
179     g_signal_new ("ancestry-changed",
180                   G_TYPE_FROM_CLASS (klass),
181                   G_SIGNAL_RUN_FIRST,
182                   G_STRUCT_OFFSET (GimpViewableClass, ancestry_changed),
183                   NULL, NULL,
184                   gimp_marshal_VOID__VOID,
185                   G_TYPE_NONE, 0);
186 
187   object_class->finalize         = gimp_viewable_finalize;
188   object_class->get_property     = gimp_viewable_get_property;
189   object_class->set_property     = gimp_viewable_set_property;
190 
191   gimp_object_class->get_memsize = gimp_viewable_get_memsize;
192 
193   klass->default_icon_name       = "gimp-question";
194   klass->name_changed_signal     = "name-changed";
195   klass->name_editable           = FALSE;
196 
197   klass->invalidate_preview      = gimp_viewable_real_invalidate_preview;
198   klass->size_changed            = NULL;
199   klass->expanded_changed        = NULL;
200   klass->ancestry_changed        = gimp_viewable_real_ancestry_changed;
201 
202   klass->get_size                = NULL;
203   klass->get_preview_size        = gimp_viewable_real_get_preview_size;
204   klass->get_popup_size          = gimp_viewable_real_get_popup_size;
205   klass->get_preview             = NULL;
206   klass->get_new_preview         = NULL;
207   klass->get_pixbuf              = NULL;
208   klass->get_new_pixbuf          = gimp_viewable_real_get_new_pixbuf;
209   klass->get_description         = gimp_viewable_real_get_description;
210   klass->is_name_editable        = gimp_viewable_real_is_name_editable;
211   klass->preview_freeze          = NULL;
212   klass->preview_thaw            = NULL;
213   klass->get_children            = gimp_viewable_real_get_children;
214   klass->set_expanded            = NULL;
215   klass->get_expanded            = NULL;
216 
217   /* compat property */
218   GIMP_CONFIG_PROP_STRING (object_class, PROP_STOCK_ID, "stock-id",
219                            NULL, NULL,
220                            NULL,
221                            GIMP_PARAM_STATIC_STRINGS);
222 
223   GIMP_CONFIG_PROP_STRING (object_class, PROP_ICON_NAME, "icon-name",
224                            NULL, NULL,
225                            NULL,
226                            GIMP_PARAM_STATIC_STRINGS);
227 
228   GIMP_CONFIG_PROP_OBJECT (object_class, PROP_ICON_PIXBUF,
229                            "icon-pixbuf",
230                            NULL, NULL,
231                            GDK_TYPE_PIXBUF,
232                            G_PARAM_CONSTRUCT |
233                            GIMP_PARAM_STATIC_STRINGS);
234 
235   g_object_class_install_property (object_class, PROP_FROZEN,
236                                    g_param_spec_boolean ("frozen",
237                                                          NULL, NULL,
238                                                          FALSE,
239                                                          GIMP_PARAM_READABLE));
240 }
241 
242 static void
gimp_viewable_init(GimpViewable * viewable)243 gimp_viewable_init (GimpViewable *viewable)
244 {
245 }
246 
247 static void
gimp_viewable_config_iface_init(GimpConfigInterface * iface)248 gimp_viewable_config_iface_init (GimpConfigInterface *iface)
249 {
250   iface->deserialize_property = gimp_viewable_deserialize_property;
251   iface->serialize_property   = gimp_viewable_serialize_property;
252 }
253 
254 static void
gimp_viewable_finalize(GObject * object)255 gimp_viewable_finalize (GObject *object)
256 {
257   GimpViewablePrivate *private = GET_PRIVATE (object);
258 
259   g_clear_pointer (&private->icon_name, g_free);
260   g_clear_object (&private->icon_pixbuf);
261   g_clear_pointer (&private->preview_temp_buf, gimp_temp_buf_unref);
262   g_clear_object (&private->preview_pixbuf);
263 
264   G_OBJECT_CLASS (parent_class)->finalize (object);
265 }
266 
267 static void
gimp_viewable_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)268 gimp_viewable_set_property (GObject      *object,
269                             guint         property_id,
270                             const GValue *value,
271                             GParamSpec   *pspec)
272 {
273   GimpViewable        *viewable = GIMP_VIEWABLE (object);
274   GimpViewablePrivate *private  = GET_PRIVATE (object);
275 
276   switch (property_id)
277     {
278     case PROP_STOCK_ID:
279       if (! g_value_get_string (value))
280         break;
281     case PROP_ICON_NAME:
282       gimp_viewable_set_icon_name (viewable, g_value_get_string (value));
283       break;
284     case PROP_ICON_PIXBUF:
285       if (private->icon_pixbuf)
286         g_object_unref (private->icon_pixbuf);
287       private->icon_pixbuf = g_value_dup_object (value);
288       gimp_viewable_invalidate_preview (viewable);
289       break;
290     case PROP_FROZEN:
291       /* read-only, fall through */
292 
293     default:
294       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
295       break;
296     }
297 }
298 
299 static void
gimp_viewable_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)300 gimp_viewable_get_property (GObject    *object,
301                             guint       property_id,
302                             GValue     *value,
303                             GParamSpec *pspec)
304 {
305   GimpViewable        *viewable = GIMP_VIEWABLE (object);
306   GimpViewablePrivate *private  = GET_PRIVATE (object);
307 
308   switch (property_id)
309     {
310     case PROP_STOCK_ID:
311     case PROP_ICON_NAME:
312       g_value_set_string (value, gimp_viewable_get_icon_name (viewable));
313       break;
314     case PROP_ICON_PIXBUF:
315       g_value_set_object (value, private->icon_pixbuf);
316       break;
317     case PROP_FROZEN:
318       g_value_set_boolean (value, gimp_viewable_preview_is_frozen (viewable));
319       break;
320 
321     default:
322       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
323       break;
324     }
325 }
326 
327 static gint64
gimp_viewable_get_memsize(GimpObject * object,gint64 * gui_size)328 gimp_viewable_get_memsize (GimpObject *object,
329                            gint64     *gui_size)
330 {
331   GimpViewablePrivate *private = GET_PRIVATE (object);
332 
333   *gui_size += gimp_temp_buf_get_memsize (private->preview_temp_buf);
334 
335   if (private->preview_pixbuf)
336     {
337       *gui_size +=
338         (gimp_g_object_get_memsize (G_OBJECT (private->preview_pixbuf)) +
339          (gsize) gdk_pixbuf_get_height (private->preview_pixbuf) *
340          gdk_pixbuf_get_rowstride (private->preview_pixbuf));
341     }
342 
343   return GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size);
344 }
345 
346 static void
gimp_viewable_real_invalidate_preview(GimpViewable * viewable)347 gimp_viewable_real_invalidate_preview (GimpViewable *viewable)
348 {
349   GimpViewablePrivate *private = GET_PRIVATE (viewable);
350 
351   g_clear_pointer (&private->preview_temp_buf, gimp_temp_buf_unref);
352   g_clear_object (&private->preview_pixbuf);
353 }
354 
355 static void
gimp_viewable_real_ancestry_changed_propagate(GimpViewable * viewable,GimpViewable * parent)356 gimp_viewable_real_ancestry_changed_propagate (GimpViewable *viewable,
357                                                GimpViewable *parent)
358 {
359   GimpViewablePrivate *private = GET_PRIVATE (viewable);
360 
361   private->depth = gimp_viewable_get_depth (parent) + 1;
362 
363   g_signal_emit (viewable, viewable_signals[ANCESTRY_CHANGED], 0);
364 }
365 
366 static void
gimp_viewable_real_ancestry_changed(GimpViewable * viewable)367 gimp_viewable_real_ancestry_changed (GimpViewable *viewable)
368 {
369   GimpContainer *children;
370 
371   children = gimp_viewable_get_children (viewable);
372 
373   if (children)
374     {
375       gimp_container_foreach (children,
376                               (GFunc) gimp_viewable_real_ancestry_changed_propagate,
377                               viewable);
378     }
379 }
380 
381 static void
gimp_viewable_real_get_preview_size(GimpViewable * viewable,gint size,gboolean popup,gboolean dot_for_dot,gint * width,gint * height)382 gimp_viewable_real_get_preview_size (GimpViewable *viewable,
383                                      gint          size,
384                                      gboolean      popup,
385                                      gboolean      dot_for_dot,
386                                      gint         *width,
387                                      gint         *height)
388 {
389   *width  = size;
390   *height = size;
391 }
392 
393 static gboolean
gimp_viewable_real_get_popup_size(GimpViewable * viewable,gint width,gint height,gboolean dot_for_dot,gint * popup_width,gint * popup_height)394 gimp_viewable_real_get_popup_size (GimpViewable *viewable,
395                                    gint          width,
396                                    gint          height,
397                                    gboolean      dot_for_dot,
398                                    gint         *popup_width,
399                                    gint         *popup_height)
400 {
401   gint w, h;
402 
403   if (gimp_viewable_get_size (viewable, &w, &h))
404     {
405       if (w > width || h > height)
406         {
407           *popup_width  = w;
408           *popup_height = h;
409 
410           return TRUE;
411         }
412     }
413 
414   return FALSE;
415 }
416 
417 static GdkPixbuf *
gimp_viewable_real_get_new_pixbuf(GimpViewable * viewable,GimpContext * context,gint width,gint height)418 gimp_viewable_real_get_new_pixbuf (GimpViewable *viewable,
419                                    GimpContext  *context,
420                                    gint          width,
421                                    gint          height)
422 {
423   GimpViewablePrivate *private = GET_PRIVATE (viewable);
424   GdkPixbuf           *pixbuf  = NULL;
425   GimpTempBuf         *temp_buf;
426 
427   temp_buf = gimp_viewable_get_preview (viewable, context, width, height);
428 
429   if (temp_buf)
430     {
431       pixbuf = gimp_temp_buf_create_pixbuf (temp_buf);
432     }
433   else if (private->icon_pixbuf)
434     {
435       pixbuf = gdk_pixbuf_scale_simple (private->icon_pixbuf,
436                                         width,
437                                         height,
438                                         GDK_INTERP_BILINEAR);
439     }
440 
441   return pixbuf;
442 }
443 
444 static gchar *
gimp_viewable_real_get_description(GimpViewable * viewable,gchar ** tooltip)445 gimp_viewable_real_get_description (GimpViewable  *viewable,
446                                     gchar        **tooltip)
447 {
448   return g_strdup (gimp_object_get_name (viewable));
449 }
450 
451 static gboolean
gimp_viewable_real_is_name_editable(GimpViewable * viewable)452 gimp_viewable_real_is_name_editable (GimpViewable *viewable)
453 {
454   return GIMP_VIEWABLE_GET_CLASS (viewable)->name_editable;
455 }
456 
457 static GimpContainer *
gimp_viewable_real_get_children(GimpViewable * viewable)458 gimp_viewable_real_get_children (GimpViewable *viewable)
459 {
460   return NULL;
461 }
462 
463 static gboolean
gimp_viewable_serialize_property(GimpConfig * config,guint property_id,const GValue * value,GParamSpec * pspec,GimpConfigWriter * writer)464 gimp_viewable_serialize_property (GimpConfig       *config,
465                                   guint             property_id,
466                                   const GValue     *value,
467                                   GParamSpec       *pspec,
468                                   GimpConfigWriter *writer)
469 {
470   GimpViewablePrivate *private = GET_PRIVATE (config);
471 
472   switch (property_id)
473     {
474     case PROP_STOCK_ID:
475       return TRUE;
476 
477     case PROP_ICON_NAME:
478       if (private->icon_name)
479         {
480           gimp_config_writer_open (writer, pspec->name);
481           gimp_config_writer_string (writer, private->icon_name);
482           gimp_config_writer_close (writer);
483         }
484       return TRUE;
485 
486     case PROP_ICON_PIXBUF:
487       {
488         GdkPixbuf *icon_pixbuf = g_value_get_object (value);
489 
490         if (icon_pixbuf)
491           {
492             gchar  *pixbuffer;
493             gsize   pixbuffer_size;
494             GError *error = NULL;
495 
496             if (gdk_pixbuf_save_to_buffer (icon_pixbuf,
497                                            &pixbuffer,
498                                            &pixbuffer_size,
499                                            "png", &error, NULL))
500               {
501                 gchar *pixbuffer_enc;
502 
503                 pixbuffer_enc = g_base64_encode ((guchar *)pixbuffer,
504                                                  pixbuffer_size);
505                 gimp_config_writer_open (writer, "icon-pixbuf");
506                 gimp_config_writer_string (writer, pixbuffer_enc);
507                 gimp_config_writer_close (writer);
508 
509                 g_free (pixbuffer_enc);
510                 g_free (pixbuffer);
511               }
512           }
513       }
514       return TRUE;
515 
516     default:
517       break;
518     }
519 
520   return FALSE;
521 }
522 
523 static gboolean
gimp_viewable_deserialize_property(GimpConfig * config,guint property_id,GValue * value,GParamSpec * pspec,GScanner * scanner,GTokenType * expected)524 gimp_viewable_deserialize_property (GimpConfig *config,
525                                     guint       property_id,
526                                     GValue     *value,
527                                     GParamSpec *pspec,
528                                     GScanner   *scanner,
529                                     GTokenType *expected)
530 {
531   switch (property_id)
532     {
533     case PROP_ICON_PIXBUF:
534       {
535         GdkPixbuf *icon_pixbuf = NULL;
536         gchar     *encoded_image;
537 
538         if (! gimp_scanner_parse_string (scanner, &encoded_image))
539           {
540             *expected = G_TOKEN_STRING;
541             return TRUE;
542           }
543 
544         if (encoded_image && strlen (encoded_image) > 0)
545           {
546             gsize   out_len;
547             guchar *decoded_image = g_base64_decode (encoded_image, &out_len);
548 
549             if (decoded_image)
550               {
551                 GInputStream *stream;
552 
553                 stream = g_memory_input_stream_new_from_data (decoded_image,
554                                                               out_len, NULL);
555                 icon_pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
556                 g_object_unref (stream);
557 
558                 g_free (decoded_image);
559               }
560           }
561 
562         g_free (encoded_image);
563 
564         g_value_take_object (value, icon_pixbuf);
565       }
566       return TRUE;
567 
568     default:
569       break;
570     }
571 
572   return FALSE;
573 }
574 
575 /**
576  * gimp_viewable_invalidate_preview:
577  * @viewable: a viewable object
578  *
579  * Causes any cached preview to be marked as invalid, so that a new
580  * preview will be generated at the next attempt to display one.
581  **/
582 void
gimp_viewable_invalidate_preview(GimpViewable * viewable)583 gimp_viewable_invalidate_preview (GimpViewable *viewable)
584 {
585   GimpViewablePrivate *private;
586 
587   g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
588 
589   private = GET_PRIVATE (viewable);
590 
591   if (private->freeze_count == 0)
592     g_signal_emit (viewable, viewable_signals[INVALIDATE_PREVIEW], 0);
593   else
594     private->invalidate_pending = TRUE;
595 }
596 
597 /**
598  * gimp_viewable_size_changed:
599  * @viewable: a viewable object
600  *
601  * This function sends a signal that is handled at a lower level in the
602  * object hierarchy, and provides a mechanism by which objects derived
603  * from #GimpViewable can respond to size changes.
604  **/
605 void
gimp_viewable_size_changed(GimpViewable * viewable)606 gimp_viewable_size_changed (GimpViewable *viewable)
607 {
608   GimpViewablePrivate *private;
609 
610   g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
611 
612   private = GET_PRIVATE (viewable);
613 
614   if (private->freeze_count == 0)
615     g_signal_emit (viewable, viewable_signals[SIZE_CHANGED], 0);
616   else
617     private->size_changed_prending = TRUE;
618 }
619 
620 /**
621  * gimp_viewable_expanded_changed:
622  * @viewable: a viewable object
623  *
624  * This function sends a signal that is handled at a lower level in the
625  * object hierarchy, and provides a mechanism by which objects derived
626  * from #GimpViewable can respond to expanded state changes.
627  **/
628 void
gimp_viewable_expanded_changed(GimpViewable * viewable)629 gimp_viewable_expanded_changed (GimpViewable *viewable)
630 {
631   g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
632 
633   g_signal_emit (viewable, viewable_signals[EXPANDED_CHANGED], 0);
634 }
635 
636 /**
637  * gimp_viewable_calc_preview_size:
638  * @aspect_width:   unscaled width of the preview for an item.
639  * @aspect_height:  unscaled height of the preview for an item.
640  * @width:          maximum available width for scaled preview.
641  * @height:         maximum available height for scaled preview.
642  * @dot_for_dot:    if #TRUE, ignore any differences in axis resolution.
643  * @xresolution:    resolution in the horizontal direction.
644  * @yresolution:    resolution in the vertical direction.
645  * @return_width:   place to return the calculated preview width.
646  * @return_height:  place to return the calculated preview height.
647  * @scaling_up:     returns #TRUE here if the calculated preview size
648  *                  is larger than the viewable itself.
649  *
650  * A utility function, for calculating the dimensions of a preview
651  * based on the information specified in the arguments.  The arguments
652  * @aspect_width and @aspect_height are the dimensions of the unscaled
653  * preview.  The arguments @width and @height represent the maximum
654  * width and height that the scaled preview must fit into. The
655  * preview is scaled to be as large as possible without exceeding
656  * these constraints.
657  *
658  * If @dot_for_dot is #TRUE, and @xresolution and @yresolution are
659  * different, then these results are corrected for the difference in
660  * resolution on the two axes, so that the requested aspect ratio
661  * applies to the appearance of the display rather than to pixel
662  * counts.
663  **/
664 void
gimp_viewable_calc_preview_size(gint aspect_width,gint aspect_height,gint width,gint height,gboolean dot_for_dot,gdouble xresolution,gdouble yresolution,gint * return_width,gint * return_height,gboolean * scaling_up)665 gimp_viewable_calc_preview_size (gint       aspect_width,
666                                  gint       aspect_height,
667                                  gint       width,
668                                  gint       height,
669                                  gboolean   dot_for_dot,
670                                  gdouble    xresolution,
671                                  gdouble    yresolution,
672                                  gint      *return_width,
673                                  gint      *return_height,
674                                  gboolean  *scaling_up)
675 {
676   gdouble xratio;
677   gdouble yratio;
678 
679   if (aspect_width > aspect_height)
680     {
681       xratio = yratio = (gdouble) width / (gdouble) aspect_width;
682     }
683   else
684     {
685       xratio = yratio = (gdouble) height / (gdouble) aspect_height;
686     }
687 
688   if (! dot_for_dot && xresolution != yresolution)
689     {
690       yratio *= xresolution / yresolution;
691     }
692 
693   width  = RINT (xratio * (gdouble) aspect_width);
694   height = RINT (yratio * (gdouble) aspect_height);
695 
696   if (width  < 1) width  = 1;
697   if (height < 1) height = 1;
698 
699   if (return_width)  *return_width  = width;
700   if (return_height) *return_height = height;
701   if (scaling_up)    *scaling_up    = (xratio > 1.0) || (yratio > 1.0);
702 }
703 
704 gboolean
gimp_viewable_get_size(GimpViewable * viewable,gint * width,gint * height)705 gimp_viewable_get_size (GimpViewable  *viewable,
706                         gint          *width,
707                         gint          *height)
708 {
709   GimpViewableClass *viewable_class;
710   gboolean           retval = FALSE;
711   gint               w      = 0;
712   gint               h      = 0;
713 
714   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE);
715 
716   viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);
717 
718   if (viewable_class->get_size)
719     retval = viewable_class->get_size (viewable, &w, &h);
720 
721   if (width)  *width  = w;
722   if (height) *height = h;
723 
724   return retval;
725 }
726 
727 /**
728  * gimp_viewable_get_preview_size:
729  * @viewable:    the object for which to calculate the preview size.
730  * @size:        requested size for preview.
731  * @popup:       %TRUE if the preview is intended for a popup window.
732  * @dot_for_dot: If #TRUE, ignore any differences in X and Y resolution.
733  * @width:       return location for the the calculated width.
734  * @height:      return location for the calculated height.
735  *
736  * Retrieve the size of a viewable's preview.  By default, this
737  * simply returns the value of the @size argument for both the @width
738  * and @height, but this can be overridden in objects derived from
739  * #GimpViewable.  If either the width or height exceeds
740  * #GIMP_VIEWABLE_MAX_PREVIEW_SIZE, they are silently truncated.
741  **/
742 void
gimp_viewable_get_preview_size(GimpViewable * viewable,gint size,gboolean popup,gboolean dot_for_dot,gint * width,gint * height)743 gimp_viewable_get_preview_size (GimpViewable *viewable,
744                                 gint          size,
745                                 gboolean      popup,
746                                 gboolean      dot_for_dot,
747                                 gint         *width,
748                                 gint         *height)
749 {
750   gint w, h;
751 
752   g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
753   g_return_if_fail (size > 0);
754 
755   GIMP_VIEWABLE_GET_CLASS (viewable)->get_preview_size (viewable, size,
756                                                         popup, dot_for_dot,
757                                                         &w, &h);
758 
759   w = MIN (w, GIMP_VIEWABLE_MAX_PREVIEW_SIZE);
760   h = MIN (h, GIMP_VIEWABLE_MAX_PREVIEW_SIZE);
761 
762   if (width)  *width  = w;
763   if (height) *height = h;
764 
765 }
766 
767 /**
768  * gimp_viewable_get_popup_size:
769  * @viewable:     the object for which to calculate the popup size.
770  * @width:        the width of the preview from which the popup will be shown.
771  * @height:       the height of the preview from which the popup will be shown.
772  * @dot_for_dot:  If #TRUE, ignore any differences in X and Y resolution.
773  * @popup_width:  return location for the calculated popup width.
774  * @popup_height: return location for the calculated popup height.
775  *
776  * Calculate the size of a viewable's preview, for use in making a
777  * popup. The arguments @width and @height specify the size of the
778  * preview from which the popup will be shown.
779  *
780  * Returns: Whether the viewable wants a popup to be shown. Usually
781  *          %TRUE if the passed preview size is smaller than the viewable
782  *          size, and %FALSE if the viewable completely fits into the
783  *          original preview.
784  **/
785 gboolean
gimp_viewable_get_popup_size(GimpViewable * viewable,gint width,gint height,gboolean dot_for_dot,gint * popup_width,gint * popup_height)786 gimp_viewable_get_popup_size (GimpViewable *viewable,
787                               gint          width,
788                               gint          height,
789                               gboolean      dot_for_dot,
790                               gint         *popup_width,
791                               gint         *popup_height)
792 {
793   gint w, h;
794 
795   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE);
796 
797   if (GIMP_VIEWABLE_GET_CLASS (viewable)->get_popup_size (viewable,
798                                                           width, height,
799                                                           dot_for_dot,
800                                                           &w, &h))
801     {
802       if (w < 1) w = 1;
803       if (h < 1) h = 1;
804 
805       /*  limit the popup to 2 * GIMP_VIEWABLE_MAX_POPUP_SIZE
806        *  on each axis.
807        */
808       if ((w > (2 * GIMP_VIEWABLE_MAX_POPUP_SIZE)) ||
809           (h > (2 * GIMP_VIEWABLE_MAX_POPUP_SIZE)))
810         {
811           gimp_viewable_calc_preview_size (w, h,
812                                            2 * GIMP_VIEWABLE_MAX_POPUP_SIZE,
813                                            2 * GIMP_VIEWABLE_MAX_POPUP_SIZE,
814                                            dot_for_dot, 1.0, 1.0,
815                                            &w, &h, NULL);
816         }
817 
818       /*  limit the number of pixels to
819        *  GIMP_VIEWABLE_MAX_POPUP_SIZE ^ 2
820        */
821       if ((w * h) > SQR (GIMP_VIEWABLE_MAX_POPUP_SIZE))
822         {
823           gdouble factor;
824 
825           factor = sqrt (((gdouble) (w * h) /
826                           (gdouble) SQR (GIMP_VIEWABLE_MAX_POPUP_SIZE)));
827 
828           w = RINT ((gdouble) w / factor);
829           h = RINT ((gdouble) h / factor);
830         }
831 
832       if (w < 1) w = 1;
833       if (h < 1) h = 1;
834 
835       if (popup_width)  *popup_width  = w;
836       if (popup_height) *popup_height = h;
837 
838       return TRUE;
839     }
840 
841   return FALSE;
842 }
843 
844 /**
845  * gimp_viewable_get_preview:
846  * @viewable: The viewable object to get a preview for.
847  * @context:  The context to render the preview for.
848  * @width:    desired width for the preview
849  * @height:   desired height for the preview
850  *
851  * Gets a preview for a viewable object, by running through a variety
852  * of methods until it finds one that works.  First, if an
853  * implementation exists of a "get_preview" method, it is tried, and
854  * the result is returned if it is not #NULL.  Second, the function
855  * checks to see whether there is a cached preview with the correct
856  * dimensions; if so, it is returned.  If neither of these works, then
857  * the function looks for an implementation of the "get_new_preview"
858  * method, and executes it, caching the result.  If everything fails,
859  * #NULL is returned.
860  *
861  * Returns: A #GimpTempBuf containing the preview image, or #NULL if
862  *          none can be found or created.
863  **/
864 GimpTempBuf *
gimp_viewable_get_preview(GimpViewable * viewable,GimpContext * context,gint width,gint height)865 gimp_viewable_get_preview (GimpViewable *viewable,
866                            GimpContext  *context,
867                            gint          width,
868                            gint          height)
869 {
870   GimpViewablePrivate *private;
871   GimpViewableClass   *viewable_class;
872   GimpTempBuf         *temp_buf = NULL;
873 
874   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
875   g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
876   g_return_val_if_fail (width  > 0, NULL);
877   g_return_val_if_fail (height > 0, NULL);
878 
879   private = GET_PRIVATE (viewable);
880 
881   if (G_UNLIKELY (context == NULL))
882     g_warning ("%s: context is NULL", G_STRFUNC);
883 
884   viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);
885 
886   if (viewable_class->get_preview)
887     temp_buf = viewable_class->get_preview (viewable, context, width, height);
888 
889   if (temp_buf)
890     return temp_buf;
891 
892   if (private->preview_temp_buf)
893     {
894       if (gimp_temp_buf_get_width  (private->preview_temp_buf) == width &&
895           gimp_temp_buf_get_height (private->preview_temp_buf) == height)
896         {
897           return private->preview_temp_buf;
898         }
899 
900       g_clear_pointer (&private->preview_temp_buf, gimp_temp_buf_unref);
901     }
902 
903   if (viewable_class->get_new_preview)
904     temp_buf = viewable_class->get_new_preview (viewable, context,
905                                                 width, height);
906 
907   private->preview_temp_buf = temp_buf;
908 
909   return temp_buf;
910 }
911 
912 /**
913  * gimp_viewable_get_new_preview:
914  * @viewable: The viewable object to get a preview for.
915  * @width:    desired width for the preview
916  * @height:   desired height for the preview
917  *
918  * Gets a new preview for a viewable object.  Similar to
919  * gimp_viewable_get_preview(), except that it tries things in a
920  * different order, first looking for a "get_new_preview" method, and
921  * then if that fails for a "get_preview" method.  This function does
922  * not look for a cached preview.
923  *
924  * Returns: A #GimpTempBuf containing the preview image, or #NULL if
925  *          none can be found or created.
926  **/
927 GimpTempBuf *
gimp_viewable_get_new_preview(GimpViewable * viewable,GimpContext * context,gint width,gint height)928 gimp_viewable_get_new_preview (GimpViewable *viewable,
929                                GimpContext  *context,
930                                gint          width,
931                                gint          height)
932 {
933   GimpViewableClass *viewable_class;
934   GimpTempBuf       *temp_buf = NULL;
935 
936   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
937   g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
938   g_return_val_if_fail (width  > 0, NULL);
939   g_return_val_if_fail (height > 0, NULL);
940 
941   if (G_UNLIKELY (context == NULL))
942     g_warning ("%s: context is NULL", G_STRFUNC);
943 
944   viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);
945 
946   if (viewable_class->get_new_preview)
947     temp_buf = viewable_class->get_new_preview (viewable, context,
948                                                 width, height);
949 
950   if (temp_buf)
951     return temp_buf;
952 
953   if (viewable_class->get_preview)
954     temp_buf = viewable_class->get_preview (viewable, context,
955                                             width, height);
956 
957   if (temp_buf)
958     return gimp_temp_buf_copy (temp_buf);
959 
960   return NULL;
961 }
962 
963 /**
964  * gimp_viewable_get_dummy_preview:
965  * @viewable: viewable object for which to get a dummy preview.
966  * @width:    width of the preview.
967  * @height:   height of the preview.
968  * @bpp:      bytes per pixel for the preview, must be 3 or 4.
969  *
970  * Creates a dummy preview the fits into the specified dimensions,
971  * containing a default "question" symbol.  This function is used to
972  * generate a preview in situations where layer previews have been
973  * disabled in the current Gimp configuration.
974  *
975  * Returns: a #GimpTempBuf containing the preview image.
976  **/
977 GimpTempBuf *
gimp_viewable_get_dummy_preview(GimpViewable * viewable,gint width,gint height,const Babl * format)978 gimp_viewable_get_dummy_preview (GimpViewable *viewable,
979                                  gint          width,
980                                  gint          height,
981                                  const Babl   *format)
982 {
983   GdkPixbuf   *pixbuf;
984   GimpTempBuf *buf;
985 
986   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
987   g_return_val_if_fail (width  > 0, NULL);
988   g_return_val_if_fail (height > 0, NULL);
989   g_return_val_if_fail (format != NULL, NULL);
990 
991   pixbuf = gimp_viewable_get_dummy_pixbuf (viewable, width, height,
992                                            babl_format_has_alpha (format));
993 
994   buf = gimp_temp_buf_new_from_pixbuf (pixbuf, format);
995 
996   g_object_unref (pixbuf);
997 
998   return buf;
999 }
1000 
1001 /**
1002  * gimp_viewable_get_pixbuf:
1003  * @viewable: The viewable object to get a pixbuf preview for.
1004  * @context:  The context to render the preview for.
1005  * @width:    desired width for the preview
1006  * @height:   desired height for the preview
1007  *
1008  * Gets a preview for a viewable object, by running through a variety
1009  * of methods until it finds one that works.  First, if an
1010  * implementation exists of a "get_pixbuf" method, it is tried, and
1011  * the result is returned if it is not #NULL.  Second, the function
1012  * checks to see whether there is a cached preview with the correct
1013  * dimensions; if so, it is returned.  If neither of these works, then
1014  * the function looks for an implementation of the "get_new_pixbuf"
1015  * method, and executes it, caching the result.  If everything fails,
1016  * #NULL is returned.
1017  *
1018  * Returns: A #GdkPixbuf containing the preview pixbuf, or #NULL if none can
1019  *          be found or created.
1020  **/
1021 GdkPixbuf *
gimp_viewable_get_pixbuf(GimpViewable * viewable,GimpContext * context,gint width,gint height)1022 gimp_viewable_get_pixbuf (GimpViewable *viewable,
1023                           GimpContext  *context,
1024                           gint          width,
1025                           gint          height)
1026 {
1027   GimpViewablePrivate *private;
1028   GimpViewableClass   *viewable_class;
1029   GdkPixbuf           *pixbuf = NULL;
1030 
1031   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
1032   g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
1033   g_return_val_if_fail (width  > 0, NULL);
1034   g_return_val_if_fail (height > 0, NULL);
1035 
1036   private = GET_PRIVATE (viewable);
1037 
1038   if (G_UNLIKELY (context == NULL))
1039     g_warning ("%s: context is NULL", G_STRFUNC);
1040 
1041   viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);
1042 
1043   if (viewable_class->get_pixbuf)
1044     pixbuf = viewable_class->get_pixbuf (viewable, context, width, height);
1045 
1046   if (pixbuf)
1047     return pixbuf;
1048 
1049   if (private->preview_pixbuf)
1050     {
1051       if (gdk_pixbuf_get_width  (private->preview_pixbuf) == width &&
1052           gdk_pixbuf_get_height (private->preview_pixbuf) == height)
1053         {
1054           return private->preview_pixbuf;
1055         }
1056 
1057       g_clear_object (&private->preview_pixbuf);
1058     }
1059 
1060   if (viewable_class->get_new_pixbuf)
1061     pixbuf = viewable_class->get_new_pixbuf (viewable, context, width, height);
1062 
1063   private->preview_pixbuf = pixbuf;
1064 
1065   return pixbuf;
1066 }
1067 
1068 /**
1069  * gimp_viewable_get_new_pixbuf:
1070  * @viewable: The viewable object to get a new pixbuf preview for.
1071  * @context:  The context to render the preview for.
1072  * @width:    desired width for the pixbuf
1073  * @height:   desired height for the pixbuf
1074  *
1075  * Gets a new preview for a viewable object.  Similar to
1076  * gimp_viewable_get_pixbuf(), except that it tries things in a
1077  * different order, first looking for a "get_new_pixbuf" method, and
1078  * then if that fails for a "get_pixbuf" method.  This function does
1079  * not look for a cached pixbuf.
1080  *
1081  * Returns: A #GdkPixbuf containing the preview, or #NULL if none can
1082  *          be created.
1083  **/
1084 GdkPixbuf *
gimp_viewable_get_new_pixbuf(GimpViewable * viewable,GimpContext * context,gint width,gint height)1085 gimp_viewable_get_new_pixbuf (GimpViewable *viewable,
1086                               GimpContext  *context,
1087                               gint          width,
1088                               gint          height)
1089 {
1090   GimpViewableClass *viewable_class;
1091   GdkPixbuf         *pixbuf = NULL;
1092 
1093   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
1094   g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
1095   g_return_val_if_fail (width  > 0, NULL);
1096   g_return_val_if_fail (height > 0, NULL);
1097 
1098   if (G_UNLIKELY (context == NULL))
1099     g_warning ("%s: context is NULL", G_STRFUNC);
1100 
1101   viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);
1102 
1103   if (viewable_class->get_new_pixbuf)
1104     pixbuf = viewable_class->get_new_pixbuf (viewable, context, width, height);
1105 
1106   if (pixbuf)
1107     return pixbuf;
1108 
1109   if (viewable_class->get_pixbuf)
1110     pixbuf = viewable_class->get_pixbuf (viewable, context, width, height);
1111 
1112   if (pixbuf)
1113     return gdk_pixbuf_copy (pixbuf);
1114 
1115   return NULL;
1116 }
1117 
1118 /**
1119  * gimp_viewable_get_dummy_pixbuf:
1120  * @viewable: the viewable object for which to create a dummy representation.
1121  * @width:    maximum permitted width for the pixbuf.
1122  * @height:   maximum permitted height for the pixbuf.
1123  * @bpp:      bytes per pixel for the pixbuf, must equal 3 or 4.
1124  *
1125  * Creates a pixbuf containing a default "question" symbol, sized to
1126  * fit into the specified dimensions.  The depth of the pixbuf must be
1127  * 3 or 4 because #GdkPixbuf does not support grayscale.  This
1128  * function is used to generate a preview in situations where
1129  * previewing has been disabled in the current Gimp configuration.
1130  * [Note: this function is currently unused except internally to
1131  * #GimpViewable -- consider making it static?]
1132  *
1133  * Returns: the created #GdkPixbuf.
1134  **/
1135 GdkPixbuf *
gimp_viewable_get_dummy_pixbuf(GimpViewable * viewable,gint width,gint height,gboolean with_alpha)1136 gimp_viewable_get_dummy_pixbuf (GimpViewable  *viewable,
1137                                 gint           width,
1138                                 gint           height,
1139                                 gboolean       with_alpha)
1140 {
1141   GdkPixbuf *icon;
1142   GdkPixbuf *pixbuf;
1143   GError    *error = NULL;
1144   gdouble    ratio;
1145   gint       w, h;
1146 
1147   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
1148   g_return_val_if_fail (width  > 0, NULL);
1149   g_return_val_if_fail (height > 0, NULL);
1150 
1151   icon = gdk_pixbuf_new_from_resource ("/org/gimp/icons/64/gimp-question.png",
1152                                        &error);
1153   if (! icon)
1154     {
1155       g_critical ("Failed to create icon image: %s", error->message);
1156       g_clear_error (&error);
1157       return NULL;
1158     }
1159 
1160   w = gdk_pixbuf_get_width (icon);
1161   h = gdk_pixbuf_get_height (icon);
1162 
1163   ratio = (gdouble) MIN (width, height) / (gdouble) MAX (w, h);
1164   ratio = MIN (ratio, 1.0);
1165 
1166   w = RINT (ratio * (gdouble) w);
1167   h = RINT (ratio * (gdouble) h);
1168 
1169   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, with_alpha, 8, width, height);
1170   gdk_pixbuf_fill (pixbuf, 0xffffffff);
1171 
1172   if (w && h)
1173     gdk_pixbuf_composite (icon, pixbuf,
1174                           (width - w) / 2, (height - h) / 2, w, h,
1175                           (width - w) / 2, (height - h) / 2, ratio, ratio,
1176                           GDK_INTERP_BILINEAR, 0xFF);
1177 
1178   g_object_unref (icon);
1179 
1180   return pixbuf;
1181 }
1182 
1183 /**
1184  * gimp_viewable_get_description:
1185  * @viewable: viewable object for which to retrieve a description.
1186  * @tooltip:  return location for an optional tooltip string.
1187  *
1188  * Retrieves a string containing a description of the viewable object,
1189  * By default, it simply returns the name of the object, but this can
1190  * be overridden by object types that inherit from #GimpViewable.
1191  *
1192  * Returns: a copy of the description string.  This should be freed
1193  *          when it is no longer needed.
1194  **/
1195 gchar *
gimp_viewable_get_description(GimpViewable * viewable,gchar ** tooltip)1196 gimp_viewable_get_description (GimpViewable  *viewable,
1197                                gchar        **tooltip)
1198 {
1199   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
1200 
1201   if (tooltip)
1202     *tooltip = NULL;
1203 
1204   return GIMP_VIEWABLE_GET_CLASS (viewable)->get_description (viewable,
1205                                                               tooltip);
1206 }
1207 
1208 /**
1209  * gimp_viewable_is_name_editable:
1210  * @viewable: viewable object for which to retrieve a description.
1211  *
1212  * Returns: whether the viewable's name is editable by the user.
1213  **/
1214 gboolean
gimp_viewable_is_name_editable(GimpViewable * viewable)1215 gimp_viewable_is_name_editable (GimpViewable *viewable)
1216 {
1217   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE);
1218 
1219   return GIMP_VIEWABLE_GET_CLASS (viewable)->is_name_editable (viewable);
1220 }
1221 
1222 /**
1223  * gimp_viewable_get_icon_name:
1224  * @viewable: viewable object for which to retrieve a icon name.
1225  *
1226  * Gets the current value of the object's icon name, for use in
1227  * constructing an iconic representation of the object.
1228  *
1229  * Returns: a pointer to the string containing the icon name.  The
1230  *          contents must not be altered or freed.
1231  **/
1232 const gchar *
gimp_viewable_get_icon_name(GimpViewable * viewable)1233 gimp_viewable_get_icon_name (GimpViewable *viewable)
1234 {
1235   GimpViewablePrivate *private;
1236 
1237   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
1238 
1239   private = GET_PRIVATE (viewable);
1240 
1241   if (private->icon_name)
1242     return (const gchar *) private->icon_name;
1243 
1244   return GIMP_VIEWABLE_GET_CLASS (viewable)->default_icon_name;
1245 }
1246 
1247 /**
1248  * gimp_viewable_set_icon_name:
1249  * @viewable: viewable object to assign the specified icon name.
1250  * @icon_name: string containing an icon name identifier.
1251  *
1252  * Seta the object's icon name, for use in constructing iconic smbols
1253  * of the object.  The contents of @icon_name are copied, so you can
1254  * free it when you are done with it.
1255  **/
1256 void
gimp_viewable_set_icon_name(GimpViewable * viewable,const gchar * icon_name)1257 gimp_viewable_set_icon_name (GimpViewable *viewable,
1258                              const gchar  *icon_name)
1259 {
1260   GimpViewablePrivate *private;
1261   GimpViewableClass   *viewable_class;
1262 
1263   g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
1264 
1265   private = GET_PRIVATE (viewable);
1266 
1267   g_clear_pointer (&private->icon_name, g_free);
1268 
1269   viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);
1270 
1271   if (icon_name)
1272     {
1273       if (viewable_class->default_icon_name == NULL ||
1274           strcmp (icon_name, viewable_class->default_icon_name))
1275         private->icon_name = g_strdup (icon_name);
1276     }
1277 
1278   gimp_viewable_invalidate_preview (viewable);
1279 
1280   g_object_notify (G_OBJECT (viewable), "icon-name");
1281 }
1282 
1283 void
gimp_viewable_preview_freeze(GimpViewable * viewable)1284 gimp_viewable_preview_freeze (GimpViewable *viewable)
1285 {
1286   GimpViewablePrivate *private;
1287 
1288   g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
1289 
1290   private = GET_PRIVATE (viewable);
1291 
1292   private->freeze_count++;
1293 
1294   if (private->freeze_count == 1)
1295     {
1296       if (GIMP_VIEWABLE_GET_CLASS (viewable)->preview_freeze)
1297         GIMP_VIEWABLE_GET_CLASS (viewable)->preview_freeze (viewable);
1298 
1299       g_object_notify (G_OBJECT (viewable), "frozen");
1300     }
1301 }
1302 
1303 void
gimp_viewable_preview_thaw(GimpViewable * viewable)1304 gimp_viewable_preview_thaw (GimpViewable *viewable)
1305 {
1306   GimpViewablePrivate *private;
1307 
1308   g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
1309 
1310   private = GET_PRIVATE (viewable);
1311 
1312   g_return_if_fail (private->freeze_count > 0);
1313 
1314   private->freeze_count--;
1315 
1316   if (private->freeze_count == 0)
1317     {
1318       if (private->size_changed_prending)
1319         {
1320           private->size_changed_prending = FALSE;
1321 
1322           gimp_viewable_size_changed (viewable);
1323         }
1324 
1325       if (private->invalidate_pending)
1326         {
1327           private->invalidate_pending = FALSE;
1328 
1329           gimp_viewable_invalidate_preview (viewable);
1330         }
1331 
1332       g_object_notify (G_OBJECT (viewable), "frozen");
1333 
1334       if (GIMP_VIEWABLE_GET_CLASS (viewable)->preview_thaw)
1335         GIMP_VIEWABLE_GET_CLASS (viewable)->preview_thaw (viewable);
1336     }
1337 }
1338 
1339 gboolean
gimp_viewable_preview_is_frozen(GimpViewable * viewable)1340 gimp_viewable_preview_is_frozen (GimpViewable *viewable)
1341 {
1342   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE);
1343 
1344   return GET_PRIVATE (viewable)->freeze_count != 0;
1345 }
1346 
1347 GimpViewable *
gimp_viewable_get_parent(GimpViewable * viewable)1348 gimp_viewable_get_parent (GimpViewable *viewable)
1349 {
1350   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
1351 
1352   return GET_PRIVATE (viewable)->parent;
1353 }
1354 
1355 void
gimp_viewable_set_parent(GimpViewable * viewable,GimpViewable * parent)1356 gimp_viewable_set_parent (GimpViewable *viewable,
1357                           GimpViewable *parent)
1358 {
1359   GimpViewablePrivate *private;
1360 
1361   g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
1362   g_return_if_fail (parent == NULL || GIMP_IS_VIEWABLE (parent));
1363 
1364   private = GET_PRIVATE (viewable);
1365 
1366   if (parent != private->parent)
1367     {
1368       private->parent = parent;
1369       private->depth  = parent ? gimp_viewable_get_depth (parent) + 1 : 0;
1370 
1371       g_signal_emit (viewable, viewable_signals[ANCESTRY_CHANGED], 0);
1372     }
1373 }
1374 
1375 gint
gimp_viewable_get_depth(GimpViewable * viewable)1376 gimp_viewable_get_depth (GimpViewable *viewable)
1377 {
1378   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), 0);
1379 
1380   return GET_PRIVATE (viewable)->depth;
1381 }
1382 
1383 GimpContainer *
gimp_viewable_get_children(GimpViewable * viewable)1384 gimp_viewable_get_children (GimpViewable *viewable)
1385 {
1386   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
1387 
1388   return GIMP_VIEWABLE_GET_CLASS (viewable)->get_children (viewable);
1389 }
1390 
1391 gboolean
gimp_viewable_get_expanded(GimpViewable * viewable)1392 gimp_viewable_get_expanded (GimpViewable *viewable)
1393 {
1394   g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE);
1395 
1396   if (GIMP_VIEWABLE_GET_CLASS (viewable)->get_expanded)
1397     return GIMP_VIEWABLE_GET_CLASS (viewable)->get_expanded (viewable);
1398 
1399   return FALSE;
1400 }
1401 
1402 void
gimp_viewable_set_expanded(GimpViewable * viewable,gboolean expanded)1403 gimp_viewable_set_expanded (GimpViewable *viewable,
1404                             gboolean       expanded)
1405 {
1406   g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
1407 
1408   if (GIMP_VIEWABLE_GET_CLASS (viewable)->set_expanded)
1409     GIMP_VIEWABLE_GET_CLASS (viewable)->set_expanded (viewable, expanded);
1410 }
1411 
1412 gboolean
gimp_viewable_is_ancestor(GimpViewable * ancestor,GimpViewable * descendant)1413 gimp_viewable_is_ancestor (GimpViewable *ancestor,
1414                            GimpViewable *descendant)
1415 {
1416   g_return_val_if_fail (GIMP_IS_VIEWABLE (ancestor), FALSE);
1417   g_return_val_if_fail (GIMP_IS_VIEWABLE (descendant), FALSE);
1418 
1419   while (descendant)
1420     {
1421       GimpViewable *parent = gimp_viewable_get_parent (descendant);
1422 
1423       if (parent == ancestor)
1424         return TRUE;
1425 
1426       descendant = parent;
1427     }
1428 
1429   return FALSE;
1430 }
1431