gimpviewable.c 37.8 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
 *
4
 * gimpviewable.c
Michael Natterer's avatar
Michael Natterer committed
5 6
 * Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10 11 12 13 14 15 16 17
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 20 21 22
 */

#include "config.h"

23 24
#include <string.h>

25
#include <gegl.h>
26

27
#include "libgimpmath/gimpmath.h"
28 29
#include "libgimpconfig/gimpconfig.h"

Michael Natterer's avatar
Michael Natterer committed
30
#include "core-types.h"
31

32 33
#include "gegl/gimp-gegl-utils.h"

34
#include "gimp-utils.h"
35
#include "gimpcontext.h"
36
#include "gimpmarshal.h"
37
#include "gimptempbuf.h"
38 39
#include "gimpviewable.h"

40 41
#include "themes/Default/images/gimp-core-pixbufs.h"

42

43 44 45
enum
{
  PROP_0,
46 47
  PROP_STOCK_ID,
  PROP_FROZEN
48 49
};

50 51 52
enum
{
  INVALIDATE_PREVIEW,
53
  SIZE_CHANGED,
54 55 56 57
  LAST_SIGNAL
};


58 59 60 61 62 63 64
typedef struct _GimpViewablePrivate GimpViewablePrivate;

struct _GimpViewablePrivate
{
  gchar        *stock_id;
  gint          freeze_count;
  GimpViewable *parent;
65

66
  GimpTempBuf  *preview_temp_buf;
67
  GdkPixbuf    *preview_pixbuf;
68 69 70
};

#define GET_PRIVATE(viewable) G_TYPE_INSTANCE_GET_PRIVATE (viewable, \
Michael Natterer's avatar
Michael Natterer committed
71 72
                                                           GIMP_TYPE_VIEWABLE, \
                                                           GimpViewablePrivate)
73 74


75
static void    gimp_viewable_config_iface_init (GimpConfigInterface *iface);
76

77 78 79 80 81 82 83 84 85
static void    gimp_viewable_finalize               (GObject        *object);
static void    gimp_viewable_set_property           (GObject        *object,
                                                     guint           property_id,
                                                     const GValue   *value,
                                                     GParamSpec     *pspec);
static void    gimp_viewable_get_property           (GObject        *object,
                                                     guint           property_id,
                                                     GValue         *value,
                                                     GParamSpec     *pspec);
86

87 88
static gint64  gimp_viewable_get_memsize             (GimpObject    *object,
                                                      gint64        *gui_size);
89

90
static void    gimp_viewable_real_invalidate_preview (GimpViewable  *viewable);
91

92
static GdkPixbuf * gimp_viewable_real_get_new_pixbuf (GimpViewable  *viewable,
93
                                                      GimpContext   *context,
94 95
                                                      gint           width,
                                                      gint           height);
96 97 98 99 100 101
static void    gimp_viewable_real_get_preview_size   (GimpViewable  *viewable,
                                                      gint           size,
                                                      gboolean       popup,
                                                      gboolean       dot_for_dot,
                                                      gint          *width,
                                                      gint          *height);
102 103 104 105 106 107
static gboolean gimp_viewable_real_get_popup_size    (GimpViewable  *viewable,
                                                      gint           width,
                                                      gint           height,
                                                      gboolean       dot_for_dot,
                                                      gint          *popup_width,
                                                      gint          *popup_height);
108 109
static gchar * gimp_viewable_real_get_description    (GimpViewable  *viewable,
                                                      gchar        **tooltip);
110 111
static GimpContainer * gimp_viewable_real_get_children (GimpViewable *viewable);

112
static gboolean gimp_viewable_serialize_property     (GimpConfig    *config,
113 114 115 116
                                                      guint          property_id,
                                                      const GValue  *value,
                                                      GParamSpec    *pspec,
                                                      GimpConfigWriter *writer);
117

118

119 120
G_DEFINE_TYPE_WITH_CODE (GimpViewable, gimp_viewable, GIMP_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
121
                                                gimp_viewable_config_iface_init))
122

123
#define parent_class gimp_viewable_parent_class
124

125
static guint viewable_signals[LAST_SIGNAL] = { 0 };
126 127 128 129 130


