gimppreview.c 27.8 KB
Newer Older
1 2 3 4 5
/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * gimppreview.c
 *
6
 * This library is free software: you can redistribute it and/or
7 8
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
9
 * version 3 of the License, or (at your option) any later version.
10 11 12 13 14 15 16
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library.  If not, see
18
 * <https://www.gnu.org/licenses/>.
19 20 21 22
 */

#include "config.h"

23
#include <gegl.h>
24 25
#include <gtk/gtk.h>

26 27
#include "libgimpmath/gimpmath.h"

28 29 30 31 32 33
#include "gimpwidgets.h"

#include "gimppreview.h"

#include "libgimp/libgimp-intl.h"

34

35 36 37 38 39 40 41 42 43 44 45
/**
 * SECTION: gimppreview
 * @title: GimpPreview
 * @short_description: A widget providing a #GimpPreviewArea plus
 *                     framework to update the preview.
 *
 * A widget providing a #GimpPreviewArea plus framework to update the
 * preview.
 **/


46
#define DEFAULT_SIZE     200
47 48 49
#define PREVIEW_TIMEOUT  200


50 51
enum
{
52
  INVALIDATED,
53 54 55 56 57 58
  LAST_SIGNAL
};

enum
{
  PROP_0,
59
  PROP_UPDATE
60 61
};

62 63

struct _GimpPreviewPrivate
64
{
65
  GtkWidget *area;
66
  GtkWidget *grid;
67 68
  GtkWidget *frame;
  GtkWidget *toggle;
69
  GtkWidget *controls;
70 71 72 73 74 75 76 77 78
  GdkCursor *cursor_busy;
  GdkCursor *default_cursor;

  gint       xoff, yoff;
  gint       xmin, xmax, ymin, ymax;
  gint       width, height;

  gboolean   update_preview;
  guint      timeout_id;
79
};
80

81
#define GET_PRIVATE(obj) (((GimpPreview *) (obj))->priv)
82

83

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
static void      gimp_preview_dispose             (GObject          *object);
static void      gimp_preview_get_property        (GObject          *object,
                                                   guint             property_id,
                                                   GValue           *value,
                                                   GParamSpec       *pspec);
static void      gimp_preview_set_property        (GObject          *object,
                                                   guint             property_id,
                                                   const GValue     *value,
                                                   GParamSpec       *pspec);

static void      gimp_preview_direction_changed   (GtkWidget        *widget,
                                                   GtkTextDirection  prev_dir);
static gboolean  gimp_preview_popup_menu          (GtkWidget        *widget);

static void      gimp_preview_area_realize        (GtkWidget        *widget,
                                                   GimpPreview      *preview);
static void      gimp_preview_area_unrealize      (GtkWidget        *widget,
                                                   GimpPreview      *preview);
static void      gimp_preview_area_size_allocate  (GtkWidget        *widget,
                                                   GtkAllocation    *allocation,
                                                   GimpPreview      *preview);
105
static void      gimp_preview_area_set_cursor     (GimpPreview      *preview);
106 107 108 109 110 111 112 113 114 115
static gboolean  gimp_preview_area_event          (GtkWidget        *area,
                                                   GdkEvent         *event,
                                                   GimpPreview      *preview);

static void      gimp_preview_toggle_callback     (GtkWidget        *toggle,
                                                   GimpPreview      *preview);

static void      gimp_preview_notify_checks       (GimpPreview      *preview);

static gboolean  gimp_preview_invalidate_now      (GimpPreview      *preview);
116
static void      gimp_preview_real_set_cursor     (GimpPreview      *preview);
117 118 119 120 121 122 123 124 125 126
static void      gimp_preview_real_transform      (GimpPreview      *preview,
                                                   gint              src_x,
                                                   gint              src_y,
                                                   gint             *dest_x,
                                                   gint             *dest_y);
static void      gimp_preview_real_untransform    (GimpPreview      *preview,
                                                   gint              src_x,
                                                   gint              src_y,
                                                   gint             *dest_x,
                                                   gint             *dest_y);
