gimpviewable.c 42.1 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 26
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
27
#include <gegl.h>
28

29
#include "libgimpcolor/gimpcolor.h"
30
#include "libgimpmath/gimpmath.h"
31 32
#include "libgimpconfig/gimpconfig.h"

Michael Natterer's avatar
Michael Natterer committed
33
#include "core-types.h"
34

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

41
#include "icons/gimp-core-pixbufs.h"
42

43

44 45 46
enum
{
  PROP_0,
47 48
  PROP_STOCK_ID, /* compat */
  PROP_ICON_NAME,
49
  PROP_ICON_PIXBUF,
50
  PROP_FROZEN
51 52
};

53 54 55
enum
{
  INVALIDATE_PREVIEW,
56
  SIZE_CHANGED,
57 58 59 60
  LAST_SIGNAL
};


61 62 63 64
typedef struct _GimpViewablePrivate GimpViewablePrivate;

struct _GimpViewablePrivate
{
65
  gchar        *icon_name;
66
  GdkPixbuf    *icon_pixbuf;
67 68
  gint          freeze_count;
  GimpViewable *parent;
69

70
  GimpTempBuf  *preview_temp_buf;
71
  GdkPixbuf    *preview_pixbuf;
72 73 74
};

#define GET_PRIVATE(viewable) G_TYPE_INSTANCE_GET_PRIVATE (viewable, \
Michael Natterer's avatar
Michael Natterer committed
75 76
                                                           GIMP_TYPE_VIEWABLE, \
                                                           GimpViewablePrivate)
77 78


79
static void    gimp_viewable_config_iface_init (GimpConfigInterface *iface);
80

81 82 83 84 85 86 87 88 89
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);
90

91 92
static gint64  gimp_viewable_get_memsize             (GimpObject    *object,
                                                      gint64        *gui_size);
93

94
static void    gimp_viewable_real_invalidate_preview (GimpViewable  *viewable);
95

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

116
static gboolean gimp_viewable_serialize_property     (GimpConfig    *config,
117 118 119 120
                                                      guint          property_id,
                                                      const GValue  *value,
                                                      GParamSpec    *pspec,
                                                      GimpConfigWriter *writer);
121 122 123 124 125 126
static gboolean gimp_viewable_deserialize_property   (GimpConfig       *config,
                                                      guint             property_id,
                                                      GValue           *value,
                                                      GParamSpec       *pspec,
                                                      GScanner         *scanner,
                                                      GTokenType       *expected);
127

128

129 130
G_DEFINE_TYPE_WITH_CODE (GimpViewable, gimp_viewable, GIMP_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
131
                                                gimp_viewable_config_iface_init))
132

133
#define parent_class gimp_viewable_parent_class
134

135
static guint viewable_signals[LAST_SIGNAL] = { 0 };
136 137 138 139 140


static void
gimp_viewable_class_init (GimpViewableClass *klass)
{
141 142
  GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
  GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
143

144
  viewable_signals[INVALIDATE_PREVIEW] =
145
    g_signal_new ("invalidate-preview",
146 147 148 149 150 151
                  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);
152

153
  viewable_signals[SIZE_CHANGED] =
154
    g_signal_new ("size-changed",
155 156 157 158 159 160
                  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);
161

162
  object_class->finalize         = gimp_viewable_finalize;
163 164
  object_class->get_property     = gimp_viewable_get_property;
  object_class->set_property     = gimp_viewable_set_property;
165

166
  gimp_object_class->get_memsize = gimp_viewable_get_memsize;
167

168
  klass->default_icon_name       = "gimp-question";
169
  klass->name_changed_signal     = "name-changed";
170

171 172 173
  klass->invalidate_preview      = gimp_viewable_real_invalidate_preview;
  klass->size_changed            = NULL;

174
  klass->get_size                = NULL;
175
  klass->get_preview_size        = gimp_viewable_real_get_preview_size;
176
  klass->get_popup_size          = gimp_viewable_real_get_popup_size;
177 178
  klass->get_preview             = NULL;
  klass->get_new_preview         = NULL;
179 180
  klass->get_pixbuf              = NULL;
  klass->get_new_pixbuf          = gimp_viewable_real_get_new_pixbuf;
181
  klass->get_description         = gimp_viewable_real_get_description;
182
  klass->get_children            = gimp_viewable_real_get_children;
183 184
  klass->set_expanded            = NULL;
  klass->get_expanded            = NULL;
185

186
  /* compat property */
187
  GIMP_CONFIG_INSTALL_PROP_STRING (object_class, PROP_STOCK_ID, "stock-id",
188 189
                                   NULL, NULL,
                                   GIMP_PARAM_STATIC_STRINGS);
190

191 192 193 194
  GIMP_CONFIG_INSTALL_PROP_STRING (object_class, PROP_ICON_NAME, "icon-name",
                                   NULL, NULL,
                                   GIMP_PARAM_STATIC_STRINGS);

195 196 197 198 199 200
  GIMP_CONFIG_INSTALL_PROP_OBJECT (object_class, PROP_ICON_PIXBUF,
                                   "icon-pixbuf", NULL,
                                   GDK_TYPE_PIXBUF,
                                   G_PARAM_CONSTRUCT |
                                   GIMP_PARAM_STATIC_STRINGS);

201 202 203 204 205
  g_object_class_install_property (object_class, PROP_FROZEN,
                                   g_param_spec_boolean ("frozen",
                                                         NULL, NULL,
                                                         FALSE,
                                                         GIMP_PARAM_READABLE));
206 207

  g_type_class_add_private (klass, sizeof (GimpViewablePrivate));
208 209 210 211 212
}