static void
gimp_viewable_class_init (GimpViewableClass *klass)
{
131 132
  GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
  GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
133

134
  viewable_signals[INVALIDATE_PREVIEW] =
135
    g_signal_new ("invalidate-preview",
136 137 138 139 140 141
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpViewableClass, invalidate_preview),
                  NULL, NULL,
                  gimp_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
142

143
  viewable_signals[SIZE_CHANGED] =
144
    g_signal_new ("size-changed",
145 146 147 148 149 150
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpViewableClass, size_changed),
                  NULL, NULL,
                  gimp_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
151

152
  object_class->finalize         = gimp_viewable_finalize;
153 154
  object_class->get_property     = gimp_viewable_get_property;
  object_class->set_property     = gimp_viewable_set_property;
155

156
  gimp_object_class->get_memsize = gimp_viewable_get_memsize;
157

158
  klass->default_stock_id        = "gimp-question";
159
  klass->name_changed_signal     = "name-changed";
160

161 162 163
  klass->invalidate_preview      = gimp_viewable_real_invalidate_preview;
  klass->size_changed            = NULL;

164
  klass->get_size                = NULL;
165
  klass->get_preview_size        = gimp_viewable_real_get_preview_size;
166
  klass->get_popup_size          = gimp_viewable_real_get_popup_size;
167 168
  klass->get_preview             = NULL;
  klass->get_new_preview         = NULL;
169 170
  klass->get_pixbuf              = NULL;
  klass->get_new_pixbuf          = gimp_viewable_real_get_new_pixbuf;
171
  klass->get_description         = gimp_viewable_real_get_description;
172
  klass->get_children            = gimp_viewable_real_get_children;
173 174
  klass->set_expanded            = NULL;
  klass->get_expanded            = NULL;
175 176

  GIMP_CONFIG_INSTALL_PROP_STRING (object_class, PROP_STOCK_ID, "stock-id",
177 178
                                   NULL, NULL,
                                   GIMP_PARAM_STATIC_STRINGS);
179 180 181 182 183 184

  g_object_class_install_property (object_class, PROP_FROZEN,
                                   g_param_spec_boolean ("frozen",
                                                         NULL, NULL,
                                                         FALSE,
                                                         GIMP_PARAM_READABLE));
185 186

  g_type_class_add_private (klass, sizeof (GimpViewablePrivate));
187 188 189 190 191
}

static void
gimp_viewable_init (GimpViewable *viewable)
{
192 193
}

194
static void
195
gimp_viewable_config_iface_init (GimpConfigInterface *iface)
196
{
197
  iface->serialize_property = gimp_viewable_serialize_property;
198 199
}

200 201 202
static void
gimp_viewable_finalize (GObject *object)
{
203
  GimpViewablePrivate *private = GET_PRIVATE (object);
204

205
  if (private->stock_id)
206
    {
207 208
      g_free (private->stock_id);
      private->stock_id = NULL;
209 210
    }

211 212
  if (private->preview_temp_buf)
    {
213
      gimp_temp_buf_unref (private->preview_temp_buf);
214 215 216 217 218 219 220 221 222
      private->preview_temp_buf = NULL;
    }

  if (private->preview_pixbuf)
    {
      g_object_unref (private->preview_pixbuf);
      private->preview_pixbuf = NULL;
    }

223
  G_OBJECT_CLASS (parent_class)->finalize (object);
224 225
}

226 227 228 229 230 231
static void
gimp_viewable_set_property (GObject      *object,
                            guint         property_id,
                            const GValue *value,
                            GParamSpec   *pspec)
{
232 233
  GimpViewable *viewable = GIMP_VIEWABLE (object);

234 235 236
  switch (property_id)
    {
    case PROP_STOCK_ID:
237
      gimp_viewable_set_stock_id (viewable, g_value_get_string (value));
238
      break;
239 240 241
    case PROP_FROZEN:
      /* read-only, fall through */

242 243
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
244
      break;
245 246 247 248 249 250 251 252 253
    }
}

static void
gimp_viewable_get_property (GObject    *object,
                            guint       property_id,
                            GValue     *value,
                            GParamSpec *pspec)
{
254 255
  GimpViewable *viewable = GIMP_VIEWABLE (object);

256 257 258
  switch (property_id)
    {
    case PROP_STOCK_ID:
259 260 261 262
      g_value_set_string (value, gimp_viewable_get_stock_id (viewable));
      break;
    case PROP_FROZEN:
      g_value_set_boolean (value, gimp_viewable_preview_is_frozen (viewable));
263
      break;
264

265 266 267
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
268
    }
269 270
}

271
static gint64
272
gimp_viewable_get_memsize (GimpObject *object,
273
                           gint64     *gui_size)
274
{
275
  GimpViewablePrivate *private = GET_PRIVATE (object);
276

277
  *gui_size += gimp_temp_buf_get_memsize (private->preview_temp_buf);
278

279
  if (private->preview_pixbuf)
280
    {
281 282 283 284
      *gui_size +=
        (gimp_g_object_get_memsize (G_OBJECT (private->preview_pixbuf)) +
         (gsize) gdk_pixbuf_get_height (private->preview_pixbuf) *
         gdk_pixbuf_get_rowstride (private->preview_pixbuf));
285 286
    }

287
  return GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size);
288 289
}

