gimpdisplayshell.c 60.5 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4
 * This program is free software: you can redistribute it and/or modify
Elliot Lee's avatar
Elliot Lee committed
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
Elliot Lee's avatar
Elliot Lee committed
7 8 9 10 11 12 13 14
 * (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
15
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
Sven Neumann's avatar
Sven Neumann committed
17

18 19
#include "config.h"

20 21
#include <string.h>

22
#include <gegl.h>
Sven Neumann's avatar
Sven Neumann committed
23 24
#include <gtk/gtk.h>

25
#include "libgimpbase/gimpbase.h"
26
#include "libgimpmath/gimpmath.h"
27
#include "libgimpcolor/gimpcolor.h"
28
#include "libgimpconfig/gimpconfig.h"
29 30
#include "libgimpwidgets/gimpwidgets.h"

31
#include "display-types.h"
32
#include "tools/tools-types.h"
Sven Neumann's avatar
Sven Neumann committed
33

34
#include "config/gimpcoreconfig.h"
35
#include "config/gimpdisplayconfig.h"
36
#include "config/gimpdisplayoptions.h"
37

38 39
#include "gegl/gimp-babl.h"

40
#include "core/gimp.h"
41
#include "core/gimp-utils.h"
42
#include "core/gimpchannel.h"
43
#include "core/gimpcontext.h"
44
#include "core/gimpimage.h"
45 46
#include "core/gimpimage-grid.h"
#include "core/gimpimage-guides.h"
47
#include "core/gimpimage-snap.h"
48
#include "core/gimpprojection.h"
49
#include "core/gimpmarshal.h"
50
#include "core/gimptemplate.h"
51

52
#include "widgets/gimpdevices.h"
53
#include "widgets/gimphelp-ids.h"
54
#include "widgets/gimpuimanager.h"
55
#include "widgets/gimpwidgets-utils.h"
56

57 58
#include "tools/tool_manager.h"

59
#include "gimpcanvas.h"
60
#include "gimpcanvaslayerboundary.h"
61 62
#include "gimpdisplay.h"
#include "gimpdisplayshell.h"
63
#include "gimpdisplayshell-appearance.h"
64
#include "gimpdisplayshell-callbacks.h"
65
#include "gimpdisplayshell-cursor.h"
66
#include "gimpdisplayshell-dnd.h"
67
#include "gimpdisplayshell-expose.h"
68
#include "gimpdisplayshell-filter.h"
69
#include "gimpdisplayshell-handlers.h"
70
#include "gimpdisplayshell-items.h"
71
#include "gimpdisplayshell-profile.h"
72
#include "gimpdisplayshell-progress.h"
73
#include "gimpdisplayshell-render.h"
74
#include "gimpdisplayshell-rotate.h"
75
#include "gimpdisplayshell-rulers.h"
76
#include "gimpdisplayshell-scale.h"
77
#include "gimpdisplayshell-scroll.h"
78
#include "gimpdisplayshell-selection.h"
79
#include "gimpdisplayshell-title.h"
80
#include "gimpdisplayshell-tool-events.h"
81
#include "gimpdisplayshell-transform.h"
82
#include "gimpimagewindow.h"
83
#include "gimpmotionbuffer.h"
84
#include "gimpstatusbar.h"
85

86
#include "about.h"
87
#include "gimp-log.h"
88
#include "gimp-priorities.h"
89

90
#include "gimp-intl.h"
91

92

93 94 95
enum
{
  PROP_0,
96
  PROP_POPUP_MANAGER,
97
  PROP_INITIAL_MONITOR,
98
  PROP_DISPLAY,
99 100
  PROP_UNIT,
  PROP_TITLE,
101 102
  PROP_STATUS,
  PROP_ICON
103 104
};

105 106 107 108
enum
{
  SCALED,
  SCROLLED,
109
  ROTATED,
110
  RECONNECT,
111 112 113
  LAST_SIGNAL
};

114

115 116 117 118
typedef struct _GimpDisplayShellOverlay GimpDisplayShellOverlay;

struct _GimpDisplayShellOverlay
{
119 120 121 122 123 124
  GimpDisplayShell *shell;
  gdouble           image_x;
  gdouble           image_y;
  GimpHandleAnchor  anchor;
  gint              spacing_x;
  gint              spacing_y;
125 126 127
};


128
/*  local function prototypes  */
129

130
static void      gimp_color_managed_iface_init     (GimpColorManagedInterface *iface);
131

132
static void      gimp_display_shell_constructed    (GObject          *object);
133
static void      gimp_display_shell_dispose        (GObject          *object);
134 135 136 137 138 139 140 141 142 143
static void      gimp_display_shell_finalize       (GObject          *object);
static void      gimp_display_shell_set_property   (GObject          *object,
                                                    guint             property_id,
                                                    const GValue     *value,
                                                    GParamSpec       *pspec);
static void      gimp_display_shell_get_property   (GObject          *object,
                                                    guint             property_id,
                                                    GValue           *value,
                                                    GParamSpec       *pspec);

144
static void      gimp_display_shell_unrealize      (GtkWidget        *widget);
145
static void      gimp_display_shell_unmap          (GtkWidget        *widget);
146 147
static void      gimp_display_shell_screen_changed (GtkWidget        *widget,
                                                    GdkScreen        *previous);
148
static gboolean  gimp_display_shell_popup_menu     (GtkWidget        *widget);
149 150

static void      gimp_display_shell_real_scaled    (GimpDisplayShell *shell);
151
static void      gimp_display_shell_real_scrolled  (GimpDisplayShell *shell);
152
static void      gimp_display_shell_real_rotated   (GimpDisplayShell *shell);
153

154 155
static const guint8 *
                 gimp_display_shell_get_icc_profile(GimpColorManaged *managed,
156
                                                    gsize            *len);
157
static GimpColorProfile *
158 159
               gimp_display_shell_get_color_profile(GimpColorManaged *managed);
static void      gimp_display_shell_profile_changed(GimpColorManaged *managed);
160 161 162 163 164 165

static void      gimp_display_shell_zoom_button_callback
                                                   (GimpDisplayShell *shell,
                                                    GtkWidget        *zoom_button);
static void      gimp_display_shell_sync_config    (GimpDisplayShell  *shell,
                                                    GimpDisplayConfig *config);
166

167 168 169
static void    gimp_display_shell_overlay_allocate (GtkWidget        *child,
                                                    GtkAllocation    *allocation,
                                                    GimpDisplayShellOverlay *overlay);
170 171 172
static void      gimp_display_shell_remove_overlay (GtkWidget        *canvas,
                                                    GtkWidget        *child,
                                                    GimpDisplayShell *shell);
173 174 175 176
static void   gimp_display_shell_transform_overlay (GimpDisplayShell *shell,
                                                    GtkWidget        *child,
                                                    gdouble          *x,
                                                    gdouble          *y);
177

178

179
G_DEFINE_TYPE_WITH_CODE (GimpDisplayShell, gimp_display_shell,
180
                         GTK_TYPE_EVENT_BOX,
181
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS,
182 183 184 185
                                                gimp_display_shell_progress_iface_init)
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_COLOR_MANAGED,
                                                gimp_color_managed_iface_init))