127

128

129
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpPreview, gimp_preview, GTK_TYPE_BOX)
130

131
#define parent_class gimp_preview_parent_class
132

133
static guint preview_signals[LAST_SIGNAL] = { 0 };
134

135 136 137 138

static void
gimp_preview_class_init (GimpPreviewClass *klass)
{
139 140
  GObjectClass   *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
141

142 143
  parent_class = g_type_class_peek_parent (klass);

144 145
  preview_signals[INVALIDATED] =
    g_signal_new ("invalidated",
146 147 148 149 150 151
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpPreviewClass, invalidated),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
152

153 154 155
  object_class->dispose           = gimp_preview_dispose;
  object_class->get_property      = gimp_preview_get_property;
  object_class->set_property      = gimp_preview_set_property;
156

157 158
  widget_class->direction_changed = gimp_preview_direction_changed;
  widget_class->popup_menu        = gimp_preview_popup_menu;
159

160
  klass->draw                     = NULL;
161
  klass->draw_thumb               = NULL;
162
  klass->draw_buffer              = NULL;
163
  klass->set_cursor               = gimp_preview_real_set_cursor;
164 165
  klass->transform                = gimp_preview_real_transform;
  klass->untransform              = gimp_preview_real_untransform;
166

167
  g_object_class_install_property (object_class,
168 169
                                   PROP_UPDATE,
                                   g_param_spec_boolean ("update",
170 171
                                                         "Update",
                                                         "Whether the preview should update automatically",
172
                                                         TRUE,
173
                                                         GIMP_PARAM_READWRITE |
174 175
                                                         G_PARAM_CONSTRUCT));

176 177
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_int ("size",
178 179
                                                             "Size",
                                                             "The preview's size",
180 181
                                                             1, 1024,
                                                             DEFAULT_SIZE,
182
                                                             GIMP_PARAM_READABLE));
183 184 185 186 187
}