290 291 292
static void
gimp_viewable_real_invalidate_preview (GimpViewable *viewable)
{
293 294 295 296
  GimpViewablePrivate *private = GET_PRIVATE (viewable);

  if (private->preview_temp_buf)
    {
297
      gimp_temp_buf_unref (private->preview_temp_buf);
298 299 300 301 302 303 304 305
      private->preview_temp_buf = NULL;
    }

  if (private->preview_pixbuf)
    {
      g_object_unref (private->preview_pixbuf);
      private->preview_pixbuf = NULL;
    }
306 307 308 309 310 311 312 313 314 315 316 317 318 319
}

static void
gimp_viewable_real_get_preview_size (GimpViewable *viewable,
                                     gint          size,
                                     gboolean      popup,
                                     gboolean      dot_for_dot,
                                     gint         *width,
                                     gint         *height)
{
  *width  = size;
  *height = size;
}

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
static gboolean
gimp_viewable_real_get_popup_size (GimpViewable *viewable,
                                   gint          width,
                                   gint          height,
                                   gboolean      dot_for_dot,
                                   gint         *popup_width,
                                   gint         *popup_height)
{
  gint w, h;

  if (gimp_viewable_get_size (viewable, &w, &h))
    {
      if (w > width || h > height)
        {
          *popup_width  = w;
          *popup_height = h;

          return TRUE;
        }
    }

  return FALSE;
}

344 345
static GdkPixbuf *
gimp_viewable_real_get_new_pixbuf (GimpViewable *viewable,
346
                                   GimpContext  *context,
347 348 349
                                   gint          width,
                                   gint          height)
{
350 351
  GimpTempBuf *temp_buf;
  GdkPixbuf   *pixbuf = NULL;
352

353
  temp_buf = gimp_viewable_get_preview (viewable, context, width, height);
354 355 356

  if (temp_buf)
    {
357 358
      GeglBuffer *src_buffer;
      GeglBuffer *dest_buffer;
359

360
      pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
361
                               babl_format_has_alpha (temp_buf->format),
362 363 364
                               8,
                               temp_buf->width,
                               temp_buf->height);
365

366
      src_buffer  = gimp_temp_buf_create_buffer (temp_buf);
367
      dest_buffer = gimp_pixbuf_create_buffer (pixbuf);
368

369
      gegl_buffer_copy (src_buffer, NULL, dest_buffer, NULL);
370

371 372
      g_object_unref (src_buffer);
      g_object_unref (dest_buffer);
373 374 375 376 377
    }

  return pixbuf;
}

378 379 380 381
static gchar *
gimp_viewable_real_get_description (GimpViewable  *viewable,
                                    gchar        **tooltip)
{
382
  return g_strdup (gimp_object_get_name (viewable));
383 384
}

385 386 387 388 389 390
static GimpContainer *
gimp_viewable_real_get_children (GimpViewable *viewable)
{
  return NULL;
}

391
static gboolean
392
gimp_viewable_serialize_property (GimpConfig       *config,
393 394 395 396 397
                                  guint             property_id,
                                  const GValue     *value,
                                  GParamSpec       *pspec,
                                  GimpConfigWriter *writer)
{
398
  GimpViewablePrivate *private = GET_PRIVATE (config);
399 400 401 402

  switch (property_id)
    {
    case PROP_STOCK_ID:
403
      if (private->stock_id)
404 405
        {
          gimp_config_writer_open (writer, pspec->name);
406
          gimp_config_writer_string (writer, private->stock_id);
407 408 409 410 411 412 413 414 415 416 417
          gimp_config_writer_close (writer);
        }
      return TRUE;

    default:
      break;
    }

  return FALSE;
}

418 419 420 421 422 423
/**
 * gimp_viewable_invalidate_preview:
 * @viewable: a viewable object
 *
 * Causes any cached preview to be marked as invalid, so that a new
 * preview will be generated at the next attempt to display one.
424
 **/
425 426 427
void
gimp_viewable_invalidate_preview (GimpViewable *viewable)
{
428 429
  GimpViewablePrivate *private;

430 431
  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));

432 433 434
  private = GET_PRIVATE (viewable);

  if (private->freeze_count == 0)
435
    g_signal_emit (viewable, viewable_signals[INVALIDATE_PREVIEW], 0);
436 437
}

438 439 440 441 442 443 444
/**
 * gimp_viewable_size_changed:
 * @viewable: a viewable object
 *
 * This function sends a signal that is handled at a lower level in the
 * object hierarchy, and provides a mechanism by which objects derived
 * from #GimpViewable can respond to size changes.
445
 **/
446 447 448 449 450
void
gimp_viewable_size_changed (GimpViewable *viewable)
{
  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));

451
  g_signal_emit (viewable, viewable_signals[SIZE_CHANGED], 0);
452 453
}

