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