186 187 188 189

#define parent_class gimp_display_shell_parent_class

static guint display_shell_signals[LAST_SIGNAL] = { 0 };
190

Elliot Lee's avatar
Elliot Lee committed
191

192 193 194
static void
gimp_display_shell_class_init (GimpDisplayShellClass *klass)
{
195 196
  GObjectClass   *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
197

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
  display_shell_signals[SCALED] =
    g_signal_new ("scaled",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpDisplayShellClass, scaled),
                  NULL, NULL,
                  gimp_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);

  display_shell_signals[SCROLLED] =
    g_signal_new ("scrolled",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpDisplayShellClass, scrolled),
                  NULL, NULL,
                  gimp_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);

216 217 218 219 220 221 222 223 224
  display_shell_signals[ROTATED] =
    g_signal_new ("rotated",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpDisplayShellClass, rotated),
                  NULL, NULL,
                  gimp_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);

225 226 227 228 229 230 231 232 233
  display_shell_signals[RECONNECT] =
    g_signal_new ("reconnect",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GimpDisplayShellClass, reconnect),
                  NULL, NULL,
                  gimp_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);

234
  object_class->constructed        = gimp_display_shell_constructed;
235
  object_class->dispose            = gimp_display_shell_dispose;
236 237 238
  object_class->finalize           = gimp_display_shell_finalize;
  object_class->set_property       = gimp_display_shell_set_property;
  object_class->get_property       = gimp_display_shell_get_property;
239

240
  widget_class->unrealize          = gimp_display_shell_unrealize;
241
  widget_class->unmap              = gimp_display_shell_unmap;
242 243
  widget_class->screen_changed     = gimp_display_shell_screen_changed;
  widget_class->popup_menu         = gimp_display_shell_popup_menu;
244

245
  klass->scaled                    = gimp_display_shell_real_scaled;
246
  klass->scrolled                  = gimp_display_shell_real_scrolled;
247
  klass->rotated                   = gimp_display_shell_real_rotated;
248
  klass->reconnect                 = NULL;
249

250 251 252 253 254 255 256
  g_object_class_install_property (object_class, PROP_POPUP_MANAGER,
                                   g_param_spec_object ("popup-manager",
                                                        NULL, NULL,
                                                        GIMP_TYPE_UI_MANAGER,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT_ONLY));

257 258
  g_object_class_install_property (object_class, PROP_INITIAL_MONITOR,
                                   g_param_spec_object ("initial-monitor",
259
                                                        NULL, NULL,
260
                                                        GDK_TYPE_MONITOR,
261 262 263
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT_ONLY));

264 265 266 267 268 269
  g_object_class_install_property (object_class, PROP_DISPLAY,
                                   g_param_spec_object ("display", NULL, NULL,
                                                        GIMP_TYPE_DISPLAY,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT_ONLY));

270
  g_object_class_install_property (object_class, PROP_UNIT,
271 272 273
                                   gimp_param_spec_unit ("unit", NULL, NULL,
                                                         TRUE, FALSE,
                                                         GIMP_UNIT_PIXEL,
274
                                                         GIMP_PARAM_READWRITE));
275