454 455
/**
 * gimp_viewable_calc_preview_size:
456
 * @aspect_width:   unscaled width of the preview for an item.
457
 * @aspect_height:  unscaled height of the preview for an item.
458 459 460 461 462 463 464 465 466
 * @width:          maximum available width for scaled preview.
 * @height:         maximum available height for scaled preview.
 * @dot_for_dot:    if #TRUE, ignore any differences in axis resolution.
 * @xresolution:    resolution in the horizontal direction.
 * @yresolution:    resolution in the vertical direction.
 * @return_width:   place to return the calculated preview width.
 * @return_height:  place to return the calculated preview height.
 * @scaling_up:     returns #TRUE here if the calculated preview size
 *                  is larger than the viewable itself.
467
 *
468 469 470 471 472 473 474
 * A utility function, for calculating the dimensions of a preview
 * based on the information specified in the arguments.  The arguments
 * @aspect_width and @aspect_height are the dimensions of the unscaled
 * preview.  The arguments @width and @height represent the maximum
 * width and height that the scaled preview must fit into. The
 * preview is scaled to be as large as possible without exceeding
 * these constraints.
475
 *
476 477 478 479 480 481
 * If @dot_for_dot is #TRUE, and @xresolution and @yresolution are
 * different, then these results are corrected for the difference in
 * resolution on the two axes, so that the requested aspect ratio
 * applies to the appearance of the display rather than to pixel
 * counts.
 **/
482
void
483 484 485 486 487 488 489 490 491 492
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)
493 494 495 496 497 498 499 500 501 502 503 504 505
{
  gdouble xratio;
  gdouble yratio;

  if (aspect_width > aspect_height)
    {
      xratio = yratio = (gdouble) width / (gdouble) aspect_width;
    }
  else
    {
      xratio = yratio = (gdouble) height / (gdouble) aspect_height;
    }

506
  if (! dot_for_dot && xresolution != yresolution)
507 508 509 510 511 512 513 514 515 516 517 518
    {
      yratio *= xresolution / yresolution;
    }

  width  = RINT (xratio * (gdouble) aspect_width);
  height = RINT (yratio * (gdouble) aspect_height);

  if (width  < 1) width  = 1;
  if (height < 1) height = 1;

  if (return_width)  *return_width  = width;
  if (return_height) *return_height = height;
519
  if (scaling_up)    *scaling_up    = (xratio > 1.0) || (yratio > 1.0);
520 521
}

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
gboolean
gimp_viewable_get_size (GimpViewable  *viewable,
                        gint          *width,
                        gint          *height)
{
  GimpViewableClass *viewable_class;
  gboolean           retval = FALSE;
  gint               w      = 0;
  gint               h      = 0;

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE);

  viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);

  if (viewable_class->get_size)
    retval = viewable_class->get_size (viewable, &w, &h);

  if (width)  *width  = w;
  if (height) *height = h;

  return retval;
}

545 546
/**
 * gimp_viewable_get_preview_size:
547 548 549
 * @viewable:    the object for which to calculate the preview size.
 * @size:        requested size for preview.
 * @popup:       %TRUE if the preview is intended for a popup window.
550
 * @dot_for_dot: If #TRUE, ignore any differences in X and Y resolution.
551 552
 * @width:       return location for the the calculated width.
 * @height:      return location for the calculated height.
553
 *
554 555 556 557
 * Retrieve the size of a viewable's preview.  By default, this
 * simply returns the value of the @size argument for both the @width
 * and @height, but this can be overridden in objects derived from
 * #GimpViewable.  If either the width or height exceeds
558
 * #GIMP_VIEWABLE_MAX_PREVIEW_SIZE, they are silently truncated.
559
 **/
560 561 562 563 564 565 566
void
gimp_viewable_get_preview_size (GimpViewable *viewable,
                                gint          size,
                                gboolean      popup,
                                gboolean      dot_for_dot,
                                gint         *width,
                                gint         *height)
567
{
568 569
  gint w, h;

570
  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
571
  g_return_if_fail (size > 0);
572

573 574
  GIMP_VIEWABLE_GET_CLASS (viewable)->get_preview_size (viewable, size,
                                                        popup, dot_for_dot,
575 576 577 578 579 580 581 582
                                                        &w, &h);

  w = MIN (w, GIMP_VIEWABLE_MAX_PREVIEW_SIZE);
  h = MIN (h, GIMP_VIEWABLE_MAX_PREVIEW_SIZE);

  if (width)  *width  = w;
  if (height) *height = h;

583 584
}