static void
gimp_viewable_init (GimpViewable *viewable)
{
213 214
}

215
static void
216
gimp_viewable_config_iface_init (GimpConfigInterface *iface)
217
{
218
  iface->deserialize_property = gimp_viewable_deserialize_property;
219
  iface->serialize_property   = gimp_viewable_serialize_property;
220 221
}

222 223 224
static void
gimp_viewable_finalize (GObject *object)
{
225
  GimpViewablePrivate *private = GET_PRIVATE (object);
226

227
  if (private->icon_name)
228
    {
229 230
      g_free (private->icon_name);
      private->icon_name = NULL;
231 232
    }

233 234 235 236 237 238
  if (private->icon_pixbuf)
    {
      g_object_unref (private->icon_pixbuf);
      private->icon_pixbuf = NULL;
    }

239 240
  if (private->preview_temp_buf)
    {
241
      gimp_temp_buf_unref (private->preview_temp_buf);
242 243 244 245 246 247 248 249 250
      private->preview_temp_buf = NULL;
    }

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

251
  G_OBJECT_CLASS (parent_class)->finalize (object);
252 253
}

254 255 256 257 258 259
static void
gimp_viewable_set_property (GObject      *object,
                            guint         property_id,
                            const GValue *value,
                            GParamSpec   *pspec)
{
260 261
  GimpViewable        *viewable = GIMP_VIEWABLE (object);
  GimpViewablePrivate *private  = GET_PRIVATE (object);
262

263 264 265
  switch (property_id)
    {
    case PROP_STOCK_ID:
266 267
    case PROP_ICON_NAME:
      gimp_viewable_set_icon_name (viewable, g_value_get_string (value));
268
      break;
269 270 271 272 273 274
    case PROP_ICON_PIXBUF:
      if (private->icon_pixbuf)
        g_object_unref (private->icon_pixbuf);
      private->icon_pixbuf = g_value_dup_object (value);
      gimp_viewable_invalidate_preview (viewable);
      break;
275 276 277
    case PROP_FROZEN:
      /* read-only, fall through */

278 279
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
280
      break;
281 282 283 284 285 286 287 288 289
    }
}

static void
gimp_viewable_get_property (GObject    *object,
                            guint       property_id,
                            GValue     *value,
                            GParamSpec *pspec)
{
290 291
  GimpViewable        *viewable = GIMP_VIEWABLE (object);
  GimpViewablePrivate *private  = GET_PRIVATE (object);
292

293 294 295
  switch (property_id)
    {
    case PROP_STOCK_ID:
296 297
    case PROP_ICON_NAME:
      g_value_set_string (value, gimp_viewable_get_icon_name (viewable));
298
      break;
299 300 301
    case PROP_ICON_PIXBUF:
      g_value_set_object (value, private->icon_pixbuf);
      break;
302 303
    case PROP_FROZEN:
      g_value_set_boolean (value, gimp_viewable_preview_is_frozen (viewable));
304
      break;
305

306 307 308
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
309
    }
310 311
}

312
static gint64
313
gimp_viewable_get_memsize (GimpObject *object,
314
                           gint64     *gui_size)