static void
gimp_preview_init (GimpPreview *preview)
{
188
  GimpPreviewPrivate *priv;
189 190
  GtkWidget          *frame;
  gdouble             xalign = 0.0;
191

192
  preview->priv = gimp_preview_get_instance_private (preview);
193 194 195

  priv = preview->priv;

196 197 198
  gtk_orientable_set_orientation (GTK_ORIENTABLE (preview),
                                  GTK_ORIENTATION_VERTICAL);

199 200 201
  gtk_box_set_homogeneous (GTK_BOX (preview), FALSE);
  gtk_box_set_spacing (GTK_BOX (preview), 6);

202 203 204
  if (gtk_widget_get_direction (GTK_WIDGET (preview)) == GTK_TEXT_DIR_RTL)
    xalign = 1.0;

205 206 207 208
  priv->frame = gtk_aspect_frame_new (NULL, xalign, 0.0, 1.0, TRUE);
  gtk_frame_set_shadow_type (GTK_FRAME (priv->frame), GTK_SHADOW_NONE);
  gtk_box_pack_start (GTK_BOX (preview), priv->frame, TRUE, TRUE, 0);
  gtk_widget_show (priv->frame);
209

210 211 212
  priv->grid = gtk_grid_new ();
  gtk_container_add (GTK_CONTAINER (priv->frame), priv->grid);
  gtk_widget_show (priv->grid);
213

214
  priv->timeout_id = 0;
215

216 217 218 219
  priv->xmin   = priv->ymin = 0;
  priv->xmax   = priv->ymax = 1;
  priv->width  = priv->xmax - priv->xmin;
  priv->height = priv->ymax - priv->ymin;
220

221 222
  priv->xoff   = 0;
  priv->yoff   = 0;
223

224
  priv->default_cursor = NULL;
225

226
  /*  preview area  */
227 228
  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
229 230 231
  gtk_widget_set_hexpand (frame, TRUE);
  gtk_widget_set_vexpand (frame, TRUE);
  gtk_grid_attach (GTK_GRID (priv->grid), frame, 0, 0, 1, 1);
232 233
  gtk_widget_show (frame);

234 235 236
  priv->area = gimp_preview_area_new ();
  gtk_container_add (GTK_CONTAINER (frame), priv->area);
  gtk_widget_show (priv->area);
237

238
  g_signal_connect_swapped (priv->area, "notify::check-size",
239 240
                            G_CALLBACK (gimp_preview_notify_checks),
                            preview);
241
  g_signal_connect_swapped (priv->area, "notify::check-type",
242 243 244
                            G_CALLBACK (gimp_preview_notify_checks),
                            preview);

245
  gtk_widget_add_events (priv->area,
246 247
                         GDK_BUTTON_PRESS_MASK        |
                         GDK_BUTTON_RELEASE_MASK      |
248
                         GDK_SCROLL_MASK              |
249
                         GDK_SMOOTH_SCROLL_MASK       |
250 251
                         GDK_POINTER_MOTION_HINT_MASK |
                         GDK_BUTTON_MOTION_MASK);
252

253
  g_signal_connect (priv->area, "event",
254 255 256
                    G_CALLBACK (gimp_preview_area_event),
                    preview);

257
  g_signal_connect (priv->area, "realize",
258
                    G_CALLBACK (gimp_preview_area_realize),
259
                    preview);
260
  g_signal_connect (priv->area, "unrealize",
261 262
                    G_CALLBACK (gimp_preview_area_unrealize),
                    preview);
263

264
  g_signal_connect_data (priv->area, "realize",
265 266 267
                         G_CALLBACK (gimp_preview_area_set_cursor),
                         preview, NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);

268
  g_signal_connect (priv->area, "size-allocate",
269 270
                    G_CALLBACK (gimp_preview_area_size_allocate),
                    preview);
271

272
  g_signal_connect_data (priv->area, "size-allocate",
273 274 275
                         G_CALLBACK (gimp_preview_area_set_cursor),
                         preview, NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);

276
  priv->controls = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
277 278
  gtk_widget_set_margin_top (priv->controls, 3);
  gtk_grid_attach (GTK_GRID (priv->grid), priv->controls, 0, 2, 2, 1);
279
  gtk_widget_show (priv->controls);
280

Sven Neumann's avatar
Sven Neumann committed
281
  /*  toggle button to (de)activate the instant preview  */
282 283 284 285 286
  priv->toggle = gtk_check_button_new_with_mnemonic (_("_Preview"));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->toggle),
                                priv->update_preview);
  gtk_box_pack_start (GTK_BOX (priv->controls), priv->toggle, TRUE, TRUE, 0);
  gtk_widget_show (priv->toggle);
287

288
  g_signal_connect (priv->toggle, "toggled",
289 290
                    G_CALLBACK (gimp_preview_toggle_callback),
                    preview);
291 292
}

293 294 295
static void
gimp_preview_dispose (GObject *object)
{
296
  GimpPreviewPrivate *priv = GET_PRIVATE (object);
297

298
  if (priv->timeout_id)
299
    {
300 301
      g_source_remove (priv->timeout_id);
      priv->timeout_id = 0;
302 303 304 305 306
    }

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

307 308
static void
gimp_preview_get_property (GObject    *object,
309
                           guint       property_id,
310 311 312
                           GValue     *value,
                           GParamSpec *pspec)
{
313
  GimpPreviewPrivate *priv = GET_PRIVATE (object);
314

315
  switch (property_id)
316
    {
317
    case PROP_UPDATE:
318
      g_value_set_boolean (value, priv->update_preview);
319
      break;
320

321
    default:
322
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
323 324 325 326 327 328
      break;
    }
}

static void
gimp_preview_set_property (GObject      *object,
329
                           guint         property_id,
330 331 332
                           const GValue *value,
                           GParamSpec   *pspec)
{
333
  GimpPreviewPrivate *priv = GET_PRIVATE (object);
334

335
  switch (property_id)
336
    {
337
    case PROP_UPDATE:
338
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->toggle),
339 340
                                    g_value_get_boolean (value));
      break;
341

342
    default:
343
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
344 345 346 347
      break;
    }
}