585 586
/**
 * gimp_viewable_get_popup_size:
587 588 589 590 591 592
 * @viewable:     the object for which to calculate the popup size.
 * @width:        the width of the preview from which the popup will be shown.
 * @height:       the height of the preview from which the popup will be shown.
 * @dot_for_dot:  If #TRUE, ignore any differences in X and Y resolution.
 * @popup_width:  return location for the calculated popup width.
 * @popup_height: return location for the calculated popup height.
593
 *
594 595 596 597 598 599 600 601 602
 * Calculate the size of a viewable's preview, for use in making a
 * popup. The arguments @width and @height specify the size of the
 * preview from which the popup will be shown.
 *
 * Returns: Whether the viewable wants a popup to be shown. Usually
 *          %TRUE if the passed preview size is smaller than the viewable
 *          size, and %FALSE if the viewable completely fits into the
 *          original preview.
 **/
603 604 605 606 607 608 609 610
gboolean
gimp_viewable_get_popup_size (GimpViewable *viewable,
                              gint          width,
                              gint          height,
                              gboolean      dot_for_dot,
                              gint         *popup_width,
                              gint         *popup_height)
{
611
  gint w, h;
612 613 614

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE);

615 616 617 618
  if (GIMP_VIEWABLE_GET_CLASS (viewable)->get_popup_size (viewable,
                                                          width, height,
                                                          dot_for_dot,
                                                          &w, &h))
619
    {
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
      if (w < 1) w = 1;
      if (h < 1) h = 1;

      /*  limit the popup to 2 * GIMP_VIEWABLE_MAX_POPUP_SIZE
       *  on each axis.
       */
      if ((w > (2 * GIMP_VIEWABLE_MAX_POPUP_SIZE)) ||
          (h > (2 * GIMP_VIEWABLE_MAX_POPUP_SIZE)))
        {
          gimp_viewable_calc_preview_size (w, h,
                                           2 * GIMP_VIEWABLE_MAX_POPUP_SIZE,
                                           2 * GIMP_VIEWABLE_MAX_POPUP_SIZE,
                                           dot_for_dot, 1.0, 1.0,
                                           &w, &h, NULL);
        }
635

636 637 638 639
      /*  limit the number of pixels to
       *  GIMP_VIEWABLE_MAX_POPUP_SIZE ^ 2
       */
      if ((w * h) > SQR (GIMP_VIEWABLE_MAX_POPUP_SIZE))
640
        {
641
          gdouble factor;
642

643 644 645 646 647
          factor = sqrt (((gdouble) (w * h) /
                          (gdouble) SQR (GIMP_VIEWABLE_MAX_POPUP_SIZE)));

          w = RINT ((gdouble) w / factor);
          h = RINT ((gdouble) h / factor);
648
        }
649 650 651 652 653 654 655 656

      if (w < 1) w = 1;
      if (h < 1) h = 1;

      if (popup_width)  *popup_width  = w;
      if (popup_height) *popup_height = h;

      return TRUE;
657 658 659 660 661
    }

  return FALSE;
}

662 663 664
/**
 * gimp_viewable_get_preview:
 * @viewable: The viewable object to get a preview for.
665
 * @context:  The context to render the preview for.
666 667
 * @width:    desired width for the preview
 * @height:   desired height for the preview
668 669
 *
 * Gets a preview for a viewable object, by running through a variety
670 671 672 673 674 675 676 677
 * of methods until it finds one that works.  First, if an
 * implementation exists of a "get_preview" method, it is tried, and
 * the result is returned if it is not #NULL.  Second, the function
 * checks to see whether there is a cached preview with the correct
 * dimensions; if so, it is returned.  If neither of these works, then
 * the function looks for an implementation of the "get_new_preview"
 * method, and executes it, caching the result.  If everything fails,
 * #NULL is returned.
678
 *
679 680
 * Returns: A #GimpTempBuf containg the preview image, or #NULL if
 *          none can be found or created.
681
 **/
682
GimpTempBuf *
683
gimp_viewable_get_preview (GimpViewable *viewable,
684
                           GimpContext  *context,
685 686
                           gint          width,
                           gint          height)
687
{
688 689
  GimpViewablePrivate *private;
  GimpViewableClass   *viewable_class;
690
  GimpTempBuf         *temp_buf = NULL;
691 692

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
693
  g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
694 695 696
  g_return_val_if_fail (width  > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);

697 698
  private = GET_PRIVATE (viewable);

699
  if (G_UNLIKELY (context == NULL))
700 701
    g_warning ("%s: context is NULL", G_STRFUNC);

702 703 704
  viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);

  if (viewable_class->get_preview)
705
    temp_buf = viewable_class->get_preview (viewable, context, width, height);
706

707 708 709
  if (temp_buf)
    return temp_buf;

710 711 712 713 714 715 716
  if (private->preview_temp_buf)
    {
      if (private->preview_temp_buf->width  == width  &&
          private->preview_temp_buf->height == height)
        {
          return private->preview_temp_buf;
        }
717

718
      gimp_temp_buf_unref (private->preview_temp_buf);
719 720
      private->preview_temp_buf = NULL;
    }