315
{
316
  GimpViewablePrivate *private = GET_PRIVATE (object);
317

318
  *gui_size += gimp_temp_buf_get_memsize (private->preview_temp_buf);
319

320
  if (private->preview_pixbuf)
321
    {
322 323 324 325
      *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));
326 327
    }

328
  return GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size);
329 330
}

331 332 333
static void
gimp_viewable_real_invalidate_preview (GimpViewable *viewable)
{
334 335 336 337
  GimpViewablePrivate *private = GET_PRIVATE (viewable);

  if (private->preview_temp_buf)
    {
338
      gimp_temp_buf_unref (private->preview_temp_buf);
339 340 341 342 343 344 345 346
      private->preview_temp_buf = NULL;
    }

  if (private->preview_pixbuf)
    {
      g_object_unref (private->preview_pixbuf);
      private->preview_pixbuf = NULL;
    }
347 348 349 350 351 352 353 354 355 356 357 358 359 360
}

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;
}

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
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;
}

385 386
static GdkPixbuf *
gimp_viewable_real_get_new_pixbuf (GimpViewable *viewable,
387
                                   GimpContext  *context,
388 389 390
                                   gint          width,
                                   gint          height)
{
391 392 393
  GimpViewablePrivate *private = GET_PRIVATE (viewable);
  GdkPixbuf           *pixbuf  = NULL;
  GimpTempBuf         *temp_buf;
394

395
  temp_buf = gimp_viewable_get_preview (viewable, context, width, height);
396 397 398

  if (temp_buf)
    {
399
      pixbuf = gimp_temp_buf_create_pixbuf (temp_buf);
400
    }
401 402 403 404 405 406 407
  else if (private->icon_pixbuf)
    {
      pixbuf = gdk_pixbuf_scale_simple (private->icon_pixbuf,
                                        width,
                                        height,
                                        GDK_INTERP_BILINEAR);
    }
408 409 410 411

  return pixbuf;
}

412 413 414 415
static gchar *
gimp_viewable_real_get_description (GimpViewable  *viewable,
                                    gchar        **tooltip)
{
416
  return g_strdup (gimp_object_get_name (viewable));
417 418
}

419 420 421 422 423 424
static GimpContainer *
gimp_viewable_real_get_children (GimpViewable *viewable)
{
  return NULL;
}

425
static gboolean
426
gimp_viewable_serialize_property (GimpConfig       *config,
427 428 429 430 431
                                  guint             property_id,
                                  const GValue     *value,
                                  GParamSpec       *pspec,
                                  GimpConfigWriter *writer)
{
432
  GimpViewablePrivate *private = GET_PRIVATE (config);
433 434 435 436

  switch (property_id)
    {
    case PROP_STOCK_ID:
437 438 439 440
      return TRUE;

    case PROP_ICON_NAME:
      if (private->icon_name)
441 442
        {
          gimp_config_writer_open (writer, pspec->name);
443
          gimp_config_writer_string (writer, private->icon_name);
444 445 446 447
          gimp_config_writer_close (writer);
        }
      return TRUE;

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
    case PROP_ICON_PIXBUF:
      {
        GdkPixbuf *icon_pixbuf    = NULL;
        gchar     *pixbuffer      = NULL;
        gchar     *pixbuffer_enc  = NULL;
        gsize      pixbuffer_size = 0;
        GError    *error          = NULL;

        icon_pixbuf = g_value_get_object (value);
        if (icon_pixbuf)
          {
            if (gdk_pixbuf_save_to_buffer (icon_pixbuf,
                                           &pixbuffer,
                                           &pixbuffer_size,
                                           "png", &error, NULL))
              {
                pixbuffer_enc = g_base64_encode ((guchar *)pixbuffer,
                                                 pixbuffer_size);
                gimp_config_writer_open (writer, "icon-pixbuf");
                gimp_config_writer_string (writer, pixbuffer_enc);
                gimp_config_writer_close (writer);

                g_free (pixbuffer_enc);
                g_free (pixbuffer);
              }
          }
      }
      return TRUE;

477 478 479 480 481 482 483
    default:
      break;
    }

  return FALSE;
}

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
static gboolean
gimp_viewable_deserialize_property (GimpConfig *config,
                                    guint       property_id,
                                    GValue     *value,
                                    GParamSpec *pspec,
                                    GScanner   *scanner,
                                    GTokenType *expected)
{
  switch (property_id)
    {
    case PROP_ICON_PIXBUF:
      {
        gchar     *encoded_image = NULL;
        GdkPixbuf *icon_pixbuf   = NULL;

        if (! gimp_scanner_parse_string (scanner, &encoded_image))
          {
            *expected = G_TOKEN_STRING;
            break;
          }

        if (encoded_image && strlen (encoded_image) > 0)
          {
            gsize   out_len       = 0;
            guchar *decoded_image = g_base64_decode (encoded_image, &out_len);

            if (decoded_image)
              {
                GInputStream *decoded_image_stream = NULL;
                GdkPixbuf    *pixbuf               = NULL;

                decoded_image_stream =
                  g_memory_input_stream_new_from_data (decoded_image,
                                                       out_len, NULL);
                pixbuf = gdk_pixbuf_new_from_stream (decoded_image_stream,
                                                     NULL,
                                                     NULL);
521 522
                g_object_unref (decoded_image_stream);

523 524 525 526
                if (pixbuf)
                  {
                    icon_pixbuf = pixbuf;
                  }
527

528 529 530 531
                g_free (decoded_image);
              }
          }

532
        g_value_take_object (value, icon_pixbuf);
533 534 535 536 537 538 539 540 541 542
      }
      break;

    default:
      return FALSE;
    }

  return TRUE;
}