348 349 350 351
static void
gimp_preview_direction_changed (GtkWidget        *widget,
                                GtkTextDirection  prev_dir)
{
352 353
  GimpPreviewPrivate *priv = GET_PRIVATE (widget);
  gdouble             xalign  = 0.0;
354 355 356 357

  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
    xalign = 1.0;

358
  gtk_aspect_frame_set (GTK_ASPECT_FRAME (priv->frame),
359 360 361
                        xalign, 0.0, 1.0, TRUE);
}

362 363 364
static gboolean
gimp_preview_popup_menu (GtkWidget *widget)
{
365
  GimpPreviewPrivate *priv = GET_PRIVATE (widget);
366

367
  gimp_preview_area_menu_popup (GIMP_PREVIEW_AREA (priv->area), NULL);
368 369 370 371

  return TRUE;
}

372
static void
373 374
gimp_preview_area_realize (GtkWidget   *widget,
                           GimpPreview *preview)
375
{
376 377
  GimpPreviewPrivate *priv    = GET_PRIVATE (preview);
  GdkDisplay         *display = gtk_widget_get_display (widget);
378

379
  g_return_if_fail (priv->cursor_busy == NULL);
380

381
  priv->cursor_busy = gdk_cursor_new_for_display (display, GDK_WATCH);
382

383 384 385 386 387 388
}

static void
gimp_preview_area_unrealize (GtkWidget   *widget,
                             GimpPreview *preview)
{
389 390 391
  GimpPreviewPrivate *priv = GET_PRIVATE (preview);

  g_clear_object (&priv->cursor_busy);
392
}
393

394
static void
395 396 397
gimp_preview_area_size_allocate (GtkWidget     *widget,
                                 GtkAllocation *allocation,
                                 GimpPreview   *preview)
398
{
399 400 401
  GimpPreviewPrivate *priv   = GET_PRIVATE (preview);
  gint                width  = priv->xmax - priv->xmin;
  gint                height = priv->ymax - priv->ymin;
402

403 404
  priv->width  = MIN (width,  allocation->width);
  priv->height = MIN (height, allocation->height);
405

406 407
  gimp_preview_draw (preview);
  gimp_preview_invalidate (preview);
408 409
}

410 411 412 413 414
static void
gimp_preview_area_set_cursor (GimpPreview *preview)
{
  GIMP_PREVIEW_GET_CLASS (preview)->set_cursor (preview);
}
415 416 417 418 419 420

static gboolean
gimp_preview_area_event (GtkWidget   *area,
                         GdkEvent    *event,
                         GimpPreview *preview)
{
421
  GdkEventButton *button_event = (GdkEventButton *) event;
422 423 424 425

  switch (event->type)
    {
    case GDK_BUTTON_PRESS:
426
      switch (button_event->button)
427
        {
428
        case 3:
429 430
          gimp_preview_area_menu_popup (GIMP_PREVIEW_AREA (area), button_event);
          return TRUE;
431
        }
432 433 434 435 436 437 438 439 440 441 442 443 444
      break;

    default:
      break;
    }

  return FALSE;
}

static void
gimp_preview_toggle_callback (GtkWidget   *toggle,
                              GimpPreview *preview)
{
445 446
  GimpPreviewPrivate *priv = GET_PRIVATE (preview);

447 448
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
    {
449
      priv->update_preview = TRUE;
450

451 452
      g_object_notify (G_OBJECT (preview), "update");

453 454
      if (priv->timeout_id)
        g_source_remove (priv->timeout_id);
455 456

      gimp_preview_invalidate_now (preview);
457 458
    }
  else
459
    {
460
      priv->update_preview = FALSE;
461

462 463
      g_object_notify (G_OBJECT (preview), "update");

464
      gimp_preview_draw (preview);
465
    }
466 467
}

