1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
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
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * 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/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gegl.h>
21 #include <gtk/gtk.h>
22 
23 #include "libgimpwidgets/gimpwidgets.h"
24 
25 #include "tools-types.h"
26 
27 #include "core/gimp.h"
28 #include "core/gimpimage.h"
29 #include "core/gimpimage-crop.h"
30 #include "core/gimpitem.h"
31 #include "core/gimptoolinfo.h"
32 
33 #include "widgets/gimphelp-ids.h"
34 
35 #include "display/gimpdisplay.h"
36 #include "display/gimpdisplayshell.h"
37 #include "display/gimptoolrectangle.h"
38 
39 #include "gimpcropoptions.h"
40 #include "gimpcroptool.h"
41 #include "gimprectangleoptions.h"
42 #include "gimptoolcontrol.h"
43 #include "gimptools-utils.h"
44 
45 #include "gimp-intl.h"
46 
47 
48 static void      gimp_crop_tool_constructed                (GObject              *object);
49 static void      gimp_crop_tool_dispose                    (GObject              *object);
50 
51 static void      gimp_crop_tool_control                    (GimpTool             *tool,
52                                                             GimpToolAction        action,
53                                                             GimpDisplay          *display);
54 static void      gimp_crop_tool_button_press               (GimpTool             *tool,
55                                                             const GimpCoords     *coords,
56                                                             guint32               time,
57                                                             GdkModifierType       state,
58                                                             GimpButtonPressType   press_type,
59                                                             GimpDisplay          *display);
60 static void      gimp_crop_tool_button_release             (GimpTool             *tool,
61                                                             const GimpCoords     *coords,
62                                                             guint32               time,
63                                                             GdkModifierType       state,
64                                                             GimpButtonReleaseType release_type,
65                                                             GimpDisplay          *display);
66 static void     gimp_crop_tool_motion                      (GimpTool             *tool,
67                                                             const GimpCoords     *coords,
68                                                             guint32               time,
69                                                             GdkModifierType       state,
70                                                             GimpDisplay          *display);
71 static void      gimp_crop_tool_options_notify             (GimpTool             *tool,
72                                                             GimpToolOptions      *options,
73                                                             const GParamSpec     *pspec);
74 
75 static void      gimp_crop_tool_rectangle_changed          (GimpToolWidget       *rectangle,
76                                                             GimpCropTool         *crop_tool);
77 static void      gimp_crop_tool_rectangle_response         (GimpToolWidget       *rectangle,
78                                                             gint                  response_id,
79                                                             GimpCropTool         *crop_tool);
80 static void      gimp_crop_tool_rectangle_change_complete  (GimpToolRectangle    *rectangle,
81                                                             GimpCropTool         *crop_tool);
82 
83 static void      gimp_crop_tool_start                      (GimpCropTool         *crop_tool,
84                                                             GimpDisplay          *display);
85 static void      gimp_crop_tool_commit                     (GimpCropTool         *crop_tool);
86 static void      gimp_crop_tool_halt                       (GimpCropTool         *crop_tool);
87 
88 static void      gimp_crop_tool_update_option_defaults     (GimpCropTool         *crop_tool,
89                                                             gboolean              ignore_pending);
90 static GimpRectangleConstraint
91                  gimp_crop_tool_get_constraint             (GimpCropTool         *crop_tool);
92 
93 static void      gimp_crop_tool_image_changed              (GimpCropTool         *crop_tool,
94                                                             GimpImage            *image,
95                                                             GimpContext          *context);
96 static void      gimp_crop_tool_image_size_changed         (GimpCropTool         *crop_tool);
97 static void      gimp_crop_tool_image_active_layer_changed (GimpCropTool         *crop_tool);
98 static void      gimp_crop_tool_layer_size_changed         (GimpCropTool         *crop_tool);
99 
100 static void      gimp_crop_tool_auto_shrink                (GimpCropTool         *crop_tool);
101 
102 
G_DEFINE_TYPE(GimpCropTool,gimp_crop_tool,GIMP_TYPE_DRAW_TOOL)103 G_DEFINE_TYPE (GimpCropTool, gimp_crop_tool, GIMP_TYPE_DRAW_TOOL)
104 
105 #define parent_class gimp_crop_tool_parent_class
106 
107 
108 /*  public functions  */
109 
110 void
111 gimp_crop_tool_register (GimpToolRegisterCallback  callback,
112                          gpointer                  data)
113 {
114   (* callback) (GIMP_TYPE_CROP_TOOL,
115                 GIMP_TYPE_CROP_OPTIONS,
116                 gimp_crop_options_gui,
117                 GIMP_CONTEXT_PROP_MASK_FOREGROUND |
118                 GIMP_CONTEXT_PROP_MASK_BACKGROUND |
119                 GIMP_CONTEXT_PROP_MASK_PATTERN,
120                 "gimp-crop-tool",
121                 _("Crop"),
122                 _("Crop Tool: Remove edge areas from image or layer"),
123                 N_("_Crop"), "<shift>C",
124                 NULL, GIMP_HELP_TOOL_CROP,
125                 GIMP_ICON_TOOL_CROP,
126                 data);
127 }
128 
129 static void
gimp_crop_tool_class_init(GimpCropToolClass * klass)130 gimp_crop_tool_class_init (GimpCropToolClass *klass)
131 {
132   GObjectClass  *object_class = G_OBJECT_CLASS (klass);
133   GimpToolClass *tool_class   = GIMP_TOOL_CLASS (klass);
134 
135   object_class->constructed  = gimp_crop_tool_constructed;
136   object_class->dispose      = gimp_crop_tool_dispose;
137 
138   tool_class->control        = gimp_crop_tool_control;
139   tool_class->button_press   = gimp_crop_tool_button_press;
140   tool_class->button_release = gimp_crop_tool_button_release;
141   tool_class->motion         = gimp_crop_tool_motion;
142   tool_class->options_notify = gimp_crop_tool_options_notify;
143 }
144 
145 static void
gimp_crop_tool_init(GimpCropTool * crop_tool)146 gimp_crop_tool_init (GimpCropTool *crop_tool)
147 {
148   GimpTool *tool = GIMP_TOOL (crop_tool);
149 
150   gimp_tool_control_set_wants_click      (tool->control, TRUE);
151   gimp_tool_control_set_active_modifiers (tool->control,
152                                           GIMP_TOOL_ACTIVE_MODIFIERS_SEPARATE);
153   gimp_tool_control_set_precision        (tool->control,
154                                           GIMP_CURSOR_PRECISION_PIXEL_BORDER);
155   gimp_tool_control_set_cursor           (tool->control,
156                                           GIMP_CURSOR_CROSSHAIR_SMALL);
157   gimp_tool_control_set_tool_cursor      (tool->control,
158                                           GIMP_TOOL_CURSOR_CROP);
159 
160   gimp_draw_tool_set_default_status (GIMP_DRAW_TOOL (tool),
161                                      _("Click-Drag to draw a crop rectangle"));
162 }
163 
164 static void
gimp_crop_tool_constructed(GObject * object)165 gimp_crop_tool_constructed (GObject *object)
166 {
167   GimpCropTool    *crop_tool = GIMP_CROP_TOOL (object);
168   GimpContext     *context;
169   GimpToolInfo    *tool_info;
170 
171   G_OBJECT_CLASS (parent_class)->constructed (object);
172 
173   tool_info = GIMP_TOOL (crop_tool)->tool_info;
174 
175   context = gimp_get_user_context (tool_info->gimp);
176 
177   g_signal_connect_object (context, "image-changed",
178                            G_CALLBACK (gimp_crop_tool_image_changed),
179                            crop_tool,
180                            G_CONNECT_SWAPPED);
181 
182   /* Make sure we are connected to "size-changed" for the initial
183    * image.
184    */
185   gimp_crop_tool_image_changed (crop_tool,
186                                 gimp_context_get_image (context),
187                                 context);
188 }
189 
190 static void
gimp_crop_tool_dispose(GObject * object)191 gimp_crop_tool_dispose (GObject *object)
192 {
193   GimpCropTool *crop_tool = GIMP_CROP_TOOL (object);
194 
195   /* Clean up current_image and current_layer. */
196   gimp_crop_tool_image_changed (crop_tool, NULL, NULL);
197 
198   G_OBJECT_CLASS (parent_class)->dispose (object);
199 }
200 
201 static void
gimp_crop_tool_control(GimpTool * tool,GimpToolAction action,GimpDisplay * display)202 gimp_crop_tool_control (GimpTool       *tool,
203                         GimpToolAction  action,
204                         GimpDisplay    *display)
205 {
206   GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
207 
208   switch (action)
209     {
210     case GIMP_TOOL_ACTION_PAUSE:
211     case GIMP_TOOL_ACTION_RESUME:
212       break;
213 
214     case GIMP_TOOL_ACTION_HALT:
215       gimp_crop_tool_halt (crop_tool);
216       break;
217 
218     case GIMP_TOOL_ACTION_COMMIT:
219       gimp_crop_tool_commit (crop_tool);
220       break;
221     }
222 
223   GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
224 }
225 
226 static void
gimp_crop_tool_button_press(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type,GimpDisplay * display)227 gimp_crop_tool_button_press (GimpTool            *tool,
228                              const GimpCoords    *coords,
229                              guint32              time,
230                              GdkModifierType      state,
231                              GimpButtonPressType  press_type,
232                              GimpDisplay         *display)
233 {
234   GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
235 
236   if (tool->display && display != tool->display)
237     gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
238 
239   if (! tool->display)
240     {
241       gimp_crop_tool_start (crop_tool, display);
242 
243       gimp_tool_widget_hover (crop_tool->widget, coords, state, TRUE);
244 
245       /* HACK: force CREATING on a newly created rectangle; otherwise,
246        * property bindings would cause the rectangle to start with the
247        * size from tool options.
248        */
249       gimp_tool_rectangle_set_function (GIMP_TOOL_RECTANGLE (crop_tool->widget),
250                                         GIMP_TOOL_RECTANGLE_CREATING);
251     }
252 
253   if (gimp_tool_widget_button_press (crop_tool->widget, coords, time, state,
254                                      press_type))
255     {
256       crop_tool->grab_widget = crop_tool->widget;
257     }
258 
259   gimp_tool_control_activate (tool->control);
260 }
261 
262 static void
gimp_crop_tool_button_release(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type,GimpDisplay * display)263 gimp_crop_tool_button_release (GimpTool              *tool,
264                                const GimpCoords      *coords,
265                                guint32                time,
266                                GdkModifierType        state,
267                                GimpButtonReleaseType  release_type,
268                                GimpDisplay           *display)
269 {
270   GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
271 
272   gimp_tool_control_halt (tool->control);
273 
274   if (crop_tool->grab_widget)
275     {
276       gimp_tool_widget_button_release (crop_tool->grab_widget,
277                                        coords, time, state, release_type);
278       crop_tool->grab_widget = NULL;
279     }
280 
281   gimp_tool_push_status (tool, display, _("Click or press Enter to crop"));
282 }
283 
284 static void
gimp_crop_tool_motion(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpDisplay * display)285 gimp_crop_tool_motion (GimpTool         *tool,
286                        const GimpCoords *coords,
287                        guint32           time,
288                        GdkModifierType   state,
289                        GimpDisplay      *display)
290 {
291   GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
292 
293   if (crop_tool->grab_widget)
294     {
295       gimp_tool_widget_motion (crop_tool->grab_widget, coords, time, state);
296     }
297 }
298 
299 static void
gimp_crop_tool_options_notify(GimpTool * tool,GimpToolOptions * options,const GParamSpec * pspec)300 gimp_crop_tool_options_notify (GimpTool         *tool,
301                                GimpToolOptions  *options,
302                                const GParamSpec *pspec)
303 {
304   GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
305 
306   if (! strcmp (pspec->name, "layer-only") ||
307       ! strcmp (pspec->name, "allow-growing"))
308     {
309       if (crop_tool->widget)
310         {
311           gimp_tool_rectangle_set_constraint (GIMP_TOOL_RECTANGLE (crop_tool->widget),
312                                               gimp_crop_tool_get_constraint (crop_tool));
313         }
314       else
315         {
316           gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
317         }
318     }
319 }
320 
321 static void
gimp_crop_tool_rectangle_changed(GimpToolWidget * rectangle,GimpCropTool * crop_tool)322 gimp_crop_tool_rectangle_changed (GimpToolWidget *rectangle,
323                                   GimpCropTool   *crop_tool)
324 {
325 }
326 
327 static void
gimp_crop_tool_rectangle_response(GimpToolWidget * rectangle,gint response_id,GimpCropTool * crop_tool)328 gimp_crop_tool_rectangle_response (GimpToolWidget *rectangle,
329                                    gint            response_id,
330                                    GimpCropTool   *crop_tool)
331 {
332   GimpTool *tool = GIMP_TOOL (crop_tool);
333 
334   switch (response_id)
335     {
336     case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM:
337       gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, tool->display);
338       break;
339 
340     case GIMP_TOOL_WIDGET_RESPONSE_CANCEL:
341       gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
342       break;
343     }
344 }
345 
346 static void
gimp_crop_tool_rectangle_change_complete(GimpToolRectangle * rectangle,GimpCropTool * crop_tool)347 gimp_crop_tool_rectangle_change_complete (GimpToolRectangle *rectangle,
348                                           GimpCropTool      *crop_tool)
349 {
350   gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
351 }
352 
353 static void
gimp_crop_tool_start(GimpCropTool * crop_tool,GimpDisplay * display)354 gimp_crop_tool_start (GimpCropTool *crop_tool,
355                       GimpDisplay  *display)
356 {
357   static const gchar *properties[] =
358   {
359     "highlight",
360     "highlight-opacity",
361     "guide",
362     "x",
363     "y",
364     "width",
365     "height",
366     "fixed-rule-active",
367     "fixed-rule",
368     "desired-fixed-width",
369     "desired-fixed-height",
370     "desired-fixed-size-width",
371     "desired-fixed-size-height",
372     "aspect-numerator",
373     "aspect-denominator",
374     "fixed-center"
375   };
376 
377   GimpTool         *tool    = GIMP_TOOL (crop_tool);
378   GimpDisplayShell *shell   = gimp_display_get_shell (display);
379   GimpCropOptions  *options = GIMP_CROP_TOOL_GET_OPTIONS (crop_tool);
380   GimpToolWidget   *widget;
381   gint              i;
382 
383   tool->display = display;
384 
385   crop_tool->widget = widget = gimp_tool_rectangle_new (shell);
386 
387   g_object_set (widget,
388                 "status-title", _("Crop to: "),
389                 NULL);
390 
391   gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), widget);
392 
393   for (i = 0; i < G_N_ELEMENTS (properties); i++)
394     {
395       GBinding *binding =
396         g_object_bind_property (G_OBJECT (options), properties[i],
397                                 G_OBJECT (widget),  properties[i],
398                                 G_BINDING_SYNC_CREATE |
399                                 G_BINDING_BIDIRECTIONAL);
400 
401       crop_tool->bindings = g_list_prepend (crop_tool->bindings, binding);
402     }
403 
404   gimp_rectangle_options_connect (GIMP_RECTANGLE_OPTIONS (options),
405                                   gimp_display_get_image (shell->display),
406                                   G_CALLBACK (gimp_crop_tool_auto_shrink),
407                                   crop_tool);
408 
409   gimp_tool_rectangle_set_constraint (GIMP_TOOL_RECTANGLE (widget),
410                                       gimp_crop_tool_get_constraint (crop_tool));
411 
412   g_signal_connect (widget, "changed",
413                     G_CALLBACK (gimp_crop_tool_rectangle_changed),
414                     crop_tool);
415   g_signal_connect (widget, "response",
416                     G_CALLBACK (gimp_crop_tool_rectangle_response),
417                     crop_tool);
418   g_signal_connect (widget, "change-complete",
419                     G_CALLBACK (gimp_crop_tool_rectangle_change_complete),
420                     crop_tool);
421 
422   gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
423 }
424 
425 static void
gimp_crop_tool_commit(GimpCropTool * crop_tool)426 gimp_crop_tool_commit (GimpCropTool *crop_tool)
427 {
428   GimpTool *tool = GIMP_TOOL (crop_tool);
429 
430   if (tool->display)
431     {
432       GimpCropOptions *options = GIMP_CROP_TOOL_GET_OPTIONS (tool);
433       GimpImage       *image   = gimp_display_get_image (tool->display);
434       gdouble          x, y;
435       gdouble          x2, y2;
436       gint             w, h;
437 
438       gimp_tool_rectangle_get_public_rect (GIMP_TOOL_RECTANGLE (crop_tool->widget),
439                                            &x, &y, &x2, &y2);
440       w = x2 - x;
441       h = y2 - y;
442 
443       gimp_tool_pop_status (tool, tool->display);
444 
445       /* if rectangle exists, crop it */
446       if (w > 0 && h > 0)
447         {
448           if (options->layer_only)
449             {
450               GimpLayer *layer = gimp_image_get_active_layer (image);
451               gint       off_x, off_y;
452 
453               if (! layer)
454                 {
455                   gimp_tool_message_literal (tool, tool->display,
456                                              _("There is no active layer to crop."));
457                   return;
458                 }
459 
460               if (gimp_item_is_content_locked (GIMP_ITEM (layer)))
461                 {
462                   gimp_tool_message_literal (tool, tool->display,
463                                              _("The active layer's pixels are locked."));
464                   gimp_tools_blink_lock_box (tool->display->gimp,
465                                              GIMP_ITEM (layer));
466                   return;
467                 }
468 
469               gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);
470 
471               off_x -= x;
472               off_y -= y;
473 
474               gimp_item_resize (GIMP_ITEM (layer),
475                                 GIMP_CONTEXT (options), options->fill_type,
476                                 w, h, off_x, off_y);
477             }
478           else
479             {
480               gimp_image_crop (image,
481                                GIMP_CONTEXT (options), GIMP_FILL_TRANSPARENT,
482                                x, y, w, h, options->delete_pixels);
483             }
484 
485           gimp_image_flush (image);
486         }
487     }
488 }
489 
490 static void
gimp_crop_tool_halt(GimpCropTool * crop_tool)491 gimp_crop_tool_halt (GimpCropTool *crop_tool)
492 {
493   GimpTool         *tool    = GIMP_TOOL (crop_tool);
494   GimpCropOptions  *options = GIMP_CROP_TOOL_GET_OPTIONS (crop_tool);
495 
496   if (tool->display)
497     {
498       GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
499 
500       gimp_display_shell_set_highlight (shell, NULL, 0.0);
501 
502       gimp_rectangle_options_disconnect (GIMP_RECTANGLE_OPTIONS (options),
503                                          G_CALLBACK (gimp_crop_tool_auto_shrink),
504                                          crop_tool);
505     }
506 
507   if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
508     gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
509 
510   /*  disconnect bindings manually so they are really gone *now*, we
511    *  might be in the middle of a signal emission that keeps the
512    *  widget and its bindings alive.
513    */
514   g_list_free_full (crop_tool->bindings, (GDestroyNotify) g_object_unref);
515   crop_tool->bindings = NULL;
516 
517   gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), NULL);
518   g_clear_object (&crop_tool->widget);
519 
520   tool->display  = NULL;
521   tool->drawable = NULL;
522 
523   gimp_crop_tool_update_option_defaults (crop_tool, TRUE);
524 }
525 
526 /**
527  * gimp_crop_tool_update_option_defaults:
528  * @crop_tool:
529  * @ignore_pending: %TRUE to ignore any pending crop rectangle.
530  *
531  * Sets the default Fixed: Aspect ratio and Fixed: Size option
532  * properties.
533  */
534 static void
gimp_crop_tool_update_option_defaults(GimpCropTool * crop_tool,gboolean ignore_pending)535 gimp_crop_tool_update_option_defaults (GimpCropTool *crop_tool,
536                                        gboolean      ignore_pending)
537 {
538   GimpTool             *tool      = GIMP_TOOL (crop_tool);
539   GimpToolRectangle    *rectangle = GIMP_TOOL_RECTANGLE (crop_tool->widget);
540   GimpRectangleOptions *options;
541 
542   options = GIMP_RECTANGLE_OPTIONS (GIMP_TOOL_GET_OPTIONS (tool));
543 
544   if (rectangle && ! ignore_pending)
545     {
546       /* There is a pending rectangle and we should not ignore it, so
547        * set default Fixed: Aspect ratio to the same as the current
548        * pending rectangle width/height.
549        */
550 
551       gimp_tool_rectangle_pending_size_set (rectangle,
552                                             G_OBJECT (options),
553                                             "default-aspect-numerator",
554                                             "default-aspect-denominator");
555 
556       g_object_set (G_OBJECT (options),
557                     "use-string-current", TRUE,
558                     NULL);
559     }
560   else
561     {
562       /* There is no pending rectangle, set default Fixed: Aspect
563        * ratio to that of the current image/layer.
564        */
565 
566       if (! rectangle)
567         {
568           /* ugly hack: if we don't have a widget, construct a temporary one
569            * so that we can use it to call
570            * gimp_tool_rectangle_constraint_size_set().
571            */
572 
573           GimpContext *context = gimp_get_user_context (tool->tool_info->gimp);
574           GimpDisplay *display = gimp_context_get_display (context);
575 
576           if (display)
577             {
578               GimpDisplayShell *shell = gimp_display_get_shell (display);
579 
580               rectangle = GIMP_TOOL_RECTANGLE (gimp_tool_rectangle_new (shell));
581 
582               gimp_tool_rectangle_set_constraint (
583                 rectangle, gimp_crop_tool_get_constraint (crop_tool));
584             }
585         }
586 
587       if (rectangle)
588         {
589           gimp_tool_rectangle_constraint_size_set (rectangle,
590                                                    G_OBJECT (options),
591                                                    "default-aspect-numerator",
592                                                    "default-aspect-denominator");
593 
594           if (! crop_tool->widget)
595             g_object_unref (rectangle);
596         }
597 
598       g_object_set (G_OBJECT (options),
599                     "use-string-current", FALSE,
600                     NULL);
601     }
602 }
603 
604 static GimpRectangleConstraint
gimp_crop_tool_get_constraint(GimpCropTool * crop_tool)605 gimp_crop_tool_get_constraint (GimpCropTool *crop_tool)
606 {
607   GimpCropOptions *crop_options = GIMP_CROP_TOOL_GET_OPTIONS (crop_tool);
608 
609   if (crop_options->allow_growing)
610     {
611       return GIMP_RECTANGLE_CONSTRAIN_NONE;
612     }
613   else
614     {
615       return crop_options->layer_only ? GIMP_RECTANGLE_CONSTRAIN_DRAWABLE :
616                                         GIMP_RECTANGLE_CONSTRAIN_IMAGE;
617     }
618 }
619 
620 static void
gimp_crop_tool_image_changed(GimpCropTool * crop_tool,GimpImage * image,GimpContext * context)621 gimp_crop_tool_image_changed (GimpCropTool *crop_tool,
622                               GimpImage    *image,
623                               GimpContext  *context)
624 {
625   if (crop_tool->current_image)
626     {
627       g_signal_handlers_disconnect_by_func (crop_tool->current_image,
628                                             gimp_crop_tool_image_size_changed,
629                                             NULL);
630       g_signal_handlers_disconnect_by_func (crop_tool->current_image,
631                                             gimp_crop_tool_image_active_layer_changed,
632                                             NULL);
633 
634       g_object_remove_weak_pointer (G_OBJECT (crop_tool->current_image),
635                                     (gpointer) &crop_tool->current_image);
636     }
637 
638   crop_tool->current_image = image;
639 
640   if (crop_tool->current_image)
641     {
642       g_object_add_weak_pointer (G_OBJECT (crop_tool->current_image),
643                                  (gpointer) &crop_tool->current_image);
644 
645       g_signal_connect_object (crop_tool->current_image, "size-changed",
646                                G_CALLBACK (gimp_crop_tool_image_size_changed),
647                                crop_tool,
648                                G_CONNECT_SWAPPED);
649       g_signal_connect_object (crop_tool->current_image, "active-layer-changed",
650                                G_CALLBACK (gimp_crop_tool_image_active_layer_changed),
651                                crop_tool,
652                                G_CONNECT_SWAPPED);
653     }
654 
655   /* Make sure we are connected to "size-changed" for the initial
656    * layer.
657    */
658   gimp_crop_tool_image_active_layer_changed (crop_tool);
659 
660   gimp_crop_tool_update_option_defaults (GIMP_CROP_TOOL (crop_tool), FALSE);
661 }
662 
663 static void
gimp_crop_tool_image_size_changed(GimpCropTool * crop_tool)664 gimp_crop_tool_image_size_changed (GimpCropTool *crop_tool)
665 {
666   gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
667 }
668 
669 static void
gimp_crop_tool_image_active_layer_changed(GimpCropTool * crop_tool)670 gimp_crop_tool_image_active_layer_changed (GimpCropTool *crop_tool)
671 {
672   if (crop_tool->current_layer)
673     {
674       g_signal_handlers_disconnect_by_func (crop_tool->current_layer,
675                                             gimp_crop_tool_layer_size_changed,
676                                             NULL);
677 
678       g_object_remove_weak_pointer (G_OBJECT (crop_tool->current_layer),
679                                     (gpointer) &crop_tool->current_layer);
680     }
681 
682   if (crop_tool->current_image)
683     {
684       crop_tool->current_layer =
685         gimp_image_get_active_layer (crop_tool->current_image);
686     }
687   else
688     {
689       crop_tool->current_layer = NULL;
690     }
691 
692   if (crop_tool->current_layer)
693     {
694       g_object_add_weak_pointer (G_OBJECT (crop_tool->current_layer),
695                                  (gpointer) &crop_tool->current_layer);
696 
697       g_signal_connect_object (crop_tool->current_layer, "size-changed",
698                                G_CALLBACK (gimp_crop_tool_layer_size_changed),
699                                crop_tool,
700                                G_CONNECT_SWAPPED);
701     }
702 
703   gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
704 }
705 
706 static void
gimp_crop_tool_layer_size_changed(GimpCropTool * crop_tool)707 gimp_crop_tool_layer_size_changed (GimpCropTool *crop_tool)
708 {
709   gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
710 }
711 
712 static void
gimp_crop_tool_auto_shrink(GimpCropTool * crop_tool)713 gimp_crop_tool_auto_shrink (GimpCropTool *crop_tool)
714 {
715   gboolean shrink_merged ;
716 
717   g_object_get (gimp_tool_get_options (GIMP_TOOL (crop_tool)),
718                 "shrink-merged", &shrink_merged,
719                 NULL);
720 
721   gimp_tool_rectangle_auto_shrink (GIMP_TOOL_RECTANGLE (crop_tool->widget),
722                                    shrink_merged);
723 }
724