543 544 545 546 547 548
/**
 * 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.
549
 **/
550 551 552
void
gimp_viewable_invalidate_preview (GimpViewable *viewable)
{
553 554
  GimpViewablePrivate *private;

555 556
  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));

557 558 559
  private = GET_PRIVATE (viewable);

  if (private->freeze_count == 0)
560
    g_signal_emit (viewable, viewable_signals[INVALIDATE_PREVIEW], 0);
561 562
}

563 564 565 566 567 568 569
/**
 * 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.
570
 **/
571 572 573 574 575
void
gimp_viewable_size_changed (GimpViewable *viewable)
{
  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));

576
  g_signal_emit (viewable, viewable_signals[SIZE_CHANGED], 0);
577 578
}

579 580
/**
 * gimp_viewable_calc_preview_size:
581
 * @aspect_width:   unscaled width of the preview for an item.
582
 * @aspect_height:  unscaled height of the preview for an item.
583 584 585 586 587 588 589 590 591
 * @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.
592
 *
593 594 595 596 597 598 599
 * 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.
600
 *
601 602 603 604 605 606
 * 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.
 **/
607
void
608 609 610 611 612 613 614 615 616 617
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)
618 619 620 621 622 623 624 625 626 627 628 629 630
{
  gdouble xratio;
  gdouble yratio;

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

631
  if (! dot_for_dot && xresolution != yresolution)
632 633 634 635 636 637 638 639 640 641 642 643
    {
      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;
644
  if (scaling_up)    *scaling_up    = (xratio > 1.0) || (yratio > 1.0);
645 646
}

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
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;
}

670 671
/**
 * gimp_viewable_get_preview_size:
672 673 674
 * @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.
675
 * @dot_for_dot: If #TRUE, ignore any differences in X and Y resolution.
676 677
 * @width:       return location for the the calculated width.
 * @height:      return location for the calculated height.
678
 *
679 680 681 682
 * 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
683
 * #GIMP_VIEWABLE_MAX_PREVIEW_SIZE, they are silently truncated.
684
 **/
685 686 687 688 689 690 691
void
gimp_viewable_get_preview_size (GimpViewable *viewable,
                                gint          size,
                                gboolean      popup,
                                gboolean      dot_for_dot,
                                gint         *width,
                                gint         *height)
692
{
693 694
  gint w, h;

695
  g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
696
  g_return_if_fail (size > 0);
697

698 699
  GIMP_VIEWABLE_GET_CLASS (viewable)->get_preview_size (viewable, size,
                                                        popup, dot_for_dot,
700 701 702 703 704 705 706 707
                                                        &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;

708 709
}

710 711
/**
 * gimp_viewable_get_popup_size:
712 713 714 715 716 717
 * @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.
718
 *
719 720 721 722 723 724 725 726 727
 * 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.
 **/
728 729 730 731 732 733 734 735
gboolean
gimp_viewable_get_popup_size (GimpViewable *viewable,
                              gint          width,
                              gint          height,
                              gboolean      dot_for_dot,
                              gint         *popup_width,
                              gint         *popup_height)
{
736
  gint w, h;
737 738 739

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), FALSE);