276
  g_object_class_install_property (object_class, PROP_TITLE,
277
                                   g_param_spec_string ("title", NULL, NULL,
278 279 280
                                                        GIMP_NAME,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
281 282 283 284 285 286

  g_object_class_install_property (object_class, PROP_STATUS,
                                   g_param_spec_string ("status", NULL, NULL,
                                                        NULL,
                                                        GIMP_PARAM_READWRITE));

287
  g_object_class_install_property (object_class, PROP_ICON,
288
                                   g_param_spec_object ("icon", NULL, NULL,
289 290 291
                                                        GDK_TYPE_PIXBUF,
                                                        GIMP_PARAM_READWRITE));

292
  gtk_widget_class_set_css_name (widget_class, "GimpDisplayShell");
293 294
}

295 296 297
static void
gimp_color_managed_iface_init (GimpColorManagedInterface *iface)
{
298 299 300
  iface->get_icc_profile   = gimp_display_shell_get_icc_profile;
  iface->get_color_profile = gimp_display_shell_get_color_profile;
  iface->profile_changed   = gimp_display_shell_profile_changed;
301 302
}

303 304 305
static void
gimp_display_shell_init (GimpDisplayShell *shell)
{
306 307 308 309 310 311 312 313 314
  shell->options            = g_object_new (GIMP_TYPE_DISPLAY_OPTIONS, NULL);
  shell->fullscreen_options = g_object_new (GIMP_TYPE_DISPLAY_OPTIONS_FULLSCREEN, NULL);
  shell->no_image_options   = g_object_new (GIMP_TYPE_DISPLAY_OPTIONS_NO_IMAGE, NULL);

  shell->zoom        = gimp_zoom_model_new ();
  shell->dot_for_dot = TRUE;
  shell->scale_x     = 1.0;
  shell->scale_y     = 1.0;

315
  gimp_display_shell_items_init (shell);
316

317 318
  shell->icon_size       = 128;
  shell->icon_size_small = 96;
319

320 321 322 323 324
  shell->cursor_handedness = GIMP_HANDEDNESS_RIGHT;
  shell->current_cursor    = (GimpCursorType) -1;
  shell->tool_cursor       = GIMP_TOOL_CURSOR_NONE;
  shell->cursor_modifier   = GIMP_CURSOR_MODIFIER_NONE;
  shell->override_cursor   = (GimpCursorType) -1;
325

326
  shell->filter_format     = babl_format ("R'G'B'A float");
327 328
  shell->filter_profile    = gimp_babl_get_builtin_color_profile (GIMP_RGB,
                                                                  GIMP_TRC_NON_LINEAR);
329

330
  shell->motion_buffer   = gimp_motion_buffer_new ();
331

332 333 334 335 336
  g_signal_connect (shell->motion_buffer, "stroke",
                    G_CALLBACK (gimp_display_shell_buffer_stroke),
                    shell);
  g_signal_connect (shell->motion_buffer, "hover",
                    G_CALLBACK (gimp_display_shell_buffer_hover),
337 338
                    shell);

339 340
  shell->zoom_focus_pointer_queue = g_queue_new ();

341 342 343 344 345 346
  gtk_widget_set_events (GTK_WIDGET (shell), (GDK_POINTER_MOTION_MASK    |
                                              GDK_BUTTON_PRESS_MASK      |
                                              GDK_KEY_PRESS_MASK         |
                                              GDK_KEY_RELEASE_MASK       |
                                              GDK_FOCUS_CHANGE_MASK      |
                                              GDK_VISIBILITY_NOTIFY_MASK |
347 348
                                              GDK_SCROLL_MASK            |
                                              GDK_SMOOTH_SCROLL_MASK));
349

350 351
  /*  zoom model callback  */
  g_signal_connect_swapped (shell->zoom, "zoomed",
352
                            G_CALLBACK (gimp_display_shell_scale_update),
353 354
                            shell);

355
  /*  active display callback  */
356
  g_signal_connect (shell, "button-press-event",
357 358
                    G_CALLBACK (gimp_display_shell_events),
                    shell);
359
  g_signal_connect (shell, "button-release-event",
360 361
                    G_CALLBACK (gimp_display_shell_events),
                    shell);
362
  g_signal_connect (shell, "key-press-event",
363 364
                    G_CALLBACK (gimp_display_shell_events),
                    shell);
365

366
  gimp_help_connect (GTK_WIDGET (shell), gimp_standard_help_func,
367
                     GIMP_HELP_IMAGE_WINDOW, NULL);
368 369
}

370 371
static void
gimp_display_shell_constructed (GObject *object)
372
{
373
  GimpDisplayShell      *shell = GIMP_DISPLAY_SHELL (object);
374 375 376
  GimpDisplayConfig     *config;
  GimpImage             *image;
  GimpColorDisplayStack *filter;
377
  GtkWidget             *grid;
378 379 380 381 382 383
  GtkWidget             *gtk_image;
  GtkAction             *action;
  gint                   image_width;
  gint                   image_height;
  gint                   shell_width;
  gint                   shell_height;
384

385
  G_OBJECT_CLASS (parent_class)->constructed (object);
386

387 388
  gimp_assert (GIMP_IS_UI_MANAGER (shell->popup_manager));
  gimp_assert (GIMP_IS_DISPLAY (shell->display));
389

390 391
  config = shell->display->config;
  image  = gimp_display_get_image (shell->display);
392

393
  gimp_display_shell_profile_init (shell);
394

395
  if (image)
396
    {
397 398
      image_width  = gimp_image_get_width  (image);
      image_height = gimp_image_get_height (image);
399
    }
400
  else
401
    {
402 403 404 405 406 407
      /* These values are arbitrary. The width is determined by the
       * menubar and the height is chosen to give a window aspect
       * ratio of roughly 3:1 (as requested by the UI team).
       */
      image_width  = GIMP_DEFAULT_IMAGE_WIDTH;
      image_height = GIMP_DEFAULT_IMAGE_HEIGHT / 3;
408
    }
409

410
  shell->dot_for_dot = config->default_dot_for_dot;
411

412
  if (config->monitor_res_from_gdk)
413
    {
414
      gimp_get_monitor_resolution (shell->initial_monitor,
415
                                   &shell->monitor_xres, &shell->monitor_yres);
416
    }
417
  else
418
    {
419 420
      shell->monitor_xres = config->monitor_xres;
      shell->monitor_yres = config->monitor_yres;
421 422
    }

423 424
  /* adjust the initial scale -- so that window fits on screen. */
  if (image)
425
    {
426 427
      gimp_display_shell_set_initial_scale (shell, 1.0, //scale,
                                            &shell_width, &shell_height);
428
    }
429
  else
430
    {
431 432
      shell_width  = -1;
      shell_height = image_height;
433 434
    }

435
  gimp_display_shell_sync_config (shell, config);
436

437 438 439 440
  /*  the grid containing everything  */
  grid = gtk_grid_new ();
  gtk_container_add (GTK_CONTAINER (shell), grid);
  gtk_widget_show (grid);
441

442
  /*  the horizontal scrollbar  */
443 444
  shell->hsbdata = gtk_adjustment_new (0, 0, image_width,
                                       1, 1, image_width);
445
  shell->hsb = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, shell->hsbdata);
446
  gtk_widget_set_can_focus (shell->hsb, FALSE);
447

448
  /*  the vertical scrollbar  */
449 450
  shell->vsbdata = gtk_adjustment_new (0, 0, image_height,
                                       1, 1, image_height);
451
  shell->vsb = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, shell->vsbdata);