468 469 470 471 472 473 474
static void
gimp_preview_notify_checks (GimpPreview *preview)
{
  gimp_preview_draw (preview);
  gimp_preview_invalidate (preview);
}

475 476 477
static gboolean
gimp_preview_invalidate_now (GimpPreview *preview)
{
478 479 480
  GimpPreviewPrivate *priv     = GET_PRIVATE (preview);
  GtkWidget          *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (preview));
  GimpPreviewClass   *class    = GIMP_PREVIEW_GET_CLASS (preview);
481

Sven Neumann's avatar
Sven Neumann committed
482
  gimp_preview_draw (preview);
483

484
  priv->timeout_id = 0;
485

486
  if (toplevel && gtk_widget_get_realized (toplevel))
487
    {
488
      gdk_window_set_cursor (gtk_widget_get_window (toplevel),
489 490 491
                             priv->cursor_busy);
      gdk_window_set_cursor (gtk_widget_get_window (priv->area),
                             priv->cursor_busy);
492

493
      gdk_display_flush (gtk_widget_get_display (toplevel));
494 495 496

      g_signal_emit (preview, preview_signals[INVALIDATED], 0);

497
      class->set_cursor (preview);
498
      gdk_window_set_cursor (gtk_widget_get_window (toplevel), NULL);
499 500 501 502 503
    }
  else
    {
      g_signal_emit (preview, preview_signals[INVALIDATED], 0);
    }
504 505 506 507

  return FALSE;
}

508
static void
509
gimp_preview_real_set_cursor (GimpPreview *preview)
510
{
511 512 513 514 515
  GimpPreviewPrivate *priv = GET_PRIVATE (preview);

  if (gtk_widget_get_realized (priv->area))
    gdk_window_set_cursor (gtk_widget_get_window (priv->area),
                           priv->default_cursor);
516 517 518 519 520 521 522 523 524
}

static void
gimp_preview_real_transform (GimpPreview *preview,
                             gint         src_x,
                             gint         src_y,
                             gint        *dest_x,
                             gint        *dest_y)
{
525 526 527 528
  GimpPreviewPrivate *priv = GET_PRIVATE (preview);

  *dest_x = src_x - priv->xoff - priv->xmin;
  *dest_y = src_y - priv->yoff - priv->ymin;
529 530 531 532 533 534 535 536 537
}

static void
gimp_preview_real_untransform (GimpPreview *preview,
                               gint         src_x,
                               gint         src_y,
                               gint        *dest_x,
                               gint        *dest_y)
{
538 539 540 541
  GimpPreviewPrivate *priv = GET_PRIVATE (preview);

  *dest_x = src_x + priv->xoff + priv->xmin;
  *dest_y = src_y + priv->yoff + priv->ymin;
542 543
}

544 545 546
/**
 * gimp_preview_set_update:
 * @preview: a #GimpPreview widget
547 548 549 550
 * @update: %TRUE if the preview should invalidate itself when being
 *          scrolled or when gimp_preview_invalidate() is being called
 *
 * Sets the state of the "Preview" check button.
551
 *
552
 * Since: 2.2
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
 **/
void
gimp_preview_set_update (GimpPreview *preview,
                         gboolean     update)
{
  g_return_if_fail (GIMP_IS_PREVIEW (preview));

  g_object_set (preview,
                "update", update,
                NULL);
}

/**
 * gimp_preview_get_update:
 * @preview: a #GimpPreview widget
 *
 * Return value: the state of the "Preview" check button.
 *
571
 * Since: 2.2
572 573 574 575 576 577
 **/
gboolean
gimp_preview_get_update (GimpPreview *preview)
{
  g_return_val_if_fail (GIMP_IS_PREVIEW (preview), FALSE);

578
  return GET_PRIVATE (preview)->update_preview;
579 580
}