721

722
  if (viewable_class->get_new_preview)
723 724
    temp_buf = viewable_class->get_new_preview (viewable, context,
                                                width, height);
725

726
  private->preview_temp_buf = temp_buf;
727

728 729 730
  return temp_buf;
}

731 732 733
/**
 * gimp_viewable_get_new_preview:
 * @viewable: The viewable object to get a preview for.
734 735
 * @width:    desired width for the preview
 * @height:   desired height for the preview
736
 *
737 738 739 740 741
 * Gets a new preview for a viewable object.  Similar to
 * gimp_viewable_get_preview(), except that it tries things in a
 * different order, first looking for a "get_new_preview" method, and
 * then if that fails for a "get_preview" method.  This function does
 * not look for a cached preview.
742
 *
743 744
 * Returns: A #GimpTempBuf containg the preview image, or #NULL if
 *          none can be found or created.
745
 **/
746
GimpTempBuf *
747
gimp_viewable_get_new_preview (GimpViewable *viewable,
748
                               GimpContext  *context,
749 750
                               gint          width,
                               gint          height)
751
{
752
  GimpViewableClass *viewable_class;
753
  GimpTempBuf       *temp_buf = NULL;
754 755

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
756
  g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
757 758 759
  g_return_val_if_fail (width  > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);

760
  if (G_UNLIKELY (context == NULL))
761 762
    g_warning ("%s: context is NULL", G_STRFUNC);

763 764 765
  viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);

  if (viewable_class->get_new_preview)
766 767
    temp_buf = viewable_class->get_new_preview (viewable, context,
                                                width, height);
768 769 770 771

  if (temp_buf)
    return temp_buf;

772
  if (viewable_class->get_preview)
773 774
    temp_buf = viewable_class->get_preview (viewable, context,
                                            width, height);
775 776

  if (temp_buf)
777
    return gimp_temp_buf_copy (temp_buf);
778 779 780

  return NULL;
}
781

782 783 784
/**
 * gimp_viewable_get_dummy_preview:
 * @viewable: viewable object for which to get a dummy preview.
785 786 787
 * @width:    width of the preview.
 * @height:   height of the preview.
 * @bpp:      bytes per pixel for the preview, must be 3 or 4.
788 789
 *
 * Creates a dummy preview the fits into the specified dimensions,
790 791
 * containing a default "question" symbol.  This function is used to
 * generate a preview in situations where layer previews have been
792 793
 * disabled in the current Gimp configuration.
 *
794
 * Returns: a #GimpTempBuf containing the preview image.
795
 **/
796
GimpTempBuf *
797 798 799 800
gimp_viewable_get_dummy_preview (GimpViewable *viewable,
                                 gint          width,
                                 gint          height,
                                 const Babl   *format)
801
{
802 803
  GdkPixbuf   *pixbuf;
  GimpTempBuf *buf;
804 805
  GeglBuffer  *src_buffer;
  GeglBuffer  *dest_buffer;
806 807 808 809

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
  g_return_val_if_fail (width  > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);
810
  g_return_val_if_fail (format != NULL, NULL);
811

812 813
  pixbuf = gimp_viewable_get_dummy_pixbuf (viewable, width, height,
                                           babl_format_has_alpha (format));
814

815
  buf = gimp_temp_buf_new (width, height, format);
816

817 818
  src_buffer  = gimp_pixbuf_create_buffer (pixbuf);
  dest_buffer = gimp_temp_buf_create_buffer (buf);
819

820
  gegl_buffer_copy (src_buffer, NULL, dest_buffer, NULL);
821

822 823
  g_object_unref (src_buffer);
  g_object_unref (dest_buffer);
824 825

  g_object_unref (pixbuf);
826

827
  return buf;
828 829
}

830 831 832
/**
 * gimp_viewable_get_pixbuf:
 * @viewable: The viewable object to get a pixbuf preview for.
833
 * @context:  The context to render the preview for.
834 835
 * @width:    desired width for the preview
 * @height:   desired height for the preview
836 837
 *
 * Gets a preview for a viewable object, by running through a variety
838 839 840 841 842 843 844 845
 * of methods until it finds one that works.  First, if an
 * implementation exists of a "get_pixbuf" method, it is tried, and
 * the result is returned if it is not #NULL.  Second, the function
 * checks to see whether there is a cached preview with the correct
 * dimensions; if so, it is returned.  If neither of these works, then
 * the function looks for an implementation of the "get_new_pixbuf"
 * method, and executes it, caching the result.  If everything fails,
 * #NULL is returned.
846 847
 *
 * Returns: A #GdkPixbuf containing the preview pixbuf, or #NULL if none can
848 849
 *          be found or created.
 **/