452
  gtk_widget_set_can_focus (shell->vsb, FALSE);
453

454 455
  /*  the menu popup button  */
  shell->origin = gtk_event_box_new ();
456
  gtk_image = gtk_image_new_from_icon_name (GIMP_ICON_MENU_RIGHT,
457
                                            GTK_ICON_SIZE_MENU);
458 459
  gtk_container_add (GTK_CONTAINER (shell->origin), gtk_image);
  gtk_widget_show (gtk_image);
460

461 462 463
  g_signal_connect (shell->origin, "button-press-event",
                    G_CALLBACK (gimp_display_shell_origin_button_press),
                    shell);
464

465 466 467
  gimp_help_set_help_data (shell->origin,
                           _("Access the image menu"),
                           GIMP_HELP_IMAGE_WINDOW_ORIGIN);
468

469
  /*  the canvas  */
470 471 472
  shell->canvas = gimp_canvas_new (config);
  gtk_widget_set_size_request (shell->canvas, shell_width, shell_height);
  gtk_container_set_border_width (GTK_CONTAINER (shell->canvas), 10);
473

474 475 476 477
  g_signal_connect (shell->canvas, "remove",
                    G_CALLBACK (gimp_display_shell_remove_overlay),
                    shell);

478 479
  gimp_display_shell_dnd_init (shell);
  gimp_display_shell_selection_init (shell);
480

481 482 483
  /*  the horizontal ruler  */
  shell->hrule = gimp_ruler_new (GTK_ORIENTATION_HORIZONTAL);
  gtk_widget_set_events (GTK_WIDGET (shell->hrule),
484 485
                         GDK_BUTTON_PRESS_MASK |
                         GDK_BUTTON_RELEASE_MASK);
486
  gimp_ruler_add_track_widget (GIMP_RULER (shell->hrule), shell->canvas);
487

488 489 490
  g_signal_connect (shell->hrule, "button-press-event",
                    G_CALLBACK (gimp_display_shell_hruler_button_press),
                    shell);
491

492
  gimp_help_set_help_data (shell->hrule, NULL, GIMP_HELP_IMAGE_WINDOW_RULER);
493

494 495 496
  /*  the vertical ruler  */
  shell->vrule = gimp_ruler_new (GTK_ORIENTATION_VERTICAL);
  gtk_widget_set_events (GTK_WIDGET (shell->vrule),
497 498
                         GDK_BUTTON_PRESS_MASK |
                         GDK_BUTTON_RELEASE_MASK);
499
  gimp_ruler_add_track_widget (GIMP_RULER (shell->vrule), shell->canvas);
500

501 502 503
  g_signal_connect (shell->vrule, "button-press-event",
                    G_CALLBACK (gimp_display_shell_vruler_button_press),
                    shell);
504

505 506
  gimp_help_set_help_data (shell->vrule, NULL, GIMP_HELP_IMAGE_WINDOW_RULER);

507 508 509 510 511 512 513
  /*  set the rulers as track widgets for each other, so we don't end up
   *  with one ruler wrongly being stuck a few pixels off while we are
   *  hovering the other
   */
  gimp_ruler_add_track_widget (GIMP_RULER (shell->hrule), shell->vrule);
  gimp_ruler_add_track_widget (GIMP_RULER (shell->vrule), shell->hrule);

514 515
  gimp_devices_add_widget (shell->display->gimp, shell->hrule);
  gimp_devices_add_widget (shell->display->gimp, shell->vrule);
516

517 518 519 520
  g_signal_connect (shell->canvas, "grab-notify",
                    G_CALLBACK (gimp_display_shell_canvas_grab_notify),
                    shell);

521 522 523 524 525 526
  g_signal_connect (shell->canvas, "realize",
                    G_CALLBACK (gimp_display_shell_canvas_realize),
                    shell);
  g_signal_connect (shell->canvas, "size-allocate",
                    G_CALLBACK (gimp_display_shell_canvas_size_allocate),
                    shell);
527 528
  g_signal_connect (shell->canvas, "draw",
                    G_CALLBACK (gimp_display_shell_canvas_draw),
529
                    shell);