581 582 583
/**
 * gimp_preview_set_bounds:
 * @preview: a #GimpPreview widget
584 585 586 587
 * @xmin:    the minimum X value
 * @ymin:    the minimum Y value
 * @xmax:    the maximum X value
 * @ymax:    the maximum Y value
588
 *
589 590 591 592
 * Sets the lower and upper limits for the previewed area. The
 * difference between the upper and lower value is used to set the
 * maximum size of the #GimpPreviewArea used in the @preview.
 *
593
 * Since: 2.2
594 595 596 597 598 599 600 601
 **/
void
gimp_preview_set_bounds (GimpPreview *preview,
                         gint         xmin,
                         gint         ymin,
                         gint         xmax,
                         gint         ymax)
{
602 603
  GimpPreviewPrivate *priv;

604 605 606 607
  g_return_if_fail (GIMP_IS_PREVIEW (preview));
  g_return_if_fail (xmax > xmin);
  g_return_if_fail (ymax > ymin);

608
  priv = GET_PRIVATE (preview);
609

610 611 612 613 614 615
  priv->xmin = xmin;
  priv->ymin = ymin;
  priv->xmax = xmax;
  priv->ymax = ymax;

  gimp_preview_area_set_max_size (GIMP_PREVIEW_AREA (priv->area),
616 617 618 619
                                  xmax - xmin,
                                  ymax - ymin);
}

620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
void
gimp_preview_get_bounds (GimpPreview *preview,
                         gint        *xmin,
                         gint        *ymin,
                         gint        *xmax,
                         gint        *ymax)
{
  GimpPreviewPrivate *priv;

  g_return_if_fail (GIMP_IS_PREVIEW (preview));

  priv = GET_PRIVATE (preview);

  if (xmin) *xmin = priv->xmin;
  if (ymin) *ymin = priv->ymin;
  if (xmax) *xmax = priv->xmax;
  if (ymax) *ymax = priv->ymax;
}

void
gimp_preview_set_size (GimpPreview *preview,
                       gint         width,
                       gint         height)
{
  GimpPreviewPrivate *priv;

  g_return_if_fail (GIMP_IS_PREVIEW (preview));

  priv = GET_PRIVATE (preview);

  priv->width  = width;
  priv->height = height;

  gtk_widget_set_size_request (priv->area, width, height);
}

656
/**
657
 * gimp_preview_get_size:
658
 * @preview: a #GimpPreview widget
659 660
 * @width:   return location for the preview area width
 * @height:  return location for the preview area height
661
 *
662
 * Since: 2.2
663
 **/
664 665 666 667
void
gimp_preview_get_size (GimpPreview *preview,
                       gint        *width,
                       gint        *height)
668
{
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
  GimpPreviewPrivate *priv;

  g_return_if_fail (GIMP_IS_PREVIEW (preview));

  priv = GET_PRIVATE (preview);

  if (width)  *width  = priv->width;
  if (height) *height = priv->height;
}

void
gimp_preview_set_offsets (GimpPreview *preview,
                          gint         xoff,
                          gint         yoff)
{
  GimpPreviewPrivate *priv;

  g_return_if_fail (GIMP_IS_PREVIEW (preview));

  priv = GET_PRIVATE (preview);

  priv->xoff = xoff;
  priv->yoff = yoff;
}

void
gimp_preview_get_offsets (GimpPreview *preview,
                          gint        *xoff,
                          gint        *yoff)
{
  GimpPreviewPrivate *priv;

701
  g_return_if_fail (GIMP_IS_PREVIEW (preview));
702

703
  priv = GET_PRIVATE (preview);
704

705 706
  if (xoff) *xoff = priv->xoff;
  if (yoff) *yoff = priv->yoff;
707 708
}

709 710 711 712 713 714
/**
 * gimp_preview_get_position:
 * @preview: a #GimpPreview widget
 * @x:       return location for the horizontal offset
 * @y:       return location for the vertical offset
 *
715
 * Since: 2.2
716 717 718 719 720 721
 **/
void
gimp_preview_get_position (GimpPreview *preview,
                           gint        *x,
                           gint        *y)
{
722 723
  GimpPreviewPrivate *priv;

724 725
  g_return_if_fail (GIMP_IS_PREVIEW (preview));

726
  priv = GET_PRIVATE (preview);
727

728 729
  if (x) *x = priv->xoff + priv->xmin;
  if (y) *y = priv->yoff + priv->ymin;
730 731
}