850
GdkPixbuf *
851
gimp_viewable_get_pixbuf (GimpViewable *viewable,
852
                          GimpContext  *context,
853 854
                          gint          width,
                          gint          height)
855
{
856 857 858
  GimpViewablePrivate *private;
  GimpViewableClass   *viewable_class;
  GdkPixbuf           *pixbuf = NULL;
859 860

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
861
  g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
862 863 864
  g_return_val_if_fail (width  > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);

865 866
  private = GET_PRIVATE (viewable);

867
  if (G_UNLIKELY (context == NULL))
868 869
    g_warning ("%s: context is NULL", G_STRFUNC);

870 871 872
  viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);

  if (viewable_class->get_pixbuf)
873
    pixbuf = viewable_class->get_pixbuf (viewable, context, width, height);
874 875 876 877

  if (pixbuf)
    return pixbuf;

878 879 880 881 882 883 884
  if (private->preview_pixbuf)
    {
      if (gdk_pixbuf_get_width  (private->preview_pixbuf) == width &&
          gdk_pixbuf_get_height (private->preview_pixbuf) == height)
        {
          return pixbuf;
        }
885

886 887 888
      g_object_unref (private->preview_pixbuf);
      private->preview_pixbuf = NULL;
    }
889

890
  if (viewable_class->get_new_pixbuf)
891
    pixbuf = viewable_class->get_new_pixbuf (viewable, context, width, height);
892

893
  private->preview_pixbuf = pixbuf;
894 895 896 897

  return pixbuf;
}

898 899 900
/**
 * gimp_viewable_get_new_pixbuf:
 * @viewable: The viewable object to get a new pixbuf preview for.
901
 * @context:  The context to render the preview for.
902 903
 * @width:    desired width for the pixbuf
 * @height:   desired height for the pixbuf
904
 *
905 906 907 908 909
 * Gets a new preview for a viewable object.  Similar to
 * gimp_viewable_get_pixbuf(), except that it tries things in a
 * different order, first looking for a "get_new_pixbuf" method, and
 * then if that fails for a "get_pixbuf" method.  This function does
 * not look for a cached pixbuf.
910 911
 *
 * Returns: A #GdkPixbuf containing the preview, or #NULL if none can
912 913
 *          be created.
 **/
914
GdkPixbuf *
915
gimp_viewable_get_new_pixbuf (GimpViewable *viewable,
916
                              GimpContext  *context,
917 918
                              gint          width,
                              gint          height)
919
{
920 921
  GimpViewableClass *viewable_class;
  GdkPixbuf         *pixbuf = NULL;
922 923

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
924
  g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
925 926 927
  g_return_val_if_fail (width  > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);

928
  if (G_UNLIKELY (context == NULL))
929 930
    g_warning ("%s: context is NULL", G_STRFUNC);

931
  viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);
932

933
  if (viewable_class->get_new_pixbuf)
934
    pixbuf = viewable_class->get_new_pixbuf (viewable, context, width, height);
935

936 937
  if (pixbuf)
    return pixbuf;
938

939
  if (viewable_class->get_pixbuf)
940
    pixbuf = viewable_class->get_pixbuf (viewable, context, width, height);
941

942 943
  if (pixbuf)
    return gdk_pixbuf_copy (pixbuf);
944

945 946
  return NULL;
}
947

948 949 950
/**
 * gimp_viewable_get_dummy_pixbuf:
 * @viewable: the viewable object for which to create a dummy representation.
951 952 953
 * @width:    maximum permitted width for the pixbuf.
 * @height:   maximum permitted height for the pixbuf.
 * @bpp:      bytes per pixel for the pixbuf, must equal 3 or 4.
954
 *
955 956 957 958 959 960 961
 * Creates a pixbuf containing a default "question" symbol, sized to
 * fit into the specified dimensions.  The depth of the pixbuf must be
 * 3 or 4 because #GdkPixbuf does not support grayscale.  This
 * function is used to generate a preview in situations where
 * previewing has been disabled in the current Gimp configuration.
 * [Note: this function is currently unused except internally to
 * #GimpViewable -- consider making it static?]
962 963
 *
 * Returns: the created #GdkPixbuf.
964
 **/
965 966 967 968
GdkPixbuf *
gimp_viewable_get_dummy_pixbuf (GimpViewable  *viewable,
                                gint           width,
                                gint           height,
969
                                gboolean       with_alpha)