530

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
  g_signal_connect (shell->canvas, "enter-notify-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "leave-notify-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "proximity-in-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "proximity-out-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "focus-in-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "focus-out-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "button-press-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "button-release-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "scroll-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "motion-notify-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "key-press-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
  g_signal_connect (shell->canvas, "key-release-event",
                    G_CALLBACK (gimp_display_shell_canvas_tool_events),
                    shell);
567

568
  /*  the zoom button  */
569 570 571 572 573 574 575
  shell->zoom_button = g_object_new (GTK_TYPE_CHECK_BUTTON,
                                     "draw-indicator", FALSE,
                                     "relief",         GTK_RELIEF_NONE,
                                     "width-request",  18,
                                     "height-request", 18,
                                     NULL);
  gtk_widget_set_can_focus (shell->zoom_button, FALSE);
576
  gtk_image = gtk_image_new_from_icon_name (GIMP_ICON_ZOOM_FOLLOW_WINDOW,
577
                                            GTK_ICON_SIZE_MENU);
578 579
  gtk_container_add (GTK_CONTAINER (shell->zoom_button), gtk_image);
  gtk_widget_show (gtk_image);
Martin Nordholts's avatar
Martin Nordholts committed
580

581 582 583
  g_signal_connect_swapped (shell->zoom_button, "toggled",
                            G_CALLBACK (gimp_display_shell_zoom_button_callback),
                            shell);
584

585 586 587
  gimp_help_set_help_data (shell->zoom_button,
                           _("Zoom image when window size changes"),
                           GIMP_HELP_IMAGE_WINDOW_ZOOM_FOLLOW_BUTTON);
588

589 590 591 592 593 594 595 596
  /*  the quick mask button  */
  shell->quick_mask_button = g_object_new (GTK_TYPE_CHECK_BUTTON,
                                           "draw-indicator", FALSE,
                                           "relief",         GTK_RELIEF_NONE,
                                           "width-request",  18,
                                           "height-request", 18,
                                           NULL);
  gtk_widget_set_can_focus (shell->quick_mask_button, FALSE);
597
  gtk_image = gtk_image_new_from_icon_name (GIMP_ICON_QUICK_MASK_OFF,
598
                                            GTK_ICON_SIZE_MENU);
599 600
  gtk_container_add (GTK_CONTAINER (shell->quick_mask_button), gtk_image);
  gtk_widget_show (gtk_image);
Elliot Lee's avatar
Elliot Lee committed
601

602 603 604 605 606 607 608
  g_signal_connect (shell->quick_mask_button, "toggled",
                    G_CALLBACK (gimp_display_shell_quick_mask_toggled),
                    shell);
  g_signal_connect (shell->quick_mask_button, "button-press-event",
                    G_CALLBACK (gimp_display_shell_quick_mask_button_press),
                    shell);

609 610 611 612 613 614 615 616
  action = gimp_ui_manager_find_action (shell->popup_manager,
                                        "quick-mask", "quick-mask-toggle");
  if (action)
    gimp_widget_set_accel_help (shell->quick_mask_button, action);
  else
    gimp_help_set_help_data (shell->quick_mask_button,
                             _("Toggle Quick Mask"),
                             GIMP_HELP_IMAGE_WINDOW_QUICK_MASK_BUTTON);
617

618 619
  /*  the navigation window button  */
  shell->nav_ebox = gtk_event_box_new ();
620
  gtk_image = gtk_image_new_from_icon_name (GIMP_ICON_DIALOG_NAVIGATION,
621
                                            GTK_ICON_SIZE_MENU);
622 623 624 625
  gtk_container_add (GTK_CONTAINER (shell->nav_ebox), gtk_image);
  gtk_widget_show (gtk_image);

  g_signal_connect (shell->nav_ebox, "button-press-event",
626
                    G_CALLBACK (gimp_display_shell_navigation_button_press),
627
                    shell);
628

629 630 631
  gimp_help_set_help_data (shell->nav_ebox,
                           _("Navigate the image display"),
                           GIMP_HELP_IMAGE_WINDOW_NAV_BUTTON);
632

633
  /*  the statusbar  */
634 635 636 637 638
  shell->statusbar = gimp_statusbar_new ();
  gimp_statusbar_set_shell (GIMP_STATUSBAR (shell->statusbar), shell);
  gimp_help_set_help_data (shell->statusbar, NULL,
                           GIMP_HELP_IMAGE_WINDOW_STATUS_BAR);

639 640
  /*  pack all the widgets  */
  gtk_grid_attach (GTK_GRID (grid), shell->origin, 0, 0, 1, 1);
641 642

  gtk_widget_set_hexpand (shell->hrule, TRUE);
643
  gtk_grid_attach (GTK_GRID (grid), shell->hrule, 1, 0, 1, 1);
644 645

  gtk_widget_set_vexpand (shell->vrule, TRUE);
646
  gtk_grid_attach (GTK_GRID (grid), shell->vrule, 0, 1, 1, 1);
647 648 649

  gtk_widget_set_hexpand (shell->canvas, TRUE);
  gtk_widget_set_vexpand (shell->canvas, TRUE);
650 651 652 653
  gtk_grid_attach (GTK_GRID (grid), shell->canvas, 1, 1, 1, 1);

  gtk_grid_attach (GTK_GRID (grid), shell->zoom_button, 2, 0, 1, 1);
  gtk_grid_attach (GTK_GRID (grid), shell->quick_mask_button, 0, 2, 1, 1);
654

655 656
  gtk_grid_attach (GTK_GRID (grid), shell->vsb, 2, 1, 1, 1);
  gtk_grid_attach (GTK_GRID (grid), shell->hsb, 1, 2, 1, 1);
657

658
  gtk_grid_attach (GTK_GRID (grid), shell->nav_ebox, 2, 2, 1, 1);
659

660 661
  gtk_widget_set_hexpand (shell->statusbar, TRUE);
  gtk_grid_attach (GTK_GRID (grid), shell->statusbar, 0, 3, 3, 1);
662

663
  /*  show everything that is always shown */
664 665
  gtk_widget_show (GTK_WIDGET (shell->canvas));

666
  /*  add display filters  */
667

668
  filter = gimp_display_shell_filter_new (shell);
669 670

  if (filter)
671
    {
672 673
      gimp_display_shell_filter_set (shell, filter);
      g_object_unref (filter);
674 675
    }

676
  if (image)
677
    {
678 679 680
      gimp_display_shell_connect (shell);

      /* After connecting to the image we want to center it. Since we
681
       * not even finished creating the display shell, we can safely
682 683
       * assume we will get a size-allocate later.
       */
684
      shell->size_allocate_center_image = TRUE;
685 686 687
    }
  else
    {
688 689 690 691
#if 0
      /* Disabled because it sets GDK_POINTER_MOTION_HINT on
       * shell->canvas. For info see Bug 677375
       */
692 693 694
      gimp_help_set_help_data (shell->canvas,
                               _("Drop image files here to open them"),
                               NULL);
695
#endif
696 697

      gimp_statusbar_empty (GIMP_STATUSBAR (shell->statusbar));
698
    }
699 700 701

  /* make sure the information is up-to-date */
  gimp_display_shell_scale_update (shell);
702
}
703