740 741 742 743
  if (GIMP_VIEWABLE_GET_CLASS (viewable)->get_popup_size (viewable,
                                                          width, height,
                                                          dot_for_dot,
                                                          &w, &h))
744
    {
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
      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);
        }
760

761 762 763 764
      /*  limit the number of pixels to
       *  GIMP_VIEWABLE_MAX_POPUP_SIZE ^ 2
       */
      if ((w * h) > SQR (GIMP_VIEWABLE_MAX_POPUP_SIZE))
765
        {
766
          gdouble factor;
767

768 769 770 771 772
          factor = sqrt (((gdouble) (w * h) /
                          (gdouble) SQR (GIMP_VIEWABLE_MAX_POPUP_SIZE)));

          w = RINT ((gdouble) w / factor);
          h = RINT ((gdouble) h / factor);
773
        }
774 775 776 777 778 779 780 781

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

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

      return TRUE;
782 783 784 785 786
    }

  return FALSE;
}

787 788 789
/**
 * gimp_viewable_get_preview:
 * @viewable: The viewable object to get a preview for.
790
 * @context:  The context to render the preview for.
791 792
 * @width:    desired width for the preview
 * @height:   desired height for the preview
793 794
 *
 * Gets a preview for a viewable object, by running through a variety
795 796 797 798 799 800 801 802
 * 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.
803
 *
804 805
 * Returns: A #GimpTempBuf containg the preview image, or #NULL if
 *          none can be found or created.
806
 **/
807
GimpTempBuf *
808
gimp_viewable_get_preview (GimpViewable *viewable,
809
                           GimpContext  *context,
810 811
                           gint          width,
                           gint          height)
812
{
813 814
  GimpViewablePrivate *private;
  GimpViewableClass   *viewable_class;
815
  GimpTempBuf         *temp_buf = NULL;
816 817

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
818
  g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
819 820 821
  g_return_val_if_fail (width  > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);

822 823
  private = GET_PRIVATE (viewable);

824
  if (G_UNLIKELY (context == NULL))
825 826
    g_warning ("%s: context is NULL", G_STRFUNC);

827 828 829
  viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);

  if (viewable_class->get_preview)
830
    temp_buf = viewable_class->get_preview (viewable, context, width, height);
831

832 833 834
  if (temp_buf)
    return temp_buf;

835 836
  if (private->preview_temp_buf)
    {
837 838
      if (gimp_temp_buf_get_width  (private->preview_temp_buf) == width &&
          gimp_temp_buf_get_height (private->preview_temp_buf) == height)
839 840 841
        {
          return private->preview_temp_buf;
        }
842

843
      gimp_temp_buf_unref (private->preview_temp_buf);
844 845
      private->preview_temp_buf = NULL;
    }
846

847
  if (viewable_class->get_new_preview)
848 849
    temp_buf = viewable_class->get_new_preview (viewable, context,
                                                width, height);
850

851
  private->preview_temp_buf = temp_buf;
852

853 854 855
  return temp_buf;
}

856 857 858
/**
 * gimp_viewable_get_new_preview:
 * @viewable: The viewable object to get a preview for.
859 860
 * @width:    desired width for the preview
 * @height:   desired height for the preview
861
 *
862 863 864 865 866
 * 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.
867
 *
868 869
 * Returns: A #GimpTempBuf containg the preview image, or #NULL if
 *          none can be found or created.
870
 **/
871
GimpTempBuf *
872
gimp_viewable_get_new_preview (GimpViewable *viewable,
873
                               GimpContext  *context,
874 875
                               gint          width,
                               gint          height)
876
{
877
  GimpViewableClass *viewable_class;
878
  GimpTempBuf       *temp_buf = NULL;
879 880

  g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
881
  g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
882 883 884
  g_return_val_if_fail (width  > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);

885
  if (G_UNLIKELY (context == NULL))
886 887
    g_warning ("%s: context is NULL", G_STRFUNC);

888 889 890
  viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);

  if (viewable_class->get_new_preview)
891 892
    temp_buf = viewable_class->get_new_preview (viewable, context,
                                                width, height);
893 894 895 896

  if (temp_buf)
    return temp_buf;