732 733 734 735 736 737 738 739 740 741
/**
 * gimp_preview_transform:
 * @preview: a #GimpPreview widget
 * @src_x:   horizontal position on the previewed image
 * @src_y:   vertical position on the previewed image
 * @dest_x:  returns the transformed horizontal position
 * @dest_y:  returns the transformed vertical position
 *
 * Transforms from image to widget coordinates.
 *
742
 * Since: 2.4
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
 **/
void
gimp_preview_transform (GimpPreview *preview,
                        gint         src_x,
                        gint         src_y,
                        gint        *dest_x,
                        gint        *dest_y)
{
  g_return_if_fail (GIMP_IS_PREVIEW (preview));
  g_return_if_fail (dest_x != NULL && dest_y != NULL);

  GIMP_PREVIEW_GET_CLASS (preview)->transform (preview,
                                               src_x, src_y, dest_x, dest_y);
}

/**
 * gimp_preview_untransform:
 * @preview: a #GimpPreview widget
 * @src_x:   horizontal position relative to the preview area's origin
 * @src_y:   vertical position relative to  preview area's origin
 * @dest_x:  returns the untransformed horizontal position
 * @dest_y:  returns the untransformed vertical position
 *
 * Transforms from widget to image coordinates.
 *
768
 * Since: 2.4
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
 **/
void
gimp_preview_untransform (GimpPreview *preview,
                          gint         src_x,
                          gint         src_y,
                          gint        *dest_x,
                          gint        *dest_y)
{
  g_return_if_fail (GIMP_IS_PREVIEW (preview));
  g_return_if_fail (dest_x != NULL && dest_y != NULL);

  GIMP_PREVIEW_GET_CLASS (preview)->untransform (preview,
                                                 src_x, src_y, dest_x, dest_y);
}

784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
/**
 * gimp_preview_get_frame:
 * @preview: a #GimpPreview widget
 *
 * Return value: a pointer to the #GtkAspectFrame used in the @preview.
 *
 * Since: 3.0
 **/
GtkWidget *
gimp_preview_get_frame (GimpPreview  *preview)
{
  g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL);

  return GET_PRIVATE (preview)->frame;
}

/**
801
 * gimp_preview_get_grid:
802 803
 * @preview: a #GimpPreview widget
 *
804
 * Return value: a pointer to the #GtkGrid used in the @preview.
805 806 807 808
 *
 * Since: 3.0
 **/
GtkWidget *
809
gimp_preview_get_grid (GimpPreview  *preview)
810 811 812
{
  g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL);

813
  return GET_PRIVATE (preview)->grid;
814 815
}

816 817 818 819 820 821 822 823 824 825 826
/**
 * gimp_preview_get_area:
 * @preview: a #GimpPreview widget
 *
 * In most cases, you shouldn't need to access the #GimpPreviewArea
 * that is being used in the @preview. Sometimes however, you need to.
 * For example if you want to receive mouse events from the area. In
 * such cases, use gimp_preview_get_area().
 *
 * Return value: a pointer to the #GimpPreviewArea used in the @preview.
 *
827
 * Since: 2.4
828 829 830 831 832 833
 **/
GtkWidget *
gimp_preview_get_area (GimpPreview  *preview)
{
  g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL);

834
  return GET_PRIVATE (preview)->area;
835 836
}

837
/**
838 839 840 841 842 843 844 845 846 847
 * gimp_preview_draw:
 * @preview: a #GimpPreview widget
 *
 * Calls the GimpPreview::draw method. GimpPreview itself doesn't
 * implement a default draw method so the behaviour is determined by
 * the derived class implementing this method.
 *
 * #GimpDrawablePreview implements gimp_preview_draw() by drawing the
 * original, unmodified drawable to the @preview.
 *
848
 * Since: 2.2
849 850 851 852 853 854 855 856 857 858
 **/