704 705 706 707 708 709 710 711 712 713
static void
gimp_display_shell_dispose (GObject *object)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object);

  if (shell->display && gimp_display_get_shell (shell->display))
    gimp_display_shell_disconnect (shell);

  shell->popup_manager = NULL;

714 715
  if (shell->selection)
    gimp_display_shell_selection_free (shell);
716 717 718 719 720 721 722 723 724 725

  if (shell->filter_stack)
    gimp_display_shell_filter_set (shell, NULL);

  if (shell->filter_idle_id)
    {
      g_source_remove (shell->filter_idle_id);
      shell->filter_idle_id = 0;
    }

726 727
  g_clear_pointer (&shell->mask_surface, cairo_surface_destroy);
  g_clear_pointer (&shell->checkerboard, cairo_pattern_destroy);
728

729
  gimp_display_shell_profile_finalize (shell);
730

731 732 733
  g_clear_object (&shell->filter_buffer);
  shell->filter_data   = NULL;
  shell->filter_stride = 0;
734

735
  g_clear_object (&shell->mask);
736

737
  gimp_display_shell_items_free (shell);
738

739
  g_clear_object (&shell->motion_buffer);
740

741
  g_clear_pointer (&shell->zoom_focus_pointer_queue, g_queue_free);
742 743 744 745 746 747 748 749 750 751 752 753 754

  if (shell->title_idle_id)
    {
      g_source_remove (shell->title_idle_id);
      shell->title_idle_id = 0;
    }

  if (shell->fill_idle_id)
    {
      g_source_remove (shell->fill_idle_id);
      shell->fill_idle_id = 0;
    }

755
  g_clear_pointer (&shell->nav_popup, gtk_widget_destroy);
756

757 758 759 760 761 762
  if (shell->blink_timeout_id)
    {
      g_source_remove (shell->blink_timeout_id);
      shell->blink_timeout_id = 0;
    }

763 764 765 766 767
  shell->display = NULL;

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

768 769 770 771
static void
gimp_display_shell_finalize (GObject *object)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object);
772

773 774 775 776 777 778 779 780 781
  g_clear_object (&shell->zoom);
  g_clear_pointer (&shell->rotate_transform,   g_free);
  g_clear_pointer (&shell->rotate_untransform, g_free);
  g_clear_object (&shell->options);
  g_clear_object (&shell->fullscreen_options);
  g_clear_object (&shell->no_image_options);
  g_clear_pointer (&shell->title,  g_free);
  g_clear_pointer (&shell->status, g_free);
  g_clear_object (&shell->icon);
Elliot Lee's avatar
Elliot Lee committed
782

783 784
  G_OBJECT_CLASS (parent_class)->finalize (object);
}
785

786 787 788 789 790 791 792
static void
gimp_display_shell_set_property (GObject      *object,
                                 guint         property_id,
                                 const GValue *value,
                                 GParamSpec   *pspec)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object);
793

794 795 796 797 798
  switch (property_id)
    {
    case PROP_POPUP_MANAGER:
      shell->popup_manager = g_value_get_object (value);
      break;
799
    case PROP_INITIAL_MONITOR:
800
      shell->initial_monitor = g_value_get_object (value);
801
      break;
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
    case PROP_DISPLAY:
      shell->display = g_value_get_object (value);
      break;
    case PROP_UNIT:
      gimp_display_shell_set_unit (shell, g_value_get_int (value));
      break;
    case PROP_TITLE:
      g_free (shell->title);
      shell->title = g_value_dup_string (value);
      break;
    case PROP_STATUS:
      g_free (shell->status);
      shell->status = g_value_dup_string (value);
      break;
    case PROP_ICON:
      if (shell->icon)
        g_object_unref (shell->icon);
      shell->icon = g_value_dup_object (value);
      break;
821

822 823 824 825 826
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}
827

828 829 830 831 832 833 834
static void
gimp_display_shell_get_property (GObject    *object,
                                 guint       property_id,
                                 GValue     *value,
                                 GParamSpec *pspec)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object);
835

836 837 838 839 840
  switch (property_id)
    {
    case PROP_POPUP_MANAGER:
      g_value_set_object (value, shell->popup_manager);
      break;
841
    case PROP_INITIAL_MONITOR:
842
      g_value_set_object (value, shell->initial_monitor);
843
      break;
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
    case PROP_DISPLAY:
      g_value_set_object (value, shell->display);
      break;
    case PROP_UNIT:
      g_value_set_int (value, shell->unit);
      break;
    case PROP_TITLE:
      g_value_set_string (value, shell->title);
      break;
    case PROP_STATUS:
      g_value_set_string (value, shell->status);
      break;
    case PROP_ICON:
      g_value_set_object (value, shell->icon);
      break;
859

860 861 862 863 864
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}
865

866 867 868 869
static void
gimp_display_shell_unrealize (GtkWidget *widget)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget);
870

871 872
  if (shell->nav_popup)
    gtk_widget_unrealize (shell->nav_popup);
873

874 875
  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
876

877 878 879 880 881 882 883 884 885 886
static void
gimp_display_shell_unmap (GtkWidget *widget)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget);

  gimp_display_shell_selection_undraw (shell);

  GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}

887 888 889 890 891
static void
gimp_display_shell_screen_changed (GtkWidget *widget,
                                   GdkScreen *previous)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget);
892

893 894
  if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
    GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous);
895

896 897
  if (shell->display->config->monitor_res_from_gdk)
    {
898
      gimp_get_monitor_resolution (gimp_widget_get_monitor (widget),
899 900
                                   &shell->monitor_xres,
                                   &shell->monitor_yres);
901 902 903 904 905 906 907
    }
  else
    {
      shell->monitor_xres = shell->display->config->monitor_xres;
      shell->monitor_yres = shell->display->config->monitor_yres;
    }
}
908

909 910 911 912
static gboolean
gimp_display_shell_popup_menu (GtkWidget *widget)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget);
913