897
  if (viewable_class->get_preview)
898 899
    temp_buf = viewable_class->get_preview (viewable, context,
                                            width, height);
900 901

  if (temp_buf)
902
    return gimp_temp_buf_copy (temp_buf);
903 904 905

  return NULL;
}
906

907 908 909
/**
 * gimp_viewable_get_dummy_preview:
 * @viewable: viewable object for which to get a dummy preview.
910 911 912
 * @width:    width of the preview.
 * @height:   height of the preview.
 * @bpp:      bytes per pixel for the preview, must be 3 or 4.
913 914
 *
 * Creates a dummy preview the fits into the specified dimensions,
915 916
 * containing a default "question" symbol.  This function is used to
 * generate a preview in situations where layer previews have been
917 918
 * disabled in the current Gimp configuration.
 *
919
 * Returns: a #GimpTempBuf containing the preview image.
920
 **/
921
GimpTempBuf *
922 923 924 925
gimp_viewable_get_dummy_preview (GimpViewable *viewable,
                                 gint          width,
                                 gint          height,
                                 const Babl   *format)
926
{
927 928
  GdkPixbuf   *pixbuf;
  GimpTempBuf *buf;
929 930 931 932

  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);
933
  g_return_val_if_fail (format != NULL, NULL);
934

935 936
  pixbuf = gimp_viewable_get_dummy_pixbuf (viewable, width, height,
                                           babl_format_has_alpha (format));
937

938
  buf = gimp_temp_buf_new_from_pixbuf (pixbuf, format);
939 940

  g_object_unref (pixbuf);
941

942
  return buf;
943 944
}

945 946 947
/**
 * gimp_viewable_get_pixbuf:
 * @viewable: The viewable object to get a pixbuf preview for.
948
 * @context:  The context to render the preview for.
949 950
 * @width:    desired width for the preview
 * @height:   desired height for the preview
951 952
 *
 * Gets a preview for a viewable object, by running through a variety
953 954 955 956 957 958 959 960
 * 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.
961 962
 *
 * Returns: A #GdkPixbuf containing the preview pixbuf, or #NULL if none can
963 964
 *          be found or created.
 **/
965
GdkPixbuf *
966
gimp_viewable_get_pixbuf (GimpViewable *viewable,
967
                          GimpContext  *context,
968 969
                          gint          width,
                          gint          height)
970
{
971 972 973
  GimpViewablePrivate *private;
  GimpViewableClass   *viewable_class;
  GdkPixbuf           *pixbuf = NULL;
974 975

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

980 981
  private = GET_PRIVATE (viewable);

982
  if (G_UNLIKELY (context == NULL))
983 984
    g_warning ("%s: context is NULL", G_STRFUNC);

985 986 987
  viewable_class = GIMP_VIEWABLE_GET_CLASS (viewable);

  if (viewable_class->get_pixbuf)
988
    pixbuf = viewable_class->get_pixbuf (viewable, context, width, height);
989 990 991 992

  if (pixbuf)
    return pixbuf;

993 994 995 996 997
  if (private->preview_pixbuf)
    {
      if (gdk_pixbuf_get_width  (private->preview_pixbuf) == width &&
          gdk_pixbuf_get_height (private->preview_pixbuf) == height)
        {
998
          return private->preview_pixbuf;
999
        }
1000

1001 1002 1003
      g_object_unref (private->preview_pixbuf);
      private->preview_pixbuf = NULL;
    }
1004

1005
  if (viewable_class->get_new_pixbuf)
1006
    pixbuf = viewable_class->get_new_pixbuf (viewable, context, width, height);
1007

1008
  private->preview_pixbuf = pixbuf;
1009 1010 1011 1012

  return pixbuf;
}

1013 1014 1015
/**
 * gimp_viewable_get_new_pixbuf:
 * @viewable: The viewable object to get a new pixbuf preview for.
1016
 * @context:  The context to render the preview for.
1017 1018
 * @width:    desired width for the pixbuf
 * @height:   desired height for the pixbuf
1019
 *
1020 1021 1022 1023 1024
 * 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.
1025 1026
 *
 * Returns: A #GdkPixbuf containing the preview, or #NULL if none can
1027 1028
 *          be created.
 **/
1029
GdkPixbuf *
1030
gimp_viewable_get_new_pixbuf (GimpViewable *viewable,
1031
                              GimpContext  *context,