void
gimp_preview_draw (GimpPreview *preview)
{
  GimpPreviewClass *class = GIMP_PREVIEW_GET_CLASS (preview);

  if (class->draw)
    class->draw (preview);
}

859
/**
860 861 862 863 864 865 866 867 868
 * gimp_preview_draw_buffer:
 * @preview:   a #GimpPreview widget
 * @buffer:    a pixel buffer the size of the preview
 * @rowstride: the @buffer's rowstride
 *
 * Calls the GimpPreview::draw_buffer method. GimpPreview itself
 * doesn't implement this method so the behaviour is determined by the
 * derived class implementing this method.
 *
869
 * Since: 2.2
870 871 872 873 874 875 876 877 878 879 880 881
 **/
void
gimp_preview_draw_buffer (GimpPreview  *preview,
                          const guchar *buffer,
                          gint          rowstride)
{
  GimpPreviewClass *class = GIMP_PREVIEW_GET_CLASS (preview);

  if (class->draw_buffer)
    class->draw_buffer (preview, buffer, rowstride);
}

882
/**
883
 * gimp_preview_invalidate:
884
 * @preview: a #GimpPreview widget
885
 *
886 887 888 889 890 891 892 893 894 895
 * This function starts or renews a short low-priority timeout. When
 * the timeout expires, the GimpPreview::invalidated signal is emitted
 * which will usually cause the @preview to be updated.
 *
 * This function does nothing unless the "Preview" button is checked.
 *
 * During the emission of the signal a busy cursor is set on the
 * toplevel window containing the @preview and on the preview area
 * itself.
 *
896
 * Since: 2.2
897 898 899 900
 **/
void
gimp_preview_invalidate (GimpPreview *preview)
{
901 902
  GimpPreviewPrivate *priv;

903 904
  g_return_if_fail (GIMP_IS_PREVIEW (preview));

905 906 907
  priv = GET_PRIVATE (preview);

  if (priv->update_preview)
908
    {
909 910
      if (priv->timeout_id)
        g_source_remove (priv->timeout_id);
911

912
      priv->timeout_id =
913 914 915 916 917
        g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, PREVIEW_TIMEOUT,
                            (GSourceFunc) gimp_preview_invalidate_now,
                            preview, NULL);
    }
}
918 919 920 921

/**
 * gimp_preview_set_default_cursor:
 * @preview: a #GimpPreview widget
922 923 924
 * @cursor:  a #GdkCursor or %NULL
 *
 * Sets the default mouse cursor for the preview.  Note that this will
925
 * be overridden by a %GDK_FLEUR if the preview has scrollbars, or by a
926
 * %GDK_WATCH when the preview is invalidated.
927
 *
928
 * Since: 2.2
929 930 931 932 933
 **/
void
gimp_preview_set_default_cursor (GimpPreview *preview,
                                 GdkCursor   *cursor)
{
934 935
  GimpPreviewPrivate *priv;

936 937
  g_return_if_fail (GIMP_IS_PREVIEW (preview));

938 939
  priv = GET_PRIVATE (preview);

940
  g_set_object (&priv->default_cursor, cursor);
941 942 943 944 945 946
}

GdkCursor *
gimp_preview_get_default_cursor (GimpPreview *preview)
{
  g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL);
947

948
  return GET_PRIVATE (preview)->default_cursor;
949 950
}

951
/**
952
 * gimp_preview_get_controls:
953 954 955 956 957 958 959 960
 * @preview: a #GimpPreview widget
 *
 * Gives access to the #GtkHBox at the bottom of the preview that
 * contains the update toggle. Derived widgets can use this function
 * if they need to add controls to this area.
 *
 * Return value: the #GtkHBox at the bottom of the preview.
 *
961
 * Since: 2.4
962 963
 **/
GtkWidget *
964
gimp_preview_get_controls (GimpPreview *preview)
965 966 967
{
  g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL);

968
  return GET_PRIVATE (preview)->controls;
969
}