914 915
  gimp_context_set_display (gimp_get_user_context (shell->display->gimp),
                            shell->display);
916

917 918 919 920 921 922 923
  gimp_ui_manager_ui_popup_at_widget (shell->popup_manager,
                                      "/dummy-menubar/image-popup",
                                      shell->origin,
                                      GDK_GRAVITY_EAST,
                                      GDK_GRAVITY_NORTH_WEST,
                                      NULL,
                                      NULL, NULL);
924

925 926
  return TRUE;
}
927

928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
static void
gimp_display_shell_set_priority_viewport (GimpDisplayShell *shell)
{
  GimpImage *image = gimp_display_get_image (shell->display);

  if (image)
    {
      GimpProjection *projection = gimp_image_get_projection (image);
      gint            x, y;
      gint            width, height;

      gimp_display_shell_untransform_viewport (shell, &x, &y, &width, &height);
      gimp_projection_set_priority_rect (projection, x, y, width, height);
    }
}

944 945 946 947
static void
gimp_display_shell_real_scaled (GimpDisplayShell *shell)
{
  GimpContext *user_context;
Elliot Lee's avatar
Elliot Lee committed
948

949 950
  if (! shell->display)
    return;
951

952
  gimp_display_shell_title_update (shell);
Michael Natterer's avatar
Michael Natterer committed
953

954
  user_context = gimp_get_user_context (shell->display->gimp);
955

956
  if (shell->display == gimp_context_get_display (user_context))
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
    {
      gimp_display_shell_set_priority_viewport (shell);

      gimp_ui_manager_update (shell->popup_manager, shell->display);
    }
}

static void
gimp_display_shell_real_scrolled (GimpDisplayShell *shell)
{
  GimpContext *user_context;

  if (! shell->display)
    return;

972 973
  gimp_display_shell_title_update (shell);

974 975 976 977 978
  user_context = gimp_get_user_context (shell->display->gimp);

  if (shell->display == gimp_context_get_display (user_context))
    {
      gimp_display_shell_set_priority_viewport (shell);
979

980
    }
981
}
982

983 984 985 986 987 988 989 990
static void
gimp_display_shell_real_rotated (GimpDisplayShell *shell)
{
  GimpContext *user_context;

  if (! shell->display)
    return;

991 992
  gimp_display_shell_title_update (shell);

993 994 995
  user_context = gimp_get_user_context (shell->display->gimp);

  if (shell->display == gimp_context_get_display (user_context))
996 997 998 999 1000
    {
      gimp_display_shell_set_priority_viewport (shell);

      gimp_ui_manager_update (shell->popup_manager, shell->display);
    }
1001 1002
}

1003 1004 1005 1006 1007 1008
static const guint8 *
gimp_display_shell_get_icc_profile (GimpColorManaged *managed,
                                    gsize            *len)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (managed);
  GimpImage        *image = gimp_display_get_image (shell->display);
Michael Natterer's avatar
Michael Natterer committed
1009

1010 1011
  if (image)
    return gimp_color_managed_get_icc_profile (GIMP_COLOR_MANAGED (image), len);
1012

1013 1014
  return NULL;
}
1015

1016
static GimpColorProfile *
1017 1018 1019 1020 1021 1022 1023 1024
gimp_display_shell_get_color_profile (GimpColorManaged *managed)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (managed);
  GimpImage        *image = gimp_display_get_image (shell->display);

  if (image)
    return gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image));

1025
  return NULL;
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
}

static void
gimp_display_shell_profile_changed (GimpColorManaged *managed)
{
  GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (managed);

  gimp_display_shell_profile_update (shell);
  gimp_display_shell_expose_full (shell);
}

1037 1038 1039 1040 1041 1042 1043 1044 1045
static void
gimp_display_shell_zoom_button_callback (GimpDisplayShell *shell,
                                         GtkWidget        *zoom_button)
{
  shell->zoom_on_resize =
    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (zoom_button));

  if (shell->zoom_on_resize &&
      gimp_display_shell_scale_image_is_within_viewport (shell, NULL, NULL))
1046
    {
1047 1048
      /* Implicitly make a View -> Fit Image in Window */
      gimp_display_shell_scale_fit_in (shell);
1049
    }
1050
}
1051

1052 1053 1054 1055 1056 1057 1058 1059 1060
static void
gimp_display_shell_sync_config (GimpDisplayShell  *shell,
                                GimpDisplayConfig *config)
{
  gimp_config_sync (G_OBJECT (config->default_view),
                    G_OBJECT (shell->options), 0);
  gimp_config_sync (G_OBJECT (config->default_fullscreen_view),
                    G_OBJECT (shell->fullscreen_options), 0);
}
1061

1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
static void
gimp_display_shell_overlay_allocate (GtkWidget               *child,
                                     GtkAllocation           *allocation,
                                     GimpDisplayShellOverlay *overlay)
{
  gdouble x, y;

  gimp_display_shell_transform_overlay (overlay->shell, child, &x, &y);

  gimp_overlay_box_set_child_position (GIMP_OVERLAY_BOX (overlay->shell->canvas),
                                       child, x, y);
}

1075 1076 1077 1078 1079
static void
gimp_display_shell_remove_overlay (GtkWidget        *canvas,
                                   GtkWidget        *child,
                                   GimpDisplayShell *shell)
{
1080 1081 1082 1083 1084 1085 1086 1087
  GimpDisplayShellOverlay *overlay;

  overlay = g_object_get_data (G_OBJECT (child), "image-coords-overlay");

  g_signal_handlers_disconnect_by_func (child,
                                        gimp_display_shell_overlay_allocate,
                                        overlay);

1088 1089 1090
  shell->children = g_list_remove (shell->children, child);
}