970 971 972 973 974
{
  GdkPixbuf *icon;
  GdkPixbuf *pixbuf;
  gdouble    ratio;
  gint       w, h;
975

976 977 978
  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
  g_return_val_if_fail (width  > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);
979

980 981 982 983 984 985 986 987 988 989 990 991 992
  icon = gdk_pixbuf_new_from_inline (-1, stock_question_64, FALSE, NULL);

  g_return_val_if_fail (icon != NULL, NULL);

  w = gdk_pixbuf_get_width (icon);
  h = gdk_pixbuf_get_height (icon);

  ratio = (gdouble) MIN (width, height) / (gdouble) MAX (w, h);
  ratio = MIN (ratio, 1.0);

  w = RINT (ratio * (gdouble) w);
  h = RINT (ratio * (gdouble) h);

993
  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, with_alpha, 8, width, height);
994 995 996 997 998 999 1000 1001 1002
  gdk_pixbuf_fill (pixbuf, 0xffffffff);

  if (w && h)
    gdk_pixbuf_composite (icon, pixbuf,
                          (width - w) / 2, (height - h) / 2, w, h,
                          (width - w) / 2, (height - h) / 2, ratio, ratio,
                          GDK_INTERP_BILINEAR, 0xFF);

  g_object_unref (icon);
1003 1004 1005

  return pixbuf;
}
1006

1007 1008 1009
/**
 * gimp_viewable_get_description:
 * @viewable: viewable object for which to retrieve a description.
1010
 * @tooltip:  return loaction for an optional tooltip string.
1011 1012
 *
 * Retrieves a string containing a description of the viewable object,
1013 1014
 * By default, it simply returns the name of the object, but this can
 * be overridden by object types that inherit from #GimpViewable.
1015 1016
 *
 * Returns: a copy of the description string.  This should be freed
1017 1018
 *          when it is no longer needed.
 **/
1019 1020 1021 1022 1023 1024
gchar *
gimp_viewable_get_description (GimpViewable  *viewable,
                               gchar        **tooltip)
{
  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);

1025 1026 1027
  if (tooltip)
    *tooltip = NULL;

1028 1029 1030 1031
  return GIMP_VIEWABLE_GET_CLASS (viewable)->get_description (viewable,
                                                              tooltip);
}

1032 1033 1034 1035 1036 1037 1038 1039
/**
 * gimp_viewable_get_stock_id:
 * @viewable: viewable object for which to retrieve a stock ID.
 *
 * Gets the current value of the object's stock ID, for use in
 * constructing an iconic representation of the object.
 *
 * Returns: a pointer to the string containing the stock ID.  The
1040 1041
 *          contents must not be altered or freed.
 **/
1042 1043 1044
const gchar *
gimp_viewable_get_stock_id (GimpViewable *viewable)
{
1045 1046
  GimpViewablePrivate *private;

1047 1048
  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);

1049 1050 1051 1052
  private = GET_PRIVATE (viewable);

  if (private->stock_id)
    return (const gchar *) private->stock_id;
1053 1054 1055 1056

  return GIMP_VIEWABLE_GET_CLASS (viewable)->default_stock_id;
}

1057 1058 1059 1060 1061
/**
 * gimp_viewable_set_stock_id:
 * @viewable: viewable object to assign the specified stock ID.
 * @stock_id: string containing a stock identifier.
 *
1062 1063 1064 1065
 * Seta the object's stock ID, for use in constructing iconic smbols
 * of the object.  The contents of @stock_id are copied, so you can
 * free it when you are done with it.
 **/
1066 1067 1068 1069
void
gimp_viewable_set_stock_id (GimpViewable *viewable,
                            const gchar  *stock_id)
{
1070 1071
  GimpViewablePrivate *private;
  GimpViewableClass   *viewable_class;
1072

1073 1074
  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));

1075 1076 1077 1078
  private = GET_PRIVATE (viewable);

  g_free (private->stock_id);
  private->stock_id = NULL;
1079 1080 1081 1082 1083 1084 1085

  viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);

  if (stock_id)
    {
      if (viewable_class->default_stock_id == NULL ||
          strcmp (stock_id, viewable_class->default_stock_id))
1086
        private->stock_id = g_strdup (stock_id);
1087
    }
1088 1089

  g_object_notify (G_OBJECT (viewable), "stock-id");
1090
}
1091 1092 1093 1094

void
gimp_viewable_preview_freeze (GimpViewable *viewable)
{
1095 1096
  GimpViewablePrivate *private;

1097 1098
  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));

1099
  private = GET_PRIVATE (viewable);
1100

1101 1102 1103
  private->freeze_count++;

  if (private->freeze_count == 1)
1104
    g_object_notify (G_OBJECT (viewable), "frozen");
1105 1106 1107 1108 1109
}

void
gimp_viewable_preview_thaw (GimpViewable *viewable)
{
1110 1111
  GimpViewablePrivate *private;

1112 1113
  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));

1114 1115 1116 1117 1118
  private = GET_PRIVATE (viewable);

  g_return_if_fail (private->freeze_count > 0);

  private->freeze_count--;