1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
static void
gimp_display_shell_transform_overlay (GimpDisplayShell *shell,
                                      GtkWidget        *child,
                                      gdouble          *x,
                                      gdouble          *y)
{
  GimpDisplayShellOverlay *overlay;
  GtkRequisition           requisition;

  overlay = g_object_get_data (G_OBJECT (child), "image-coords-overlay");

  gimp_display_shell_transform_xy_f (shell,
                                     overlay->image_x,
                                     overlay->image_y,
1105
                                     x, y);
1106

1107
  gtk_widget_get_preferred_size (child, &requisition, NULL);
1108 1109 1110

  switch (overlay->anchor)
    {
1111
    case GIMP_HANDLE_ANCHOR_CENTER:
1112 1113 1114 1115
      *x -= requisition.width  / 2;
      *y -= requisition.height / 2;
      break;

1116
    case GIMP_HANDLE_ANCHOR_NORTH:
1117 1118 1119 1120
      *x -= requisition.width / 2;
      *y += overlay->spacing_y;
      break;

1121
    case GIMP_HANDLE_ANCHOR_NORTH_WEST:
1122 1123 1124 1125
      *x += overlay->spacing_x;
      *y += overlay->spacing_y;
      break;

1126
    case GIMP_HANDLE_ANCHOR_NORTH_EAST:
1127 1128 1129 1130
      *x -= requisition.width + overlay->spacing_x;
      *y += overlay->spacing_y;
      break;

1131
    case GIMP_HANDLE_ANCHOR_SOUTH:
1132 1133 1134 1135
      *x -= requisition.width / 2;
      *y -= requisition.height + overlay->spacing_y;
      break;

1136
    case GIMP_HANDLE_ANCHOR_SOUTH_WEST:
1137 1138 1139 1140
      *x += overlay->spacing_x;
      *y -= requisition.height + overlay->spacing_y;
      break;

1141
    case GIMP_HANDLE_ANCHOR_SOUTH_EAST:
1142 1143 1144 1145
      *x -= requisition.width + overlay->spacing_x;
      *y -= requisition.height + overlay->spacing_y;
      break;

1146
    case GIMP_HANDLE_ANCHOR_WEST:
1147 1148 1149 1150
      *x += overlay->spacing_x;
      *y -= requisition.height / 2;
      break;

1151
    case GIMP_HANDLE_ANCHOR_EAST:
1152 1153 1154 1155 1156 1157
      *x -= requisition.width + overlay->spacing_x;
      *y -= requisition.height / 2;
      break;
    }
}

1158

1159 1160 1161
/*  public functions  */

GtkWidget *
1162 1163 1164 1165
gimp_display_shell_new (GimpDisplay   *display,
                        GimpUnit       unit,
                        gdouble        scale,
                        GimpUIManager *popup_manager,
1166
                        GdkMonitor    *monitor)
1167 1168 1169
{
  g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
  g_return_val_if_fail (GIMP_IS_UI_MANAGER (popup_manager), NULL);
1170
  g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL);
1171 1172

  return g_object_new (GIMP_TYPE_DISPLAY_SHELL,
1173 1174 1175 1176
                       "popup-manager",   popup_manager,
                       "initial-monitor", monitor,
                       "display",         display,
                       "unit",            unit,
1177
                       NULL);
1178 1179
}

1180 1181 1182 1183
void
gimp_display_shell_add_overlay (GimpDisplayShell *shell,
                                GtkWidget        *child,
                                gdouble           image_x,
1184
                                gdouble           image_y,
1185
                                GimpHandleAnchor  anchor,
1186 1187
                                gint              spacing_x,
                                gint              spacing_y)
1188
{
1189 1190
  GimpDisplayShellOverlay *overlay;
  gdouble                  x, y;
1191 1192 1193 1194

  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
  g_return_if_fail (GTK_IS_WIDGET (shell));

1195
  overlay = g_new0 (GimpDisplayShellOverlay, 1);
1196

1197
  overlay->shell     = shell;
1198 1199 1200 1201 1202
  overlay->image_x   = image_x;
  overlay->image_y   = image_y;
  overlay->anchor    = anchor;
  overlay->spacing_x = spacing_x;
  overlay->spacing_y = spacing_y;
1203

1204
  g_object_set_data_full (G_OBJECT (child), "image-coords-overlay", overlay,
1205 1206 1207 1208
                          (GDestroyNotify) g_free);

  shell->children = g_list_prepend (shell->children, child);

1209 1210 1211 1212
  g_signal_connect (child, "size-allocate",
                    G_CALLBACK (gimp_display_shell_overlay_allocate),
                    overlay);

1213
  gimp_display_shell_transform_overlay (shell, child, &x, &y);
1214 1215 1216 1217 1218 1219

  gimp_overlay_box_add_child (GIMP_OVERLAY_BOX (shell->canvas), child, 0.0, 0.0);
  gimp_overlay_box_set_child_position (GIMP_OVERLAY_BOX (shell->canvas),
                                       child, x, y);
}

1220 1221 1222 1223
void
gimp_display_shell_move_overlay (GimpDisplayShell *shell,
                                 GtkWidget        *child,
                                 gdouble           image_x,
1224
                                 gdouble           image_y,
1225
                                 GimpHandleAnchor  anchor,
1226 1227
                                 gint              spacing_x,
                                 gint              spacing_y)
1228
{
1229 1230
  GimpDisplayShellOverlay *overlay;
  gdouble                  x, y;
1231 1232 1233 1234

  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
  g_return_if_fail (GTK_IS_WIDGET (shell));

1235
  overlay = g_object_get_data (G_OBJECT (child), "image-coords-overlay");
1236

1237
  g_return_if_fail (overlay != NULL);
1238

1239 1240 1241 1242 1243
  overlay->image_x   = image_x;
  overlay->image_y   = image_y;
  overlay->anchor    = anchor;
  overlay->spacing_x = spacing_x;
  overlay->spacing_y = spacing_y;
1244

1245
  gimp_display_shell_transform_overlay (shell, child, &x, &y);