1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimptoolrectangle.c
5  * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
6  *
7  * Based on GimpRectangleTool
8  * Copyright (C) 2007 Martin Nordholts
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 
26 #include <gegl.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 
30 #include "libgimpbase/gimpbase.h"
31 #include "libgimpmath/gimpmath.h"
32 
33 #include "display-types.h"
34 
35 #include "core/gimp.h"
36 #include "core/gimpcontext.h"
37 #include "core/gimpimage.h"
38 #include "core/gimpitem.h"
39 #include "core/gimpmarshal.h"
40 #include "core/gimppickable.h"
41 #include "core/gimppickable-auto-shrink.h"
42 
43 #include "widgets/gimpwidgets-utils.h"
44 
45 #include "gimpcanvasarc.h"
46 #include "gimpcanvascorner.h"
47 #include "gimpcanvashandle.h"
48 #include "gimpcanvasitem-utils.h"
49 #include "gimpcanvasrectangle.h"
50 #include "gimpcanvasrectangleguides.h"
51 #include "gimpdisplay.h"
52 #include "gimpdisplayshell.h"
53 #include "gimpdisplayshell-scroll.h"
54 #include "gimptoolrectangle.h"
55 
56 #include "gimp-intl.h"
57 
58 
59 /*  speed of key movement  */
60 #define ARROW_VELOCITY   25
61 
62 #define MAX_HANDLE_SIZE         50
63 #define MIN_HANDLE_SIZE         15
64 #define NARROW_MODE_HANDLE_SIZE 15
65 #define NARROW_MODE_THRESHOLD   45
66 
67 
68 enum
69 {
70   PROP_0,
71   PROP_X1,
72   PROP_Y1,
73   PROP_X2,
74   PROP_Y2,
75   PROP_CONSTRAINT,
76   PROP_PRECISION,
77   PROP_NARROW_MODE,
78   PROP_FORCE_NARROW_MODE,
79   PROP_DRAW_ELLIPSE,
80   PROP_ROUND_CORNERS,
81   PROP_CORNER_RADIUS,
82   PROP_STATUS_TITLE,
83 
84   PROP_HIGHLIGHT,
85   PROP_HIGHLIGHT_OPACITY,
86   PROP_GUIDE,
87   PROP_X,
88   PROP_Y,
89   PROP_WIDTH,
90   PROP_HEIGHT,
91   PROP_FIXED_RULE_ACTIVE,
92   PROP_FIXED_RULE,
93   PROP_DESIRED_FIXED_WIDTH,
94   PROP_DESIRED_FIXED_HEIGHT,
95   PROP_DESIRED_FIXED_SIZE_WIDTH,
96   PROP_DESIRED_FIXED_SIZE_HEIGHT,
97   PROP_ASPECT_NUMERATOR,
98   PROP_ASPECT_DENOMINATOR,
99   PROP_FIXED_CENTER
100 };
101 
102 enum
103 {
104   CHANGE_COMPLETE,
105   LAST_SIGNAL
106 };
107 
108 typedef enum
109 {
110   CLAMPED_NONE   = 0,
111   CLAMPED_LEFT   = 1 << 0,
112   CLAMPED_RIGHT  = 1 << 1,
113   CLAMPED_TOP    = 1 << 2,
114   CLAMPED_BOTTOM = 1 << 3
115 } ClampedSide;
116 
117 typedef enum
118 {
119   SIDE_TO_RESIZE_NONE,
120   SIDE_TO_RESIZE_LEFT,
121   SIDE_TO_RESIZE_RIGHT,
122   SIDE_TO_RESIZE_TOP,
123   SIDE_TO_RESIZE_BOTTOM,
124   SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY,
125   SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY,
126 } SideToResize;
127 
128 
129 #define FEQUAL(a,b)       (fabs ((a) - (b)) < 0.0001)
130 #define PIXEL_FEQUAL(a,b) (fabs ((a) - (b)) < 0.5)
131 
132 
133 struct _GimpToolRectanglePrivate
134 {
135   /* The following members are "constants", that is, variables that are setup
136    * during gimp_tool_rectangle_button_press and then only read.
137    */
138 
139   /* Whether or not the rectangle currently being rubber-banded is the
140    * first one created with this instance, this determines if we can
141    * undo it on button_release.
142    */
143   gboolean                is_first;
144 
145   /* Whether or not the rectangle currently being rubber-banded was
146    * created from scratch.
147    */
148   gboolean                is_new;
149 
150   /* Holds the coordinate that should be used as the "other side" when
151    * fixed-center is turned off.
152    */
153   gdouble                 other_side_x;
154   gdouble                 other_side_y;
155 
156   /* Holds the coordinate to be used as center when fixed-center is used. */
157   gdouble                 center_x_on_fixed_center;
158   gdouble                 center_y_on_fixed_center;
159 
160   /* True when the rectangle is being adjusted (moved or
161    * rubber-banded).
162    */
163   gboolean                rect_adjusting;
164 
165 
166   /* The rest of the members are internal state variables, that is, variables
167    * that might change during the manipulation session of the rectangle. Make
168    * sure these variables are in consistent states.
169    */
170 
171   /* Coordinates of upper left and lower right rectangle corners. */
172   gdouble                 x1, y1;
173   gdouble                 x2, y2;
174 
175   /* Integer coordinats of upper left corner and size. We must
176    * calculate this separately from the gdouble ones because sometimes
177    * we don't want to affect the integer size (e.g. when moving the
178    * rectangle), but that will be the case if we always calculate the
179    * integer coordinates based on rounded values of the gdouble
180    * coordinates even if the gdouble width remains constant.
181    *
182    * TODO: Change the internal double-representation of the rectangle
183    * to x,y width,height instead of x1,y1 x2,y2. That way we don't
184    * need to keep a separate representation of the integer version of
185    * the rectangle; rounding width an height will yield consistent
186    * results and not depend on position of the rectangle.
187    */
188   gint                    x1_int,    y1_int;
189   gint                    width_int, height_int;
190 
191   /* How to constrain the rectangle. */
192   GimpRectangleConstraint constraint;
193 
194   /* What precision the rectangle will appear to have externally (it
195    * will always be double internally)
196    */
197   GimpRectanglePrecision  precision;
198 
199   /* Previous coordinate applied to the rectangle. */
200   gdouble                 lastx;
201   gdouble                 lasty;
202 
203   /* Width and height of corner handles. */
204   gint                    corner_handle_w;
205   gint                    corner_handle_h;
206 
207   /* Width and height of side handles. */
208   gint                    top_and_bottom_handle_w;
209   gint                    left_and_right_handle_h;
210 
211   /* Whether or not the rectangle is in a 'narrow situation' i.e. it is
212    * too small for reasonable sized handle to be inside. In this case
213    * we put handles on the outside.
214    */
215   gboolean                narrow_mode;
216 
217   /* This boolean allows to always set narrow mode */
218   gboolean                force_narrow_mode;
219 
220   /* Whether or not to draw an ellipse inside the rectangle */
221   gboolean                draw_ellipse;
222 
223   /* Whether to draw round corners */
224   gboolean                round_corners;
225   gdouble                 corner_radius;
226 
227   /* The title for the statusbar coords */
228   gchar                  *status_title;
229 
230   /* For saving in case of cancellation. */
231   gdouble                 saved_x1;
232   gdouble                 saved_y1;
233   gdouble                 saved_x2;
234   gdouble                 saved_y2;
235 
236   gint                    suppress_updates;
237 
238   GimpRectangleFunction   function;
239 
240   /* The following values are externally synced with GimpRectangleOptions */
241 
242   gboolean                highlight;
243   gdouble                 highlight_opacity;
244   GimpGuidesType          guide;
245 
246   gdouble                 x;
247   gdouble                 y;
248   gdouble                 width;
249   gdouble                 height;
250 
251   gboolean                fixed_rule_active;
252   GimpRectangleFixedRule  fixed_rule;
253   gdouble                 desired_fixed_width;
254   gdouble                 desired_fixed_height;
255   gdouble                 desired_fixed_size_width;
256   gdouble                 desired_fixed_size_height;
257   gdouble                 aspect_numerator;
258   gdouble                 aspect_denominator;
259   gboolean                fixed_center;
260 
261   /* Canvas items for drawing the GUI */
262 
263   GimpCanvasItem         *guides;
264   GimpCanvasItem         *rectangle;
265   GimpCanvasItem         *ellipse;
266   GimpCanvasItem         *corners[4];
267   GimpCanvasItem         *center;
268   GimpCanvasItem         *creating_corners[4];
269   GimpCanvasItem         *handles[GIMP_N_TOOL_RECTANGLE_FUNCTIONS];
270   GimpCanvasItem         *highlight_handles[GIMP_N_TOOL_RECTANGLE_FUNCTIONS];
271 };
272 
273 
274 /*  local function prototypes  */
275 
276 static void     gimp_tool_rectangle_constructed     (GObject               *object);
277 static void     gimp_tool_rectangle_finalize        (GObject               *object);
278 static void     gimp_tool_rectangle_set_property    (GObject               *object,
279                                                      guint                  property_id,
280                                                      const GValue          *value,
281                                                      GParamSpec            *pspec);
282 static void     gimp_tool_rectangle_get_property    (GObject               *object,
283                                                      guint                  property_id,
284                                                      GValue                *value,
285                                                      GParamSpec            *pspec);
286 static void     gimp_tool_rectangle_notify          (GObject               *object,
287                                                      GParamSpec            *pspec);
288 
289 static void     gimp_tool_rectangle_changed         (GimpToolWidget        *widget);
290 static gint     gimp_tool_rectangle_button_press    (GimpToolWidget        *widget,
291                                                      const GimpCoords      *coords,
292                                                      guint32                time,
293                                                      GdkModifierType        state,
294                                                      GimpButtonPressType    press_type);
295 static void     gimp_tool_rectangle_button_release  (GimpToolWidget        *widget,
296                                                      const GimpCoords      *coords,
297                                                      guint32                time,
298                                                      GdkModifierType        state,
299                                                      GimpButtonReleaseType  release_type);
300 static void     gimp_tool_rectangle_motion          (GimpToolWidget        *widget,
301                                                      const GimpCoords      *coords,
302                                                      guint32                time,
303                                                      GdkModifierType        state);
304 static GimpHit  gimp_tool_rectangle_hit             (GimpToolWidget        *widget,
305                                                      const GimpCoords      *coords,
306                                                      GdkModifierType        state,
307                                                      gboolean               proximity);
308 static void     gimp_tool_rectangle_hover           (GimpToolWidget        *widget,
309                                                      const GimpCoords      *coords,
310                                                      GdkModifierType        state,
311                                                      gboolean               proximity);
312 static void     gimp_tool_rectangle_leave_notify    (GimpToolWidget        *widget);
313 static gboolean gimp_tool_rectangle_key_press       (GimpToolWidget        *widget,
314                                                      GdkEventKey           *kevent);
315 static void     gimp_tool_rectangle_motion_modifier (GimpToolWidget        *widget,
316                                                      GdkModifierType        key,
317                                                      gboolean               press,
318                                                      GdkModifierType        state);
319 static gboolean gimp_tool_rectangle_get_cursor      (GimpToolWidget        *widget,
320                                                      const GimpCoords      *coords,
321                                                      GdkModifierType        state,
322                                                      GimpCursorType        *cursor,
323                                                      GimpToolCursorType    *tool_cursor,
324                                                      GimpCursorModifier    *modifier);
325 
326 static void     gimp_tool_rectangle_change_complete (GimpToolRectangle     *rectangle);
327 
328 static void     gimp_tool_rectangle_update_options  (GimpToolRectangle     *rectangle);
329 static void     gimp_tool_rectangle_update_handle_sizes
330                                                     (GimpToolRectangle     *rectangle);
331 static void     gimp_tool_rectangle_update_status   (GimpToolRectangle     *rectangle);
332 
333 static void     gimp_tool_rectangle_synthesize_motion
334                                                     (GimpToolRectangle     *rectangle,
335                                                      gint               function,
336                                                      gdouble            new_x,
337                                                      gdouble            new_y);
338 
339 static GimpRectangleFunction
340                 gimp_tool_rectangle_calc_function   (GimpToolRectangle     *rectangle,
341                                                      const GimpCoords      *coords,
342                                                      gboolean               proximity);
343 static void     gimp_tool_rectangle_check_function  (GimpToolRectangle     *rectangle);
344 
345 static gboolean gimp_tool_rectangle_coord_outside   (GimpToolRectangle     *rectangle,
346                                                      const GimpCoords      *coords);
347 
348 static gboolean gimp_tool_rectangle_coord_on_handle (GimpToolRectangle     *rectangle,
349                                                      const GimpCoords      *coords,
350                                                      GimpHandleAnchor       anchor);
351 
352 static GimpHandleAnchor gimp_tool_rectangle_get_anchor
353                                                     (GimpRectangleFunction  function);
354 static gboolean gimp_tool_rectangle_rect_rubber_banding_func
355                                                     (GimpToolRectangle     *rectangle);
356 static gboolean gimp_tool_rectangle_rect_adjusting_func
357                                                     (GimpToolRectangle     *rectangle);
358 
359 static void     gimp_tool_rectangle_get_other_side  (GimpToolRectangle     *rectangle,
360                                                      gdouble              **other_x,
361                                                      gdouble              **other_y);
362 static void     gimp_tool_rectangle_get_other_side_coord
363                                                     (GimpToolRectangle     *rectangle,
364                                                      gdouble               *other_side_x,
365                                                      gdouble               *other_side_y);
366 static void     gimp_tool_rectangle_set_other_side_coord
367                                                     (GimpToolRectangle     *rectangle,
368                                                      gdouble                other_side_x,
369                                                      gdouble                other_side_y);
370 
371 static void     gimp_tool_rectangle_apply_coord     (GimpToolRectangle     *rectangle,
372                                                      gdouble                coord_x,
373                                                      gdouble                coord_y);
374 static void     gimp_tool_rectangle_setup_snap_offsets
375                                                     (GimpToolRectangle     *rectangle,
376                                                      const GimpCoords      *coords);
377 
378 static void     gimp_tool_rectangle_clamp           (GimpToolRectangle     *rectangle,
379                                                      ClampedSide           *clamped_sides,
380                                                      GimpRectangleConstraint constraint,
381                                                      gboolean               symmetrically);
382 static void     gimp_tool_rectangle_clamp_width     (GimpToolRectangle     *rectangle,
383                                                      ClampedSide           *clamped_sides,
384                                                      GimpRectangleConstraint constraint,
385                                                      gboolean               symmetrically);
386 static void     gimp_tool_rectangle_clamp_height    (GimpToolRectangle     *rectangle,
387                                                      ClampedSide           *clamped_sides,
388                                                      GimpRectangleConstraint constraint,
389                                                      gboolean               symmetrically);
390 
391 static void     gimp_tool_rectangle_keep_inside     (GimpToolRectangle     *rectangle,
392                                                      GimpRectangleConstraint constraint);
393 static void     gimp_tool_rectangle_keep_inside_horizontally
394                                                     (GimpToolRectangle     *rectangle,
395                                                      GimpRectangleConstraint constraint);
396 static void     gimp_tool_rectangle_keep_inside_vertically
397                                                     (GimpToolRectangle     *rectangle,
398                                                      GimpRectangleConstraint constraint);
399 
400 static void     gimp_tool_rectangle_apply_fixed_width
401                                                     (GimpToolRectangle     *rectangle,
402                                                      GimpRectangleConstraint constraint,
403                                                      gdouble                width);
404 static void     gimp_tool_rectangle_apply_fixed_height
405                                                     (GimpToolRectangle     *rectangle,
406                                                      GimpRectangleConstraint constraint,
407                                                      gdouble                height);
408 
409 static void     gimp_tool_rectangle_apply_aspect    (GimpToolRectangle     *rectangle,
410                                                      gdouble                aspect,
411                                                      gint                   clamped_sides);
412 
413 static void     gimp_tool_rectangle_update_with_coord
414                                                     (GimpToolRectangle     *rectangle,
415                                                      gdouble                new_x,
416                                                      gdouble                new_y);
417 static void     gimp_tool_rectangle_apply_fixed_rule(GimpToolRectangle     *rectangle);
418 
419 static void     gimp_tool_rectangle_get_constraints (GimpToolRectangle     *rectangle,
420                                                      gint                  *min_x,
421                                                      gint                  *min_y,
422                                                      gint                  *max_x,
423                                                      gint                  *max_y,
424                                                      GimpRectangleConstraint constraint);
425 
426 static void     gimp_tool_rectangle_handle_general_clamping
427                                                     (GimpToolRectangle     *rectangle);
428 static void     gimp_tool_rectangle_update_int_rect (GimpToolRectangle     *rectangle);
429 static void     gimp_tool_rectangle_adjust_coord    (GimpToolRectangle     *rectangle,
430                                                      gdouble                coord_x_input,
431                                                      gdouble                coord_y_input,
432                                                      gdouble               *coord_x_output,
433                                                      gdouble               *coord_y_output);
434 static void     gimp_tool_rectangle_recalculate_center_xy
435                                                     (GimpToolRectangle     *rectangle);
436 
437 
438 G_DEFINE_TYPE_WITH_PRIVATE (GimpToolRectangle, gimp_tool_rectangle,
439                             GIMP_TYPE_TOOL_WIDGET)
440 
441 #define parent_class gimp_tool_rectangle_parent_class
442 
443 static guint rectangle_signals[LAST_SIGNAL] = { 0, };
444 
445 
446 static void
gimp_tool_rectangle_class_init(GimpToolRectangleClass * klass)447 gimp_tool_rectangle_class_init (GimpToolRectangleClass *klass)
448 {
449   GObjectClass        *object_class = G_OBJECT_CLASS (klass);
450   GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass);
451 
452   object_class->constructed     = gimp_tool_rectangle_constructed;
453   object_class->finalize        = gimp_tool_rectangle_finalize;
454   object_class->set_property    = gimp_tool_rectangle_set_property;
455   object_class->get_property    = gimp_tool_rectangle_get_property;
456   object_class->notify          = gimp_tool_rectangle_notify;
457 
458   widget_class->changed         = gimp_tool_rectangle_changed;
459   widget_class->button_press    = gimp_tool_rectangle_button_press;
460   widget_class->button_release  = gimp_tool_rectangle_button_release;
461   widget_class->motion          = gimp_tool_rectangle_motion;
462   widget_class->hit             = gimp_tool_rectangle_hit;
463   widget_class->hover           = gimp_tool_rectangle_hover;
464   widget_class->leave_notify    = gimp_tool_rectangle_leave_notify;
465   widget_class->key_press       = gimp_tool_rectangle_key_press;
466   widget_class->motion_modifier = gimp_tool_rectangle_motion_modifier;
467   widget_class->get_cursor      = gimp_tool_rectangle_get_cursor;
468   widget_class->update_on_scale = TRUE;
469 
470   rectangle_signals[CHANGE_COMPLETE] =
471     g_signal_new ("change-complete",
472                   G_TYPE_FROM_CLASS (klass),
473                   G_SIGNAL_RUN_FIRST,
474                   G_STRUCT_OFFSET (GimpToolRectangleClass, change_complete),
475                   NULL, NULL,
476                   g_cclosure_marshal_VOID__VOID,
477                   G_TYPE_NONE, 0);
478 
479   g_object_class_install_property (object_class, PROP_X1,
480                                    g_param_spec_double ("x1",
481                                                         NULL, NULL,
482                                                         -GIMP_MAX_IMAGE_SIZE,
483                                                         GIMP_MAX_IMAGE_SIZE,
484                                                         0.0,
485                                                         GIMP_PARAM_READWRITE |
486                                                         G_PARAM_CONSTRUCT));
487 
488   g_object_class_install_property (object_class, PROP_Y1,
489                                    g_param_spec_double ("y1",
490                                                         NULL, NULL,
491                                                         -GIMP_MAX_IMAGE_SIZE,
492                                                         GIMP_MAX_IMAGE_SIZE,
493                                                         0.0,
494                                                         GIMP_PARAM_READWRITE |
495                                                         G_PARAM_CONSTRUCT));
496 
497   g_object_class_install_property (object_class, PROP_X2,
498                                    g_param_spec_double ("x2",
499                                                         NULL, NULL,
500                                                         -GIMP_MAX_IMAGE_SIZE,
501                                                         GIMP_MAX_IMAGE_SIZE,
502                                                         0.0,
503                                                         GIMP_PARAM_READWRITE |
504                                                         G_PARAM_CONSTRUCT));
505 
506   g_object_class_install_property (object_class, PROP_Y2,
507                                    g_param_spec_double ("y2",
508                                                         NULL, NULL,
509                                                         -GIMP_MAX_IMAGE_SIZE,
510                                                         GIMP_MAX_IMAGE_SIZE,
511                                                         0.0,
512                                                         GIMP_PARAM_READWRITE |
513                                                         G_PARAM_CONSTRUCT));
514 
515   g_object_class_install_property (object_class, PROP_CONSTRAINT,
516                                    g_param_spec_enum ("constraint",
517                                                       NULL, NULL,
518                                                       GIMP_TYPE_RECTANGLE_CONSTRAINT,
519                                                       GIMP_RECTANGLE_CONSTRAIN_NONE,
520                                                       GIMP_PARAM_READWRITE |
521                                                       G_PARAM_CONSTRUCT));
522 
523   g_object_class_install_property (object_class, PROP_PRECISION,
524                                    g_param_spec_enum ("precision",
525                                                       NULL, NULL,
526                                                       GIMP_TYPE_RECTANGLE_PRECISION,
527                                                       GIMP_RECTANGLE_PRECISION_INT,
528                                                       GIMP_PARAM_READWRITE |
529                                                       G_PARAM_CONSTRUCT));
530 
531   g_object_class_install_property (object_class, PROP_NARROW_MODE,
532                                    g_param_spec_boolean ("narrow-mode",
533                                                          NULL, NULL,
534                                                          FALSE,
535                                                          GIMP_PARAM_READWRITE |
536                                                          G_PARAM_CONSTRUCT));
537 
538   g_object_class_install_property (object_class, PROP_FORCE_NARROW_MODE,
539                                    g_param_spec_boolean ("force-narrow-mode",
540                                                          NULL, NULL,
541                                                          FALSE,
542                                                          GIMP_PARAM_READWRITE |
543                                                          G_PARAM_CONSTRUCT));
544 
545   g_object_class_install_property (object_class, PROP_DRAW_ELLIPSE,
546                                    g_param_spec_boolean ("draw-ellipse",
547                                                          NULL, NULL,
548                                                          FALSE,
549                                                          GIMP_PARAM_READWRITE |
550                                                          G_PARAM_CONSTRUCT));
551 
552   g_object_class_install_property (object_class, PROP_ROUND_CORNERS,
553                                    g_param_spec_boolean ("round-corners",
554                                                          NULL, NULL,
555                                                          FALSE,
556                                                          GIMP_PARAM_READWRITE |
557                                                          G_PARAM_CONSTRUCT));
558 
559   g_object_class_install_property (object_class, PROP_CORNER_RADIUS,
560                                    g_param_spec_double ("corner-radius",
561                                                         NULL, NULL,
562                                                         0.0, 10000.0, 10.0,
563                                                         GIMP_PARAM_READWRITE |
564                                                         G_PARAM_CONSTRUCT));
565 
566   g_object_class_install_property (object_class, PROP_STATUS_TITLE,
567                                    g_param_spec_string ("status-title",
568                                                         NULL, NULL,
569                                                         _("Rectangle: "),
570                                                         GIMP_PARAM_READWRITE |
571                                                         G_PARAM_CONSTRUCT));
572 
573   g_object_class_install_property (object_class, PROP_HIGHLIGHT,
574                                    g_param_spec_boolean ("highlight",
575                                                          NULL, NULL,
576                                                          FALSE,
577                                                          GIMP_PARAM_READWRITE |
578                                                          G_PARAM_CONSTRUCT));
579 
580   g_object_class_install_property (object_class, PROP_HIGHLIGHT_OPACITY,
581                                    g_param_spec_double ("highlight-opacity",
582                                                         NULL, NULL,
583                                                         0.0, 1.0, 0.5,
584                                                         GIMP_PARAM_READWRITE |
585                                                         G_PARAM_CONSTRUCT));
586 
587   g_object_class_install_property (object_class, PROP_GUIDE,
588                                    g_param_spec_enum ("guide",
589                                                       NULL, NULL,
590                                                       GIMP_TYPE_GUIDES_TYPE,
591                                                       GIMP_GUIDES_NONE,
592                                                       GIMP_PARAM_READWRITE |
593                                                       G_PARAM_CONSTRUCT));
594 
595   g_object_class_install_property (object_class, PROP_X,
596                                    g_param_spec_double ("x",
597                                                         NULL, NULL,
598                                                         -GIMP_MAX_IMAGE_SIZE,
599                                                         GIMP_MAX_IMAGE_SIZE,
600                                                         0.0,
601                                                         GIMP_PARAM_READWRITE |
602                                                         G_PARAM_CONSTRUCT));
603 
604   g_object_class_install_property (object_class, PROP_Y,
605                                    g_param_spec_double ("y",
606                                                         NULL, NULL,
607                                                         -GIMP_MAX_IMAGE_SIZE,
608                                                         GIMP_MAX_IMAGE_SIZE,
609                                                         0.0,
610                                                         GIMP_PARAM_READWRITE |
611                                                         G_PARAM_CONSTRUCT));
612 
613   g_object_class_install_property (object_class, PROP_WIDTH,
614                                    g_param_spec_double ("width",
615                                                         NULL, NULL,
616                                                         0.0,
617                                                         GIMP_MAX_IMAGE_SIZE,
618                                                         0.0,
619                                                         GIMP_PARAM_READWRITE |
620                                                         G_PARAM_CONSTRUCT));
621 
622   g_object_class_install_property (object_class, PROP_HEIGHT,
623                                    g_param_spec_double ("height",
624                                                         NULL, NULL,
625                                                         0.0,
626                                                         GIMP_MAX_IMAGE_SIZE,
627                                                         0.0,
628                                                         GIMP_PARAM_READWRITE |
629                                                         G_PARAM_CONSTRUCT));
630 
631   g_object_class_install_property (object_class, PROP_FIXED_RULE_ACTIVE,
632                                    g_param_spec_boolean ("fixed-rule-active",
633                                                          NULL, NULL,
634                                                          FALSE,
635                                                          GIMP_PARAM_READWRITE |
636                                                          G_PARAM_CONSTRUCT));
637 
638   g_object_class_install_property (object_class, PROP_FIXED_RULE,
639                                    g_param_spec_enum ("fixed-rule",
640                                                       NULL, NULL,
641                                                       GIMP_TYPE_RECTANGLE_FIXED_RULE,
642                                                       GIMP_RECTANGLE_FIXED_ASPECT,
643                                                       GIMP_PARAM_READWRITE |
644                                                       G_PARAM_CONSTRUCT));
645 
646   g_object_class_install_property (object_class, PROP_DESIRED_FIXED_WIDTH,
647                                    g_param_spec_double ("desired-fixed-width",
648                                                         NULL, NULL,
649                                                         0.0, GIMP_MAX_IMAGE_SIZE,
650                                                         100.0,
651                                                         GIMP_PARAM_READWRITE |
652                                                         G_PARAM_CONSTRUCT));
653 
654   g_object_class_install_property (object_class, PROP_DESIRED_FIXED_HEIGHT,
655                                    g_param_spec_double ("desired-fixed-height",
656                                                         NULL, NULL,
657                                                         0.0, GIMP_MAX_IMAGE_SIZE,
658                                                         100.0,
659                                                         GIMP_PARAM_READWRITE |
660                                                         G_PARAM_CONSTRUCT));
661 
662   g_object_class_install_property (object_class, PROP_DESIRED_FIXED_SIZE_WIDTH,
663                                    g_param_spec_double ("desired-fixed-size-width",
664                                                         NULL, NULL,
665                                                         0.0, GIMP_MAX_IMAGE_SIZE,
666                                                         100.0,
667                                                         GIMP_PARAM_READWRITE |
668                                                         G_PARAM_CONSTRUCT));
669 
670   g_object_class_install_property (object_class, PROP_DESIRED_FIXED_SIZE_HEIGHT,
671                                    g_param_spec_double ("desired-fixed-size-height",
672                                                         NULL, NULL,
673                                                         0.0, GIMP_MAX_IMAGE_SIZE,
674                                                         100.0,
675                                                         GIMP_PARAM_READWRITE |
676                                                         G_PARAM_CONSTRUCT));
677 
678   g_object_class_install_property (object_class, PROP_ASPECT_NUMERATOR,
679                                    g_param_spec_double ("aspect-numerator",
680                                                         NULL, NULL,
681                                                         0.0, GIMP_MAX_IMAGE_SIZE,
682                                                         1.0,
683                                                         GIMP_PARAM_READWRITE |
684                                                         G_PARAM_CONSTRUCT));
685 
686   g_object_class_install_property (object_class, PROP_ASPECT_DENOMINATOR,
687                                    g_param_spec_double ("aspect-denominator",
688                                                         NULL, NULL,
689                                                         0.0, GIMP_MAX_IMAGE_SIZE,
690                                                         1.0,
691                                                         GIMP_PARAM_READWRITE |
692                                                         G_PARAM_CONSTRUCT));
693 
694   g_object_class_install_property (object_class, PROP_FIXED_CENTER,
695                                    g_param_spec_boolean ("fixed-center",
696                                                          NULL, NULL,
697                                                          FALSE,
698                                                          GIMP_PARAM_READWRITE |
699                                                          G_PARAM_CONSTRUCT));
700 }
701 
702 static void
gimp_tool_rectangle_init(GimpToolRectangle * rectangle)703 gimp_tool_rectangle_init (GimpToolRectangle *rectangle)
704 {
705   rectangle->private = gimp_tool_rectangle_get_instance_private (rectangle);
706 
707   rectangle->private->function = GIMP_TOOL_RECTANGLE_CREATING;
708   rectangle->private->is_first = TRUE;
709 }
710 
711 static void
gimp_tool_rectangle_constructed(GObject * object)712 gimp_tool_rectangle_constructed (GObject *object)
713 {
714   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (object);
715   GimpToolWidget           *widget    = GIMP_TOOL_WIDGET (object);
716   GimpToolRectanglePrivate *private   = rectangle->private;
717   GimpCanvasGroup          *stroke_group;
718   gint                      i;
719 
720   G_OBJECT_CLASS (parent_class)->constructed (object);
721 
722   stroke_group = gimp_tool_widget_add_stroke_group (widget);
723 
724   gimp_tool_widget_push_group (widget, stroke_group);
725 
726   private->guides = gimp_tool_widget_add_rectangle_guides (widget,
727                                                            0, 0, 10, 10,
728                                                            GIMP_GUIDES_NONE);
729 
730   private->rectangle = gimp_tool_widget_add_rectangle (widget,
731                                                        0, 0, 10, 10,
732                                                        FALSE);
733 
734   private->ellipse = gimp_tool_widget_add_arc (widget,
735                                                0, 0, 10, 10,
736                                                0.0, 2 * G_PI,
737                                                FALSE);
738 
739   for (i = 0; i < 4; i++)
740     private->corners[i] = gimp_tool_widget_add_arc (widget,
741                                                     0, 0, 10, 10,
742                                                     0.0, 2 * G_PI,
743                                                     FALSE);
744 
745   gimp_tool_widget_pop_group (widget);
746 
747   private->center = gimp_tool_widget_add_handle (widget,
748                                                  GIMP_HANDLE_CROSS,
749                                                  0, 0,
750                                                  GIMP_CANVAS_HANDLE_SIZE_SMALL,
751                                                  GIMP_CANVAS_HANDLE_SIZE_SMALL,
752                                                  GIMP_HANDLE_ANCHOR_CENTER);
753 
754   gimp_tool_widget_push_group (widget, stroke_group);
755 
756   private->creating_corners[0] =
757     gimp_tool_widget_add_corner (widget,
758                                  0, 0, 10, 10,
759                                  GIMP_HANDLE_ANCHOR_NORTH_WEST,
760                                  10, 10,
761                                  FALSE);
762 
763   private->creating_corners[1] =
764     gimp_tool_widget_add_corner (widget,
765                                  0, 0, 10, 10,
766                                  GIMP_HANDLE_ANCHOR_NORTH_EAST,
767                                  10, 10,
768                                  FALSE);
769 
770   private->creating_corners[2] =
771     gimp_tool_widget_add_corner (widget,
772                                  0, 0, 10, 10,
773                                  GIMP_HANDLE_ANCHOR_SOUTH_WEST,
774                                  10, 10,
775                                  FALSE);
776 
777   private->creating_corners[3] =
778     gimp_tool_widget_add_corner (widget,
779                                  0, 0, 10, 10,
780                                  GIMP_HANDLE_ANCHOR_SOUTH_EAST,
781                                  10, 10,
782                                  FALSE);
783 
784   gimp_tool_widget_pop_group (widget);
785 
786   for (i = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
787        i <= GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM;
788        i++)
789     {
790       GimpHandleAnchor anchor;
791 
792       anchor = gimp_tool_rectangle_get_anchor (i);
793 
794       gimp_tool_widget_push_group (widget, stroke_group);
795 
796       private->handles[i] = gimp_tool_widget_add_corner (widget,
797                                                          0, 0, 10, 10,
798                                                          anchor,
799                                                          10, 10,
800                                                          FALSE);
801 
802       gimp_tool_widget_pop_group (widget);
803 
804       private->highlight_handles[i] = gimp_tool_widget_add_corner (widget,
805                                                                    0, 0, 10, 10,
806                                                                    anchor,
807                                                                    10, 10,
808                                                                    FALSE);
809       gimp_canvas_item_set_highlight (private->highlight_handles[i], TRUE);
810     }
811 
812   gimp_tool_rectangle_changed (widget);
813 }
814 
815 static void
gimp_tool_rectangle_finalize(GObject * object)816 gimp_tool_rectangle_finalize (GObject *object)
817 {
818   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (object);
819   GimpToolRectanglePrivate *private   = rectangle->private;
820 
821   g_clear_pointer (&private->status_title, g_free);
822 
823   G_OBJECT_CLASS (parent_class)->finalize (object);
824 }
825 
826 static void
gimp_tool_rectangle_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)827 gimp_tool_rectangle_set_property (GObject      *object,
828                                   guint         property_id,
829                                   const GValue *value,
830                                   GParamSpec   *pspec)
831 {
832   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (object);
833   GimpToolRectanglePrivate *private   = rectangle->private;
834 
835   switch (property_id)
836     {
837     case PROP_X1:
838       private->x1 = g_value_get_double (value);
839       break;
840     case PROP_Y1:
841       private->y1 = g_value_get_double (value);
842       break;
843     case PROP_X2:
844       private->x2 = g_value_get_double (value);
845       break;
846     case PROP_Y2:
847       private->y2 = g_value_get_double (value);
848       break;
849 
850     case PROP_CONSTRAINT:
851       private->constraint = g_value_get_enum (value);
852       break;
853     case PROP_PRECISION:
854       private->precision = g_value_get_enum (value);
855       break;
856 
857     case PROP_NARROW_MODE:
858       private->narrow_mode = g_value_get_boolean (value);
859       break;
860     case PROP_FORCE_NARROW_MODE:
861       private->force_narrow_mode = g_value_get_boolean (value);
862       break;
863     case PROP_DRAW_ELLIPSE:
864       private->draw_ellipse = g_value_get_boolean (value);
865       break;
866     case PROP_ROUND_CORNERS:
867       private->round_corners = g_value_get_boolean (value);
868       break;
869     case PROP_CORNER_RADIUS:
870       private->corner_radius = g_value_get_double (value);
871       break;
872 
873     case PROP_STATUS_TITLE:
874       g_free (private->status_title);
875       private->status_title = g_value_dup_string (value);
876       if (! private->status_title)
877         private->status_title = g_strdup (_("Rectangle: "));
878       break;
879 
880     case PROP_HIGHLIGHT:
881       private->highlight = g_value_get_boolean (value);
882       break;
883     case PROP_HIGHLIGHT_OPACITY:
884       private->highlight_opacity = g_value_get_double (value);
885       break;
886     case PROP_GUIDE:
887       private->guide = g_value_get_enum (value);
888       break;
889 
890     case PROP_X:
891       private->x = g_value_get_double (value);
892       break;
893     case PROP_Y:
894       private->y = g_value_get_double (value);
895       break;
896     case PROP_WIDTH:
897       private->width = g_value_get_double (value);
898       break;
899     case PROP_HEIGHT:
900       private->height = g_value_get_double (value);
901       break;
902 
903     case PROP_FIXED_RULE_ACTIVE:
904       private->fixed_rule_active = g_value_get_boolean (value);
905       break;
906     case PROP_FIXED_RULE:
907       private->fixed_rule = g_value_get_enum (value);
908       break;
909     case PROP_DESIRED_FIXED_WIDTH:
910       private->desired_fixed_width = g_value_get_double (value);
911       break;
912     case PROP_DESIRED_FIXED_HEIGHT:
913       private->desired_fixed_height = g_value_get_double (value);
914       break;
915     case PROP_DESIRED_FIXED_SIZE_WIDTH:
916       private->desired_fixed_size_width = g_value_get_double (value);
917       break;
918     case PROP_DESIRED_FIXED_SIZE_HEIGHT:
919       private->desired_fixed_size_height = g_value_get_double (value);
920       break;
921     case PROP_ASPECT_NUMERATOR:
922       private->aspect_numerator = g_value_get_double (value);
923       break;
924     case PROP_ASPECT_DENOMINATOR:
925       private->aspect_denominator = g_value_get_double (value);
926       break;
927 
928     case PROP_FIXED_CENTER:
929       private->fixed_center = g_value_get_boolean (value);
930       break;
931 
932     default:
933       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
934       break;
935     }
936 }
937 
938 static void
gimp_tool_rectangle_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)939 gimp_tool_rectangle_get_property (GObject    *object,
940                                   guint       property_id,
941                                   GValue     *value,
942                                   GParamSpec *pspec)
943 {
944   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (object);
945   GimpToolRectanglePrivate *private   = rectangle->private;
946 
947   switch (property_id)
948     {
949     case PROP_X1:
950       g_value_set_double (value, private->x1);
951       break;
952     case PROP_Y1:
953       g_value_set_double (value, private->y1);
954       break;
955     case PROP_X2:
956       g_value_set_double (value, private->x2);
957       break;
958     case PROP_Y2:
959       g_value_set_double (value, private->y2);
960       break;
961 
962     case PROP_CONSTRAINT:
963       g_value_set_enum (value, private->constraint);
964       break;
965     case PROP_PRECISION:
966       g_value_set_enum (value, private->precision);
967       break;
968 
969     case PROP_NARROW_MODE:
970       g_value_set_boolean (value, private->narrow_mode);
971       break;
972     case PROP_FORCE_NARROW_MODE:
973       g_value_set_boolean (value, private->force_narrow_mode);
974       break;
975     case PROP_DRAW_ELLIPSE:
976       g_value_set_boolean (value, private->draw_ellipse);
977       break;
978     case PROP_ROUND_CORNERS:
979       g_value_set_boolean (value, private->round_corners);
980       break;
981     case PROP_CORNER_RADIUS:
982       g_value_set_double (value, private->corner_radius);
983       break;
984 
985     case PROP_STATUS_TITLE:
986       g_value_set_string (value, private->status_title);
987       break;
988 
989     case PROP_HIGHLIGHT:
990       g_value_set_boolean (value, private->highlight);
991       break;
992     case PROP_HIGHLIGHT_OPACITY:
993       g_value_set_double (value, private->highlight_opacity);
994       break;
995     case PROP_GUIDE:
996       g_value_set_enum (value, private->guide);
997       break;
998 
999     case PROP_X:
1000       g_value_set_double (value, private->x);
1001       break;
1002     case PROP_Y:
1003       g_value_set_double (value, private->y);
1004       break;
1005     case PROP_WIDTH:
1006       g_value_set_double (value, private->width);
1007       break;
1008     case PROP_HEIGHT:
1009       g_value_set_double (value, private->height);
1010       break;
1011 
1012     case PROP_FIXED_RULE_ACTIVE:
1013       g_value_set_boolean (value, private->fixed_rule_active);
1014       break;
1015     case PROP_FIXED_RULE:
1016       g_value_set_enum (value, private->fixed_rule);
1017       break;
1018     case PROP_DESIRED_FIXED_WIDTH:
1019       g_value_set_double (value, private->desired_fixed_width);
1020       break;
1021     case PROP_DESIRED_FIXED_HEIGHT:
1022       g_value_set_double (value, private->desired_fixed_height);
1023       break;
1024     case PROP_DESIRED_FIXED_SIZE_WIDTH:
1025       g_value_set_double (value, private->desired_fixed_size_width);
1026       break;
1027     case PROP_DESIRED_FIXED_SIZE_HEIGHT:
1028       g_value_set_double (value, private->desired_fixed_size_height);
1029       break;
1030     case PROP_ASPECT_NUMERATOR:
1031       g_value_set_double (value, private->aspect_numerator);
1032       break;
1033     case PROP_ASPECT_DENOMINATOR:
1034       g_value_set_double (value, private->aspect_denominator);
1035       break;
1036 
1037     case PROP_FIXED_CENTER:
1038       g_value_set_boolean (value, private->fixed_center);
1039       break;
1040 
1041     default:
1042       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1043       break;
1044     }
1045 }
1046 
1047 static void
gimp_tool_rectangle_notify(GObject * object,GParamSpec * pspec)1048 gimp_tool_rectangle_notify (GObject    *object,
1049                             GParamSpec *pspec)
1050 {
1051   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (object);
1052   GimpToolRectanglePrivate *private   = rectangle->private;
1053 
1054   if (G_OBJECT_CLASS (parent_class)->notify)
1055     G_OBJECT_CLASS (parent_class)->notify (object, pspec);
1056 
1057   if (! strcmp (pspec->name, "x1") ||
1058       ! strcmp (pspec->name, "y1") ||
1059       ! strcmp (pspec->name, "x2") ||
1060       ! strcmp (pspec->name, "y2"))
1061     {
1062       gimp_tool_rectangle_update_int_rect (rectangle);
1063 
1064       gimp_tool_rectangle_recalculate_center_xy (rectangle);
1065 
1066       gimp_tool_rectangle_update_options (rectangle);
1067     }
1068   else if (! strcmp  (pspec->name, "x") &&
1069            ! PIXEL_FEQUAL (private->x1, private->x))
1070     {
1071       gimp_tool_rectangle_synthesize_motion (rectangle,
1072                                              GIMP_TOOL_RECTANGLE_MOVING,
1073                                              private->x,
1074                                              private->y1);
1075     }
1076   else if (! strcmp  (pspec->name, "y") &&
1077            ! PIXEL_FEQUAL (private->y1, private->y))
1078     {
1079       gimp_tool_rectangle_synthesize_motion (rectangle,
1080                                              GIMP_TOOL_RECTANGLE_MOVING,
1081                                              private->x1,
1082                                              private->y);
1083     }
1084   else if (! strcmp  (pspec->name, "width") &&
1085            ! PIXEL_FEQUAL (private->x2 - private->x1, private->width))
1086     {
1087       /* Calculate x2, y2 that will create a rectangle of given width,
1088        * for the current options.
1089        */
1090       gdouble x2;
1091 
1092       if (private->fixed_center)
1093         {
1094           x2 = private->center_x_on_fixed_center +
1095                private->width / 2;
1096         }
1097       else
1098         {
1099           x2 = private->x1 + private->width;
1100         }
1101 
1102       gimp_tool_rectangle_synthesize_motion (rectangle,
1103                                              GIMP_TOOL_RECTANGLE_RESIZING_RIGHT,
1104                                              x2,
1105                                              private->y2);
1106     }
1107   else if (! strcmp  (pspec->name, "height") &&
1108            ! PIXEL_FEQUAL (private->y2 - private->y1, private->height))
1109     {
1110       /* Calculate x2, y2 that will create a rectangle of given
1111        * height, for the current options.
1112        */
1113       gdouble y2;
1114 
1115       if (private->fixed_center)
1116         {
1117           y2 = private->center_y_on_fixed_center +
1118                private->height / 2;
1119         }
1120       else
1121         {
1122           y2 = private->y1 + private->height;
1123         }
1124 
1125       gimp_tool_rectangle_synthesize_motion (rectangle,
1126                                              GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM,
1127                                              private->x2,
1128                                              y2);
1129     }
1130   else if (! strcmp (pspec->name, "desired-fixed-size-width"))
1131     {
1132       /* We are only interested in when width and height swaps, so
1133        * it's enough to only check e.g. for width.
1134        */
1135 
1136       gdouble width  = private->x2 - private->x1;
1137       gdouble height = private->y2 - private->y1;
1138 
1139       /* Depending on a bunch of conditions, we might want to
1140        * immedieately switch width and height of the pending
1141        * rectangle.
1142        */
1143       if (private->fixed_rule_active                          &&
1144 #if 0
1145           tool->button_press_state                            == 0    &&
1146           tool->active_modifier_state                         == 0    &&
1147 #endif
1148           FEQUAL (private->desired_fixed_size_width,  height) &&
1149           FEQUAL (private->desired_fixed_size_height, width))
1150         {
1151           gdouble x = private->x1;
1152           gdouble y = private->y1;
1153 
1154           gimp_tool_rectangle_synthesize_motion (rectangle,
1155                                                  GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT,
1156                                                  private->x2,
1157                                                  private->y2);
1158 
1159           /* For some reason these needs to be set separately... */
1160           g_object_set (rectangle,
1161                         "x", x,
1162                         NULL);
1163           g_object_set (rectangle,
1164                         "y", y,
1165                         NULL);
1166         }
1167     }
1168   else if (! strcmp (pspec->name, "aspect-numerator"))
1169     {
1170       /* We are only interested in when numerator and denominator
1171        * swaps, so it's enough to only check e.g. for numerator.
1172        */
1173 
1174       double    width             = private->x2 - private->x1;
1175       double    height            = private->y2 - private->y1;
1176       gdouble   new_inverse_ratio = private->aspect_denominator /
1177                                     private->aspect_numerator;
1178       gdouble   lower_ratio;
1179       gdouble   higher_ratio;
1180 
1181       /* The ratio of the Fixed: Aspect ratio rule and the pending
1182        * rectangle is very rarely exactly the same so use an
1183        * interval. For small rectangles the below code will
1184        * automatically yield a more generous accepted ratio interval
1185        * which is exactly what we want.
1186        */
1187       if (width > height && height > 1.0)
1188         {
1189           lower_ratio  = width / (height + 1.0);
1190           higher_ratio = width / (height - 1.0);
1191         }
1192       else
1193         {
1194           lower_ratio  = (width - 1.0) / height;
1195           higher_ratio = (width + 1.0) / height;
1196         }
1197 
1198       /* Depending on a bunch of conditions, we might want to
1199        * immedieately switch width and height of the pending
1200        * rectangle.
1201        */
1202       if (private->fixed_rule_active               &&
1203 #if 0
1204           tool->button_press_state    == 0                 &&
1205           tool->active_modifier_state == 0                 &&
1206 #endif
1207           lower_ratio                 <  new_inverse_ratio &&
1208           higher_ratio                >  new_inverse_ratio)
1209         {
1210           gdouble new_x2 = private->x1 + private->y2 - private->y1;
1211           gdouble new_y2 = private->y1 + private->x2 - private->x1;
1212 
1213           gimp_tool_rectangle_synthesize_motion (rectangle,
1214                                                  GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT,
1215                                                  new_x2,
1216                                                  new_y2);
1217         }
1218     }
1219 }
1220 
1221 static void
gimp_tool_rectangle_changed(GimpToolWidget * widget)1222 gimp_tool_rectangle_changed (GimpToolWidget *widget)
1223 {
1224   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
1225   GimpToolRectanglePrivate *private   = rectangle->private;
1226   GimpDisplayShell         *shell     = gimp_tool_widget_get_shell (widget);
1227   gdouble                   x1, y1, x2, y2;
1228   gint                      handle_width;
1229   gint                      handle_height;
1230   gint                      i;
1231 
1232   gimp_tool_rectangle_update_handle_sizes (rectangle);
1233 
1234   gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
1235 
1236   gimp_canvas_rectangle_guides_set (private->guides,
1237                                     x1, y1,
1238                                     x2 - x1,
1239                                     y2 - y1,
1240                                     private->guide, 4);
1241 
1242   gimp_canvas_rectangle_set (private->rectangle,
1243                              x1, y1,
1244                              x2 - x1,
1245                              y2 - y1);
1246 
1247   if (private->draw_ellipse)
1248     {
1249       gimp_canvas_arc_set (private->ellipse,
1250                            (x1 + x2) / 2.0,
1251                            (y1 + y2) / 2.0,
1252                            (x2 - x1) / 2.0,
1253                            (y2 - y1) / 2.0,
1254                            0.0, 2 * G_PI);
1255       gimp_canvas_item_set_visible (private->ellipse, TRUE);
1256     }
1257   else
1258     {
1259       gimp_canvas_item_set_visible (private->ellipse, FALSE);
1260     }
1261 
1262   if (private->round_corners && private->corner_radius > 0.0)
1263     {
1264       gdouble radius;
1265 
1266       radius = MIN (private->corner_radius,
1267                     MIN ((x2 - x1) / 2.0, (y2 - y1) / 2.0));
1268 
1269       gimp_canvas_arc_set (private->corners[0],
1270                            x1 + radius,
1271                            y1 + radius,
1272                            radius, radius,
1273                            G_PI / 2.0, G_PI / 2.0);
1274 
1275       gimp_canvas_arc_set (private->corners[1],
1276                            x2 - radius,
1277                            y1 + radius,
1278                            radius, radius,
1279                            0.0, G_PI / 2.0);
1280 
1281       gimp_canvas_arc_set (private->corners[2],
1282                            x1 + radius,
1283                            y2 - radius,
1284                            radius, radius,
1285                            G_PI, G_PI / 2.0);
1286 
1287       gimp_canvas_arc_set (private->corners[3],
1288                            x2 - radius,
1289                            y2 - radius,
1290                            radius, radius,
1291                            G_PI * 1.5, G_PI / 2.0);
1292 
1293       for (i = 0; i < 4; i++)
1294         gimp_canvas_item_set_visible (private->corners[i], TRUE);
1295     }
1296   else
1297     {
1298       for (i = 0; i < 4; i++)
1299         gimp_canvas_item_set_visible (private->corners[i], FALSE);
1300     }
1301 
1302   gimp_canvas_item_set_visible (private->center, FALSE);
1303 
1304   for (i = 0; i < 4; i++)
1305     {
1306       gimp_canvas_item_set_visible (private->creating_corners[i], FALSE);
1307       gimp_canvas_item_set_highlight (private->creating_corners[i], FALSE);
1308     }
1309 
1310   for (i = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
1311        i <= GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM;
1312        i++)
1313     {
1314       gimp_canvas_item_set_visible (private->handles[i], FALSE);
1315       gimp_canvas_item_set_visible (private->highlight_handles[i], FALSE);
1316     }
1317 
1318   handle_width  = private->corner_handle_w;
1319   handle_height = private->corner_handle_h;
1320 
1321   switch (private->function)
1322     {
1323     case GIMP_TOOL_RECTANGLE_MOVING:
1324       if (private->rect_adjusting)
1325         {
1326           /* Mark the center because we snap to it */
1327           gimp_canvas_handle_set_position (private->center,
1328                                            (x1 + x2) / 2.0,
1329                                            (y1 + y2) / 2.0);
1330           gimp_canvas_item_set_visible (private->center, TRUE);
1331           break;
1332         }
1333 
1334       /* else fallthrough */
1335 
1336     case GIMP_TOOL_RECTANGLE_DEAD:
1337     case GIMP_TOOL_RECTANGLE_CREATING:
1338     case GIMP_TOOL_RECTANGLE_AUTO_SHRINK:
1339       for (i = 0; i < 4; i++)
1340         {
1341           gimp_canvas_corner_set (private->creating_corners[i],
1342                                   x1, y1, x2 - x1, y2 - y1,
1343                                   handle_width, handle_height,
1344                                   private->narrow_mode);
1345           gimp_canvas_item_set_visible (private->creating_corners[i], TRUE);
1346         }
1347       break;
1348 
1349     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
1350     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
1351       handle_width = private->top_and_bottom_handle_w;
1352       break;
1353 
1354     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
1355     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
1356       handle_height = private->left_and_right_handle_h;
1357       break;
1358 
1359     default:
1360       break;
1361     }
1362 
1363   if (handle_width  >= 3                                           &&
1364       handle_height >= 3                                           &&
1365       private->function >= GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT &&
1366       private->function <= GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM)
1367     {
1368       GimpCanvasItem *corner;
1369 
1370       if (private->rect_adjusting)
1371         corner = private->handles[private->function];
1372       else
1373         corner = private->highlight_handles[private->function];
1374 
1375       gimp_canvas_corner_set (corner,
1376                               x1, y1, x2 - x1, y2 - y1,
1377                               handle_width, handle_height,
1378                               private->narrow_mode);
1379       gimp_canvas_item_set_visible (corner, TRUE);
1380     }
1381 
1382   if (private->highlight && ! private->rect_adjusting)
1383     {
1384       GdkRectangle rect;
1385 
1386       rect.x      = x1;
1387       rect.y      = y1;
1388       rect.width  = x2 - x1;
1389       rect.height = y2 - y1;
1390 
1391       gimp_display_shell_set_highlight (shell, &rect, private->highlight_opacity);
1392     }
1393   else
1394     {
1395       gimp_display_shell_set_highlight (shell, NULL, 0.0);
1396     }
1397 }
1398 
1399 gint
gimp_tool_rectangle_button_press(GimpToolWidget * widget,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type)1400 gimp_tool_rectangle_button_press (GimpToolWidget      *widget,
1401                                   const GimpCoords    *coords,
1402                                   guint32              time,
1403                                   GdkModifierType      state,
1404                                   GimpButtonPressType  press_type)
1405 {
1406   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
1407   GimpToolRectanglePrivate *private   = rectangle->private;
1408   gdouble                   snapped_x, snapped_y;
1409   gint                      snap_x, snap_y;
1410 
1411   /* save existing shape in case of cancellation */
1412   private->saved_x1 = private->x1;
1413   private->saved_y1 = private->y1;
1414   private->saved_x2 = private->x2;
1415   private->saved_y2 = private->y2;
1416 
1417   gimp_tool_rectangle_setup_snap_offsets (rectangle, coords);
1418   gimp_tool_widget_get_snap_offsets (widget, &snap_x, &snap_y, NULL, NULL);
1419 
1420   snapped_x = coords->x + snap_x;
1421   snapped_y = coords->y + snap_y;
1422 
1423   private->lastx = snapped_x;
1424   private->lasty = snapped_y;
1425 
1426   if (private->function == GIMP_TOOL_RECTANGLE_CREATING)
1427     {
1428       /* Remember that this rectangle was created from scratch. */
1429       private->is_new = TRUE;
1430 
1431       private->x1 = private->x2 = snapped_x;
1432       private->y1 = private->y2 = snapped_y;
1433 
1434       /* Unless forced, created rectangles should not be started in
1435        * narrow-mode
1436        */
1437       if (private->force_narrow_mode)
1438         private->narrow_mode = TRUE;
1439       else
1440         private->narrow_mode = FALSE;
1441 
1442       /* If the rectangle is being modified we want the center on
1443        * fixed_center to be at the center of the currently existing
1444        * rectangle, otherwise we want the point where the user clicked
1445        * to be the center on fixed_center.
1446        */
1447       private->center_x_on_fixed_center = snapped_x;
1448       private->center_y_on_fixed_center = snapped_y;
1449 
1450       /* When the user toggles modifier keys, we want to keep track of
1451        * what coordinates the "other side" should have. If we are
1452        * creating a rectangle, use the current mouse coordinates as
1453        * the coordinate of the "other side", otherwise use the
1454        * immediate "other side" for that.
1455        */
1456       private->other_side_x = snapped_x;
1457       private->other_side_y = snapped_y;
1458     }
1459   else
1460     {
1461       /* This rectangle was not created from scratch. */
1462       private->is_new = FALSE;
1463 
1464       private->center_x_on_fixed_center = (private->x1 + private->x2) / 2;
1465       private->center_y_on_fixed_center = (private->y1 + private->y2) / 2;
1466 
1467       gimp_tool_rectangle_get_other_side_coord (rectangle,
1468                                                 &private->other_side_x,
1469                                                 &private->other_side_y);
1470     }
1471 
1472   gimp_tool_rectangle_update_int_rect (rectangle);
1473 
1474   /* Is the rectangle being rubber-banded? */
1475   private->rect_adjusting = gimp_tool_rectangle_rect_adjusting_func (rectangle);
1476 
1477   gimp_tool_rectangle_changed (widget);
1478 
1479   gimp_tool_rectangle_update_status (rectangle);
1480 
1481   return 1;
1482 }
1483 
1484 void
gimp_tool_rectangle_button_release(GimpToolWidget * widget,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type)1485 gimp_tool_rectangle_button_release (GimpToolWidget        *widget,
1486                                     const GimpCoords      *coords,
1487                                     guint32                time,
1488                                     GdkModifierType        state,
1489                                     GimpButtonReleaseType  release_type)
1490 {
1491   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
1492   GimpToolRectanglePrivate *private   = rectangle->private;
1493   gint                      response  = 0;
1494 
1495   gimp_tool_widget_set_status (widget, NULL);
1496 
1497   /* On button release, we are not rubber-banding the rectangle any longer. */
1498   private->rect_adjusting = FALSE;
1499 
1500   gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0);
1501 
1502   switch (release_type)
1503     {
1504     case GIMP_BUTTON_RELEASE_NO_MOTION:
1505       /* If the first created rectangle was not expanded, halt the
1506        * tool...
1507        */
1508       if (gimp_tool_rectangle_rectangle_is_first (rectangle))
1509         {
1510           response = GIMP_TOOL_WIDGET_RESPONSE_CANCEL;
1511           break;
1512         }
1513 
1514       /* ...else fallthrough and treat a long click without movement
1515        * like a normal change
1516        */
1517 
1518     case GIMP_BUTTON_RELEASE_NORMAL:
1519       /* If a normal click-drag-release actually created a rectangle
1520        * with content...
1521        */
1522       if (private->x1 != private->x2 &&
1523           private->y1 != private->y2)
1524         {
1525           gimp_tool_rectangle_change_complete (rectangle);
1526           break;
1527         }
1528 
1529       /* ...else fallthrough and undo the operation, we can't have
1530        * zero-extent rectangles
1531        */
1532 
1533     case GIMP_BUTTON_RELEASE_CANCEL:
1534       private->x1 = private->saved_x1;
1535       private->y1 = private->saved_y1;
1536       private->x2 = private->saved_x2;
1537       private->y2 = private->saved_y2;
1538 
1539       gimp_tool_rectangle_update_int_rect (rectangle);
1540 
1541       /* If the first created rectangle was canceled, halt the tool */
1542       if (gimp_tool_rectangle_rectangle_is_first (rectangle))
1543         response = GIMP_TOOL_WIDGET_RESPONSE_CANCEL;
1544       break;
1545 
1546     case GIMP_BUTTON_RELEASE_CLICK:
1547       /* When a dead area is clicked, don't execute. */
1548       if (private->function != GIMP_TOOL_RECTANGLE_DEAD)
1549         response = GIMP_TOOL_WIDGET_RESPONSE_CONFIRM;
1550       break;
1551     }
1552 
1553   /* We must update this. */
1554   gimp_tool_rectangle_recalculate_center_xy (rectangle);
1555 
1556   gimp_tool_rectangle_update_options (rectangle);
1557 
1558   gimp_tool_rectangle_changed (widget);
1559 
1560   private->is_first = FALSE;
1561 
1562   /*  emit response at the end, so everything is up to date even if
1563    *  a signal handler decides hot to shut down the rectangle
1564    */
1565   if (response != 0)
1566     gimp_tool_widget_response (widget, response);
1567 }
1568 
1569 void
gimp_tool_rectangle_motion(GimpToolWidget * widget,const GimpCoords * coords,guint32 time,GdkModifierType state)1570 gimp_tool_rectangle_motion (GimpToolWidget   *widget,
1571                             const GimpCoords *coords,
1572                             guint32           time,
1573                             GdkModifierType   state)
1574 {
1575   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
1576   GimpToolRectanglePrivate *private   = rectangle->private;
1577   gdouble                   snapped_x;
1578   gdouble                   snapped_y;
1579   gint                      snap_x, snap_y;
1580 
1581   /* Motion events should be ignored when we're just waiting for the
1582    * button release event to execute or if the user has grabbed a dead
1583    * area of the rectangle.
1584    */
1585   if (private->function == GIMP_TOOL_RECTANGLE_EXECUTING ||
1586       private->function == GIMP_TOOL_RECTANGLE_DEAD)
1587     return;
1588 
1589   /* Handle snapping. */
1590   gimp_tool_widget_get_snap_offsets (widget, &snap_x, &snap_y, NULL, NULL);
1591 
1592   snapped_x = coords->x + snap_x;
1593   snapped_y = coords->y + snap_y;
1594 
1595   /* This is the core rectangle shape updating function: */
1596   gimp_tool_rectangle_update_with_coord (rectangle, snapped_x, snapped_y);
1597 
1598   gimp_tool_rectangle_update_status (rectangle);
1599 
1600   if (private->function == GIMP_TOOL_RECTANGLE_CREATING)
1601     {
1602       GimpRectangleFunction function = GIMP_TOOL_RECTANGLE_CREATING;
1603       gdouble               dx       = snapped_x - private->lastx;
1604       gdouble               dy       = snapped_y - private->lasty;
1605 
1606       /* When the user starts to move the cursor, set the current
1607        * function to one of the corner-grabbed functions, depending on
1608        * in what direction the user starts dragging the rectangle.
1609        */
1610       if (dx < 0)
1611         {
1612           function = (dy < 0 ?
1613                       GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT :
1614                       GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT);
1615         }
1616       else if (dx > 0)
1617         {
1618           function = (dy < 0 ?
1619                       GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT :
1620                       GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT);
1621         }
1622       else if (dy < 0)
1623         {
1624           function = (dx < 0 ?
1625                       GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT :
1626                       GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT);
1627         }
1628       else if (dy > 0)
1629         {
1630           function = (dx < 0 ?
1631                       GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT :
1632                       GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT);
1633         }
1634 
1635       gimp_tool_rectangle_set_function (rectangle, function);
1636 
1637       if (private->fixed_rule_active &&
1638           private->fixed_rule == GIMP_RECTANGLE_FIXED_SIZE)
1639         {
1640           /* For fixed size, set the function to moving immediately since the
1641            * rectangle can not be resized anyway.
1642            */
1643 
1644           /* We fake a coord update to get the right size. */
1645           gimp_tool_rectangle_update_with_coord (rectangle,
1646                                                  snapped_x,
1647                                                  snapped_y);
1648 
1649           gimp_tool_widget_set_snap_offsets (widget,
1650                                              -(private->x2 - private->x1) / 2,
1651                                              -(private->y2 - private->y1) / 2,
1652                                              private->x2 - private->x1,
1653                                              private->y2 - private->y1);
1654 
1655           gimp_tool_rectangle_set_function (rectangle,
1656                                             GIMP_TOOL_RECTANGLE_MOVING);
1657         }
1658     }
1659 
1660   gimp_tool_rectangle_update_options (rectangle);
1661 
1662   private->lastx = snapped_x;
1663   private->lasty = snapped_y;
1664 }
1665 
1666 GimpHit
gimp_tool_rectangle_hit(GimpToolWidget * widget,const GimpCoords * coords,GdkModifierType state,gboolean proximity)1667 gimp_tool_rectangle_hit (GimpToolWidget   *widget,
1668                          const GimpCoords *coords,
1669                          GdkModifierType   state,
1670                          gboolean          proximity)
1671 {
1672   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
1673   GimpToolRectanglePrivate *private   = rectangle->private;
1674   GimpRectangleFunction     function;
1675 
1676   if (private->suppress_updates)
1677     {
1678       function = gimp_tool_rectangle_get_function (rectangle);
1679     }
1680   else
1681     {
1682       function = gimp_tool_rectangle_calc_function (rectangle,
1683                                                     coords, proximity);
1684     }
1685 
1686   switch (function)
1687     {
1688     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
1689     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
1690     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
1691     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
1692     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
1693     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
1694     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
1695     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
1696       return GIMP_HIT_DIRECT;
1697 
1698     case GIMP_TOOL_RECTANGLE_CREATING:
1699     case GIMP_TOOL_RECTANGLE_MOVING:
1700       return GIMP_HIT_INDIRECT;
1701 
1702     case GIMP_TOOL_RECTANGLE_DEAD:
1703     case GIMP_TOOL_RECTANGLE_AUTO_SHRINK:
1704     case GIMP_TOOL_RECTANGLE_EXECUTING:
1705     default:
1706       return GIMP_HIT_NONE;
1707     }
1708 }
1709 
1710 void
gimp_tool_rectangle_hover(GimpToolWidget * widget,const GimpCoords * coords,GdkModifierType state,gboolean proximity)1711 gimp_tool_rectangle_hover (GimpToolWidget   *widget,
1712                            const GimpCoords *coords,
1713                            GdkModifierType   state,
1714                            gboolean          proximity)
1715 {
1716   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
1717   GimpToolRectanglePrivate *private   = rectangle->private;
1718   GimpRectangleFunction     function;
1719 
1720   if (private->suppress_updates)
1721     {
1722       private->suppress_updates--;
1723       return;
1724     }
1725 
1726   function = gimp_tool_rectangle_calc_function (rectangle, coords, proximity);
1727 
1728   gimp_tool_rectangle_set_function (rectangle, function);
1729 }
1730 
1731 static void
gimp_tool_rectangle_leave_notify(GimpToolWidget * widget)1732 gimp_tool_rectangle_leave_notify (GimpToolWidget *widget)
1733 {
1734   GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (widget);
1735 
1736   gimp_tool_rectangle_set_function (rectangle, GIMP_TOOL_RECTANGLE_DEAD);
1737 
1738   GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget);
1739 }
1740 
1741 static gboolean
gimp_tool_rectangle_key_press(GimpToolWidget * widget,GdkEventKey * kevent)1742 gimp_tool_rectangle_key_press (GimpToolWidget *widget,
1743                                GdkEventKey    *kevent)
1744 {
1745   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
1746   GimpToolRectanglePrivate *private   = rectangle->private;
1747   gint                      dx        = 0;
1748   gint                      dy        = 0;
1749   gdouble                   new_x     = 0;
1750   gdouble                   new_y     = 0;
1751 
1752   switch (kevent->keyval)
1753     {
1754     case GDK_KEY_Up:
1755       dy = -1;
1756       break;
1757     case GDK_KEY_Left:
1758       dx = -1;
1759       break;
1760     case GDK_KEY_Right:
1761       dx = 1;
1762       break;
1763     case GDK_KEY_Down:
1764       dy = 1;
1765       break;
1766 
1767     default:
1768       return GIMP_TOOL_WIDGET_CLASS (parent_class)->key_press (widget, kevent);
1769     }
1770 
1771   /*  If the shift key is down, move by an accelerated increment  */
1772   if (kevent->state & gimp_get_extend_selection_mask ())
1773     {
1774       dx *= ARROW_VELOCITY;
1775       dy *= ARROW_VELOCITY;
1776     }
1777 
1778   gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0);
1779 
1780   /*  Resize the rectangle if the mouse is over a handle, otherwise move it  */
1781   switch (private->function)
1782     {
1783     case GIMP_TOOL_RECTANGLE_MOVING:
1784     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
1785       new_x = private->x1 + dx;
1786       new_y = private->y1 + dy;
1787       private->lastx = new_x;
1788       private->lasty = new_y;
1789       break;
1790     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
1791       new_x = private->x2 + dx;
1792       new_y = private->y1 + dy;
1793       private->lastx = new_x;
1794       private->lasty = new_y;
1795       break;
1796     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
1797       new_x = private->x1 + dx;
1798       new_y = private->y2 + dy;
1799       private->lastx = new_x;
1800       private->lasty = new_y;
1801       break;
1802     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
1803       new_x = private->x2 + dx;
1804       new_y = private->y2 + dy;
1805       private->lastx = new_x;
1806       private->lasty = new_y;
1807       break;
1808     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
1809       new_x = private->x1 + dx;
1810       private->lastx = new_x;
1811       break;
1812     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
1813       new_x = private->x2 + dx;
1814       private->lastx = new_x;
1815       break;
1816     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
1817       new_y = private->y1 + dy;
1818       private->lasty = new_y;
1819       break;
1820     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
1821       new_y = private->y2 + dy;
1822       private->lasty = new_y;
1823       break;
1824 
1825     default:
1826       return TRUE;
1827     }
1828 
1829   gimp_tool_rectangle_update_with_coord (rectangle, new_x, new_y);
1830 
1831   gimp_tool_rectangle_recalculate_center_xy (rectangle);
1832 
1833   gimp_tool_rectangle_update_options (rectangle);
1834 
1835   gimp_tool_rectangle_change_complete (rectangle);
1836 
1837   /*  Evil hack to suppress oper updates. We do this because we don't
1838    *  want the rectangle tool to change function while the rectangle
1839    *  is being resized or moved using the keyboard.
1840    */
1841   private->suppress_updates = 2;
1842 
1843   return TRUE;
1844 }
1845 
1846 static void
gimp_tool_rectangle_motion_modifier(GimpToolWidget * widget,GdkModifierType key,gboolean press,GdkModifierType state)1847 gimp_tool_rectangle_motion_modifier (GimpToolWidget  *widget,
1848                                      GdkModifierType  key,
1849                                      gboolean         press,
1850                                      GdkModifierType  state)
1851 {
1852   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
1853   GimpToolRectanglePrivate *private   = rectangle->private;
1854   gboolean                  button1_down;
1855 
1856   button1_down = (state & GDK_BUTTON1_MASK);
1857 
1858   if (key == gimp_get_extend_selection_mask ())
1859     {
1860 #if 0
1861       /* Here we want to handle manually when to update the rectangle, so we
1862        * don't want gimp_tool_rectangle_options_notify to do anything.
1863        */
1864       g_signal_handlers_block_by_func (options,
1865                                        gimp_tool_rectangle_options_notify,
1866                                        rectangle);
1867 #endif
1868 
1869       g_object_set (rectangle,
1870                     "fixed-rule-active", ! private->fixed_rule_active,
1871                     NULL);
1872 
1873 #if 0
1874       g_signal_handlers_unblock_by_func (options,
1875                                          gimp_tool_rectangle_options_notify,
1876                                          rectangle);
1877 #endif
1878 
1879       /* Only change the shape if the mouse is still down (i.e. the user is
1880        * still editing the rectangle.
1881        */
1882       if (button1_down)
1883         {
1884           if (! private->fixed_rule_active)
1885             {
1886               /* Reset anchor point */
1887               gimp_tool_rectangle_set_other_side_coord (rectangle,
1888                                                         private->other_side_x,
1889                                                         private->other_side_y);
1890             }
1891 
1892           gimp_tool_rectangle_update_with_coord (rectangle,
1893                                                  private->lastx,
1894                                                  private->lasty);
1895         }
1896     }
1897 
1898   if (key == gimp_get_toggle_behavior_mask ())
1899     {
1900       g_object_set (rectangle,
1901                     "fixed-center", ! private->fixed_center,
1902                     NULL);
1903 
1904       if (private->fixed_center)
1905         {
1906           gimp_tool_rectangle_update_with_coord (rectangle,
1907                                                  private->lastx,
1908                                                  private->lasty);
1909 
1910           /* Only emit the rectangle-changed signal if the button is
1911            * not down. If it is down, the signal will and shall be
1912            * emitted on _button_release instead.
1913            */
1914           if (! button1_down)
1915             {
1916               gimp_tool_rectangle_change_complete (rectangle);
1917             }
1918         }
1919       else if (button1_down)
1920         {
1921           /* If we are leaving fixed_center mode we want to set the
1922            * "other side" where it should be. Don't do anything if we
1923            * came here by a mouse-click though, since then the user
1924            * has confirmed the shape and we don't want to modify it
1925            * afterwards.
1926            */
1927           gimp_tool_rectangle_set_other_side_coord (rectangle,
1928                                                     private->other_side_x,
1929                                                     private->other_side_y);
1930         }
1931     }
1932 
1933   gimp_tool_rectangle_update_options (rectangle);
1934 }
1935 
1936 static gboolean
gimp_tool_rectangle_get_cursor(GimpToolWidget * widget,const GimpCoords * coords,GdkModifierType state,GimpCursorType * cursor,GimpToolCursorType * tool_cursor,GimpCursorModifier * modifier)1937 gimp_tool_rectangle_get_cursor (GimpToolWidget     *widget,
1938                                 const GimpCoords   *coords,
1939                                 GdkModifierType     state,
1940                                 GimpCursorType     *cursor,
1941                                 GimpToolCursorType *tool_cursor,
1942                                 GimpCursorModifier *modifier)
1943 {
1944   GimpToolRectangle        *rectangle = GIMP_TOOL_RECTANGLE (widget);
1945   GimpToolRectanglePrivate *private   = rectangle->private;
1946 
1947   switch (private->function)
1948     {
1949     case GIMP_TOOL_RECTANGLE_CREATING:
1950       *cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
1951       break;
1952     case GIMP_TOOL_RECTANGLE_MOVING:
1953       *cursor   = GIMP_CURSOR_MOVE;
1954       *modifier = GIMP_CURSOR_MODIFIER_MOVE;
1955       break;
1956     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
1957       *cursor = GIMP_CURSOR_CORNER_TOP_LEFT;
1958       break;
1959     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
1960       *cursor = GIMP_CURSOR_CORNER_TOP_RIGHT;
1961       break;
1962     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
1963       *cursor = GIMP_CURSOR_CORNER_BOTTOM_LEFT;
1964       break;
1965     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
1966       *cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT;
1967       break;
1968     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
1969       *cursor = GIMP_CURSOR_SIDE_LEFT;
1970       break;
1971     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
1972       *cursor = GIMP_CURSOR_SIDE_RIGHT;
1973       break;
1974     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
1975       *cursor = GIMP_CURSOR_SIDE_TOP;
1976       break;
1977     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
1978       *cursor = GIMP_CURSOR_SIDE_BOTTOM;
1979       break;
1980 
1981     default:
1982       return FALSE;
1983     }
1984 
1985   return TRUE;
1986 }
1987 
1988 static void
gimp_tool_rectangle_change_complete(GimpToolRectangle * rectangle)1989 gimp_tool_rectangle_change_complete (GimpToolRectangle *rectangle)
1990 {
1991   g_signal_emit (rectangle, rectangle_signals[CHANGE_COMPLETE], 0);
1992 }
1993 
1994 static void
gimp_tool_rectangle_update_options(GimpToolRectangle * rectangle)1995 gimp_tool_rectangle_update_options (GimpToolRectangle *rectangle)
1996 {
1997   GimpToolRectanglePrivate *private = rectangle->private;
1998   gdouble                   x1, y1;
1999   gdouble                   x2, y2;
2000 
2001   gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
2002 
2003 #if 0
2004   g_signal_handlers_block_by_func (options,
2005                                    gimp_tool_rectangle_options_notify,
2006                                    rect_tool);
2007 #endif
2008 
2009   g_object_freeze_notify (G_OBJECT (rectangle));
2010 
2011   if (! FEQUAL (private->x, x1))
2012     g_object_set (rectangle, "x", x1, NULL);
2013 
2014   if (! FEQUAL (private->y, y1))
2015     g_object_set (rectangle, "y", y1, NULL);
2016 
2017   if (! FEQUAL (private->width, x2 - x1))
2018     g_object_set (rectangle, "width", x2 - x1, NULL);
2019 
2020   if (! FEQUAL (private->height, y2 - y1))
2021     g_object_set (rectangle, "height", y2 - y1, NULL);
2022 
2023   g_object_thaw_notify (G_OBJECT (rectangle));
2024 
2025 #if 0
2026   g_signal_handlers_unblock_by_func (options,
2027                                      gimp_tool_rectangle_options_notify,
2028                                      rect_tool);
2029 #endif
2030 }
2031 
2032 static void
gimp_tool_rectangle_update_handle_sizes(GimpToolRectangle * rectangle)2033 gimp_tool_rectangle_update_handle_sizes (GimpToolRectangle *rectangle)
2034 {
2035   GimpToolRectanglePrivate *private = rectangle->private;
2036   GimpDisplayShell         *shell;
2037   gint                      visible_rectangle_width;
2038   gint                      visible_rectangle_height;
2039   gint                      rectangle_width;
2040   gint                      rectangle_height;
2041   gdouble                   pub_x1, pub_y1;
2042   gdouble                   pub_x2, pub_y2;
2043 
2044   shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
2045 
2046   gimp_tool_rectangle_get_public_rect (rectangle,
2047                                        &pub_x1, &pub_y1, &pub_x2, &pub_y2);
2048   {
2049     /* Calculate rectangles of the selection rectangle and the display
2050      * shell, with origin at (0, 0) of image, and in screen coordinate
2051      * scale.
2052      */
2053     gint x1 =  pub_x1 * shell->scale_x;
2054     gint y1 =  pub_y1 * shell->scale_y;
2055     gint w1 = (pub_x2 - pub_x1) * shell->scale_x;
2056     gint h1 = (pub_y2 - pub_y1) * shell->scale_y;
2057 
2058     gint x2, y2, w2, h2;
2059 
2060     gimp_display_shell_scroll_get_scaled_viewport (shell, &x2, &y2, &w2, &h2);
2061 
2062     rectangle_width  = w1;
2063     rectangle_height = h1;
2064 
2065     /* Handle size calculations shall be based on the visible part of
2066      * the rectangle, so calculate the size for the visible rectangle
2067      * by intersecting with the viewport rectangle.
2068      */
2069     gimp_rectangle_intersect (x1, y1,
2070                               w1, h1,
2071                               x2, y2,
2072                               w2, h2,
2073                               NULL, NULL,
2074                               &visible_rectangle_width,
2075                               &visible_rectangle_height);
2076 
2077     /* Determine if we are in narrow-mode or not. */
2078     if (private->force_narrow_mode)
2079       private->narrow_mode = TRUE;
2080     else
2081       private->narrow_mode = (visible_rectangle_width  < NARROW_MODE_THRESHOLD ||
2082                               visible_rectangle_height < NARROW_MODE_THRESHOLD);
2083   }
2084 
2085   if (private->narrow_mode)
2086     {
2087       /* Corner handles always have the same (on-screen) size in
2088        * narrow-mode.
2089        */
2090       private->corner_handle_w = NARROW_MODE_HANDLE_SIZE;
2091       private->corner_handle_h = NARROW_MODE_HANDLE_SIZE;
2092 
2093       private->top_and_bottom_handle_w = CLAMP (rectangle_width,
2094                                                 MIN (rectangle_width - 2,
2095                                                      NARROW_MODE_HANDLE_SIZE),
2096                                                 G_MAXINT);
2097       private->left_and_right_handle_h = CLAMP (rectangle_height,
2098                                                 MIN (rectangle_height - 2,
2099                                                      NARROW_MODE_HANDLE_SIZE),
2100                                                 G_MAXINT);
2101     }
2102   else
2103     {
2104       /* Calculate and clamp corner handle size. */
2105 
2106       private->corner_handle_w = visible_rectangle_width  / 4;
2107       private->corner_handle_h = visible_rectangle_height / 4;
2108 
2109       private->corner_handle_w = CLAMP (private->corner_handle_w,
2110                                         MIN_HANDLE_SIZE,
2111                                         MAX_HANDLE_SIZE);
2112       private->corner_handle_h = CLAMP (private->corner_handle_h,
2113                                         MIN_HANDLE_SIZE,
2114                                         MAX_HANDLE_SIZE);
2115 
2116       /* Calculate and clamp side handle size. */
2117 
2118       private->top_and_bottom_handle_w = rectangle_width  - 3 * private->corner_handle_w;
2119       private->left_and_right_handle_h = rectangle_height - 3 * private->corner_handle_h;
2120 
2121       private->top_and_bottom_handle_w = CLAMP (private->top_and_bottom_handle_w,
2122                                                 MIN_HANDLE_SIZE,
2123                                                 G_MAXINT);
2124       private->left_and_right_handle_h = CLAMP (private->left_and_right_handle_h,
2125                                                 MIN_HANDLE_SIZE,
2126                                                 G_MAXINT);
2127     }
2128 }
2129 
2130 static void
gimp_tool_rectangle_update_status(GimpToolRectangle * rectangle)2131 gimp_tool_rectangle_update_status (GimpToolRectangle *rectangle)
2132 {
2133   GimpToolRectanglePrivate *private = rectangle->private;
2134   gdouble                   x1, y1, x2, y2;
2135 
2136   gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
2137 
2138   if (private->function == GIMP_TOOL_RECTANGLE_MOVING)
2139     {
2140       gimp_tool_widget_set_status_coords (GIMP_TOOL_WIDGET (rectangle),
2141                                           _("Position: "),
2142                                           x1, ", ", y1,
2143                                           NULL);
2144     }
2145   else
2146     {
2147       gchar *aspect_text = NULL;
2148       gint   width       = x2 - x1;
2149       gint   height      = y2 - y1;
2150 
2151       if (width > 0.0 && height > 0.0)
2152         {
2153           aspect_text = g_strdup_printf ("  (%.2f:1)",
2154                                          (gdouble) width / (gdouble) height);
2155         }
2156 
2157       gimp_tool_widget_set_status_coords (GIMP_TOOL_WIDGET (rectangle),
2158                                           private->status_title,
2159                                           width, " × ", height,
2160                                           aspect_text);
2161       g_free (aspect_text);
2162     }
2163 }
2164 
2165 static void
gimp_tool_rectangle_synthesize_motion(GimpToolRectangle * rectangle,gint function,gdouble new_x,gdouble new_y)2166 gimp_tool_rectangle_synthesize_motion (GimpToolRectangle *rectangle,
2167                                        gint               function,
2168                                        gdouble            new_x,
2169                                        gdouble            new_y)
2170 {
2171   GimpToolRectanglePrivate *private = rectangle->private;
2172   GimpRectangleFunction     old_function;
2173 
2174   /* We don't want to synthesize motions if the tool control is active
2175    * since that means the mouse button is down and the rectangle will
2176    * get updated in _motion anyway. The reason we want to prevent this
2177    * function from executing is that is emits the
2178    * rectangle-changed-complete signal which we don't want in the
2179    * middle of a rectangle change.
2180    *
2181    * In addition to that, we don't want to synthesize a motion if
2182    * there is no pending rectangle because that doesn't make any
2183    * sense.
2184    */
2185   if (private->rect_adjusting)
2186     return;
2187 
2188   old_function = private->function;
2189 
2190   gimp_tool_rectangle_set_function (rectangle, function);
2191 
2192   gimp_tool_rectangle_update_with_coord (rectangle, new_x, new_y);
2193 
2194   /* We must update this. */
2195   gimp_tool_rectangle_recalculate_center_xy (rectangle);
2196 
2197   gimp_tool_rectangle_update_options (rectangle);
2198 
2199   gimp_tool_rectangle_set_function (rectangle, old_function);
2200 
2201   gimp_tool_rectangle_change_complete (rectangle);
2202 }
2203 
2204 static void
swap_doubles(gdouble * i,gdouble * j)2205 swap_doubles (gdouble *i,
2206               gdouble *j)
2207 {
2208   gdouble tmp;
2209 
2210   tmp = *i;
2211   *i = *j;
2212   *j = tmp;
2213 }
2214 
2215 static GimpRectangleFunction
gimp_tool_rectangle_calc_function(GimpToolRectangle * rectangle,const GimpCoords * coords,gboolean proximity)2216 gimp_tool_rectangle_calc_function (GimpToolRectangle *rectangle,
2217                                    const GimpCoords  *coords,
2218                                    gboolean           proximity)
2219 {
2220   if (! proximity)
2221     {
2222       return GIMP_TOOL_RECTANGLE_DEAD;
2223     }
2224   else if (gimp_tool_rectangle_coord_outside (rectangle, coords))
2225     {
2226       /* The cursor is outside of the rectangle, clicking should
2227        * create a new rectangle.
2228        */
2229       return GIMP_TOOL_RECTANGLE_CREATING;
2230     }
2231   else if (gimp_tool_rectangle_coord_on_handle (rectangle,
2232                                                 coords,
2233                                                 GIMP_HANDLE_ANCHOR_NORTH_WEST))
2234     {
2235       return GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
2236     }
2237   else if (gimp_tool_rectangle_coord_on_handle (rectangle,
2238                                                 coords,
2239                                                 GIMP_HANDLE_ANCHOR_SOUTH_EAST))
2240     {
2241       return GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT;
2242     }
2243   else if  (gimp_tool_rectangle_coord_on_handle (rectangle,
2244                                                  coords,
2245                                                  GIMP_HANDLE_ANCHOR_NORTH_EAST))
2246     {
2247       return GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT;
2248     }
2249   else if (gimp_tool_rectangle_coord_on_handle (rectangle,
2250                                                 coords,
2251                                                 GIMP_HANDLE_ANCHOR_SOUTH_WEST))
2252     {
2253       return GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT;
2254     }
2255   else if (gimp_tool_rectangle_coord_on_handle (rectangle,
2256                                                 coords,
2257                                                 GIMP_HANDLE_ANCHOR_WEST))
2258     {
2259       return GIMP_TOOL_RECTANGLE_RESIZING_LEFT;
2260     }
2261   else if (gimp_tool_rectangle_coord_on_handle (rectangle,
2262                                                 coords,
2263                                                 GIMP_HANDLE_ANCHOR_EAST))
2264     {
2265       return GIMP_TOOL_RECTANGLE_RESIZING_RIGHT;
2266     }
2267   else if (gimp_tool_rectangle_coord_on_handle (rectangle,
2268                                                 coords,
2269                                                 GIMP_HANDLE_ANCHOR_NORTH))
2270     {
2271       return GIMP_TOOL_RECTANGLE_RESIZING_TOP;
2272     }
2273   else if (gimp_tool_rectangle_coord_on_handle (rectangle,
2274                                                 coords,
2275                                                 GIMP_HANDLE_ANCHOR_SOUTH))
2276     {
2277       return GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM;
2278     }
2279   else if (gimp_tool_rectangle_coord_on_handle (rectangle,
2280                                                 coords,
2281                                                 GIMP_HANDLE_ANCHOR_CENTER))
2282     {
2283       return GIMP_TOOL_RECTANGLE_MOVING;
2284     }
2285   else
2286     {
2287       return GIMP_TOOL_RECTANGLE_DEAD;
2288     }
2289 }
2290 
2291 /* gimp_tool_rectangle_check_function() is needed to deal with
2292  * situations where the user drags a corner or edge across one of the
2293  * existing edges, thereby changing its function.  Ugh.
2294  */
2295 static void
gimp_tool_rectangle_check_function(GimpToolRectangle * rectangle)2296 gimp_tool_rectangle_check_function (GimpToolRectangle *rectangle)
2297 
2298 {
2299   GimpToolRectanglePrivate *private  = rectangle->private;
2300   GimpRectangleFunction     function = private->function;
2301 
2302   if (private->x2 < private->x1)
2303     {
2304       swap_doubles (&private->x1, &private->x2);
2305 
2306       switch (function)
2307         {
2308         case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
2309           function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT;
2310           break;
2311         case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
2312           function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
2313           break;
2314         case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
2315           function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT;
2316           break;
2317         case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
2318           function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT;
2319           break;
2320         case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
2321           function = GIMP_TOOL_RECTANGLE_RESIZING_RIGHT;
2322           break;
2323         case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
2324           function = GIMP_TOOL_RECTANGLE_RESIZING_LEFT;
2325           break;
2326           /* avoid annoying warnings about unhandled enums */
2327         default:
2328           break;
2329         }
2330     }
2331 
2332   if (private->y2 < private->y1)
2333     {
2334       swap_doubles (&private->y1, &private->y2);
2335 
2336       switch (function)
2337         {
2338         case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
2339           function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT;
2340           break;
2341         case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
2342           function = GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT;
2343           break;
2344         case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
2345           function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT;
2346           break;
2347         case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
2348           function = GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT;
2349           break;
2350         case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
2351           function = GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM;
2352           break;
2353         case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
2354           function = GIMP_TOOL_RECTANGLE_RESIZING_TOP;
2355           break;
2356         default:
2357           break;
2358         }
2359     }
2360 
2361   gimp_tool_rectangle_set_function (rectangle, function);
2362 }
2363 
2364 /**
2365  * gimp_tool_rectangle_coord_outside:
2366  *
2367  * Returns: %TRUE if the coord is outside the rectangle bounds
2368  *          including any outside handles.
2369  */
2370 static gboolean
gimp_tool_rectangle_coord_outside(GimpToolRectangle * rectangle,const GimpCoords * coord)2371 gimp_tool_rectangle_coord_outside (GimpToolRectangle *rectangle,
2372                                    const GimpCoords  *coord)
2373 {
2374   GimpToolRectanglePrivate *private = rectangle->private;
2375   GimpDisplayShell         *shell;
2376   gboolean                  narrow_mode = private->narrow_mode;
2377   gdouble                   x1, y1, x2, y2;
2378   gdouble                   x1_b, y1_b, x2_b, y2_b;
2379 
2380   shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
2381 
2382   gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
2383 
2384   x1_b = x1 - (narrow_mode ? private->corner_handle_w / shell->scale_x : 0);
2385   x2_b = x2 + (narrow_mode ? private->corner_handle_w / shell->scale_x : 0);
2386   y1_b = y1 - (narrow_mode ? private->corner_handle_h / shell->scale_y : 0);
2387   y2_b = y2 + (narrow_mode ? private->corner_handle_h / shell->scale_y : 0);
2388 
2389   return (coord->x < x1_b ||
2390           coord->x > x2_b ||
2391           coord->y < y1_b ||
2392           coord->y > y2_b);
2393 }
2394 
2395 /**
2396  * gimp_tool_rectangle_coord_on_handle:
2397  *
2398  * Returns: %TRUE if the coord is on the handle that corresponds to
2399  *          @anchor.
2400  */
2401 static gboolean
gimp_tool_rectangle_coord_on_handle(GimpToolRectangle * rectangle,const GimpCoords * coords,GimpHandleAnchor anchor)2402 gimp_tool_rectangle_coord_on_handle (GimpToolRectangle *rectangle,
2403                                      const GimpCoords  *coords,
2404                                      GimpHandleAnchor   anchor)
2405 {
2406   GimpToolRectanglePrivate *private = rectangle->private;
2407   GimpDisplayShell         *shell;
2408   gdouble                   x1, y1, x2, y2;
2409   gdouble                   rect_w, rect_h;
2410   gdouble                   handle_x          = 0;
2411   gdouble                   handle_y          = 0;
2412   gdouble                   handle_width      = 0;
2413   gdouble                   handle_height     = 0;
2414   gint                      narrow_mode_x_dir = 0;
2415   gint                      narrow_mode_y_dir = 0;
2416 
2417   shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
2418 
2419   gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
2420 
2421   rect_w = x2 - x1;
2422   rect_h = y2 - y1;
2423 
2424   switch (anchor)
2425     {
2426     case GIMP_HANDLE_ANCHOR_NORTH_WEST:
2427       handle_x      = x1;
2428       handle_y      = y1;
2429       handle_width  = private->corner_handle_w;
2430       handle_height = private->corner_handle_h;
2431 
2432       narrow_mode_x_dir = -1;
2433       narrow_mode_y_dir = -1;
2434       break;
2435 
2436     case GIMP_HANDLE_ANCHOR_SOUTH_EAST:
2437       handle_x      = x2;
2438       handle_y      = y2;
2439       handle_width  = private->corner_handle_w;
2440       handle_height = private->corner_handle_h;
2441 
2442       narrow_mode_x_dir =  1;
2443       narrow_mode_y_dir =  1;
2444       break;
2445 
2446     case GIMP_HANDLE_ANCHOR_NORTH_EAST:
2447       handle_x      = x2;
2448       handle_y      = y1;
2449       handle_width  = private->corner_handle_w;
2450       handle_height = private->corner_handle_h;
2451 
2452       narrow_mode_x_dir =  1;
2453       narrow_mode_y_dir = -1;
2454       break;
2455 
2456     case GIMP_HANDLE_ANCHOR_SOUTH_WEST:
2457       handle_x      = x1;
2458       handle_y      = y2;
2459       handle_width  = private->corner_handle_w;
2460       handle_height = private->corner_handle_h;
2461 
2462       narrow_mode_x_dir = -1;
2463       narrow_mode_y_dir =  1;
2464       break;
2465 
2466     case GIMP_HANDLE_ANCHOR_WEST:
2467       handle_x      = x1;
2468       handle_y      = y1 + rect_h / 2;
2469       handle_width  = private->corner_handle_w;
2470       handle_height = private->left_and_right_handle_h;
2471 
2472       narrow_mode_x_dir = -1;
2473       narrow_mode_y_dir =  0;
2474       break;
2475 
2476     case GIMP_HANDLE_ANCHOR_EAST:
2477       handle_x      = x2;
2478       handle_y      = y1 + rect_h / 2;
2479       handle_width  = private->corner_handle_w;
2480       handle_height = private->left_and_right_handle_h;
2481 
2482       narrow_mode_x_dir =  1;
2483       narrow_mode_y_dir =  0;
2484       break;
2485 
2486     case GIMP_HANDLE_ANCHOR_NORTH:
2487       handle_x      = x1 + rect_w / 2;
2488       handle_y      = y1;
2489       handle_width  = private->top_and_bottom_handle_w;
2490       handle_height = private->corner_handle_h;
2491 
2492       narrow_mode_x_dir =  0;
2493       narrow_mode_y_dir = -1;
2494       break;
2495 
2496     case GIMP_HANDLE_ANCHOR_SOUTH:
2497       handle_x      = x1 + rect_w / 2;
2498       handle_y      = y2;
2499       handle_width  = private->top_and_bottom_handle_w;
2500       handle_height = private->corner_handle_h;
2501 
2502       narrow_mode_x_dir =  0;
2503       narrow_mode_y_dir =  1;
2504       break;
2505 
2506     case GIMP_HANDLE_ANCHOR_CENTER:
2507       handle_x      = x1 + rect_w / 2;
2508       handle_y      = y1 + rect_h / 2;
2509 
2510       if (private->narrow_mode)
2511         {
2512           handle_width  = rect_w * shell->scale_x;
2513           handle_height = rect_h * shell->scale_y;
2514         }
2515       else
2516         {
2517           handle_width  = rect_w * shell->scale_x - private->corner_handle_w * 2;
2518           handle_height = rect_h * shell->scale_y - private->corner_handle_h * 2;
2519         }
2520 
2521       narrow_mode_x_dir =  0;
2522       narrow_mode_y_dir =  0;
2523       break;
2524     }
2525 
2526   if (private->narrow_mode)
2527     {
2528       handle_x += narrow_mode_x_dir * handle_width  / shell->scale_x;
2529       handle_y += narrow_mode_y_dir * handle_height / shell->scale_y;
2530     }
2531 
2532   return gimp_canvas_item_on_handle (private->rectangle,
2533                                      coords->x, coords->y,
2534                                      GIMP_HANDLE_SQUARE,
2535                                      handle_x,     handle_y,
2536                                      handle_width, handle_height,
2537                                      anchor);
2538 }
2539 
2540 static GimpHandleAnchor
gimp_tool_rectangle_get_anchor(GimpRectangleFunction function)2541 gimp_tool_rectangle_get_anchor (GimpRectangleFunction function)
2542 {
2543   switch (function)
2544     {
2545     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
2546       return GIMP_HANDLE_ANCHOR_NORTH_WEST;
2547 
2548     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
2549       return GIMP_HANDLE_ANCHOR_NORTH_EAST;
2550 
2551     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
2552       return GIMP_HANDLE_ANCHOR_SOUTH_WEST;
2553 
2554     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
2555       return GIMP_HANDLE_ANCHOR_SOUTH_EAST;
2556 
2557     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
2558       return GIMP_HANDLE_ANCHOR_WEST;
2559 
2560     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
2561       return GIMP_HANDLE_ANCHOR_EAST;
2562 
2563     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
2564       return GIMP_HANDLE_ANCHOR_NORTH;
2565 
2566     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
2567       return GIMP_HANDLE_ANCHOR_SOUTH;
2568 
2569     default:
2570       return GIMP_HANDLE_ANCHOR_CENTER;
2571     }
2572 }
2573 
2574 static gboolean
gimp_tool_rectangle_rect_rubber_banding_func(GimpToolRectangle * rectangle)2575 gimp_tool_rectangle_rect_rubber_banding_func (GimpToolRectangle *rectangle)
2576 {
2577   GimpToolRectanglePrivate *private = rectangle->private;
2578 
2579   switch (private->function)
2580     {
2581     case GIMP_TOOL_RECTANGLE_CREATING:
2582     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
2583     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
2584     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
2585     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
2586     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
2587     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
2588     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
2589     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
2590     case GIMP_TOOL_RECTANGLE_AUTO_SHRINK:
2591       return TRUE;
2592 
2593     case GIMP_TOOL_RECTANGLE_MOVING:
2594     case GIMP_TOOL_RECTANGLE_DEAD:
2595     default:
2596       break;
2597     }
2598 
2599   return FALSE;
2600 }
2601 
2602 /**
2603  * gimp_tool_rectangle_rect_adjusting_func:
2604  * @rectangle:
2605  *
2606  * Returns: %TRUE if the current function is a rectangle adjusting
2607  *          function.
2608  */
2609 static gboolean
gimp_tool_rectangle_rect_adjusting_func(GimpToolRectangle * rectangle)2610 gimp_tool_rectangle_rect_adjusting_func (GimpToolRectangle *rectangle)
2611 {
2612   GimpToolRectanglePrivate *private = rectangle->private;
2613 
2614   return (gimp_tool_rectangle_rect_rubber_banding_func (rectangle) ||
2615           private->function == GIMP_TOOL_RECTANGLE_MOVING);
2616 }
2617 
2618 /**
2619  * gimp_tool_rectangle_get_other_side:
2620  * @rectangle: A #GimpToolRectangle.
2621  * @other_x:   Pointer to double of the other-x double.
2622  * @other_y:   Pointer to double of the other-y double.
2623  *
2624  * Calculates pointers to member variables that hold the coordinates
2625  * of the opposite side (either the opposite corner or literally the
2626  * opposite side), based on the current function. The opposite of a
2627  * corner needs two coordinates, the opposite of a side only needs
2628  * one.
2629  */
2630 static void
gimp_tool_rectangle_get_other_side(GimpToolRectangle * rectangle,gdouble ** other_x,gdouble ** other_y)2631 gimp_tool_rectangle_get_other_side (GimpToolRectangle  *rectangle,
2632                                     gdouble           **other_x,
2633                                     gdouble           **other_y)
2634 {
2635   GimpToolRectanglePrivate *private = rectangle->private;
2636 
2637   switch (private->function)
2638     {
2639     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
2640     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
2641     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
2642       *other_x = &private->x1;
2643       break;
2644 
2645     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
2646     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
2647     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
2648       *other_x = &private->x2;
2649       break;
2650 
2651     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
2652     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
2653     default:
2654       *other_x = NULL;
2655       break;
2656     }
2657 
2658   switch (private->function)
2659     {
2660     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
2661     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
2662     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
2663       *other_y = &private->y1;
2664       break;
2665 
2666     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
2667     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
2668     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
2669       *other_y = &private->y2;
2670       break;
2671 
2672     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
2673     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
2674     default:
2675       *other_y = NULL;
2676       break;
2677     }
2678 }
2679 
2680 static void
gimp_tool_rectangle_get_other_side_coord(GimpToolRectangle * rectangle,gdouble * other_side_x,gdouble * other_side_y)2681 gimp_tool_rectangle_get_other_side_coord (GimpToolRectangle *rectangle,
2682                                           gdouble           *other_side_x,
2683                                           gdouble           *other_side_y)
2684 {
2685   gdouble *other_x = NULL;
2686   gdouble *other_y = NULL;
2687 
2688   gimp_tool_rectangle_get_other_side (rectangle, &other_x, &other_y);
2689 
2690   if (other_x)
2691     *other_side_x = *other_x;
2692   if (other_y)
2693     *other_side_y = *other_y;
2694 }
2695 
2696 static void
gimp_tool_rectangle_set_other_side_coord(GimpToolRectangle * rectangle,gdouble other_side_x,gdouble other_side_y)2697 gimp_tool_rectangle_set_other_side_coord (GimpToolRectangle *rectangle,
2698                                           gdouble            other_side_x,
2699                                           gdouble            other_side_y)
2700 {
2701   gdouble *other_x = NULL;
2702   gdouble *other_y = NULL;
2703 
2704   gimp_tool_rectangle_get_other_side (rectangle, &other_x, &other_y);
2705 
2706   if (other_x)
2707     *other_x = other_side_x;
2708   if (other_y)
2709     *other_y = other_side_y;
2710 
2711   gimp_tool_rectangle_check_function (rectangle);
2712 
2713   gimp_tool_rectangle_update_int_rect (rectangle);
2714 }
2715 
2716 /**
2717  * gimp_tool_rectangle_apply_coord:
2718  * @param:     A #GimpToolRectangle.
2719  * @coord_x:   X of coord.
2720  * @coord_y:   Y of coord.
2721  *
2722  * Adjust the rectangle to the new position specified by passed
2723  * coordinate, taking fixed_center into account, which means it
2724  * expands the rectangle around the center point.
2725  */
2726 static void
gimp_tool_rectangle_apply_coord(GimpToolRectangle * rectangle,gdouble coord_x,gdouble coord_y)2727 gimp_tool_rectangle_apply_coord (GimpToolRectangle *rectangle,
2728                                  gdouble            coord_x,
2729                                  gdouble            coord_y)
2730 {
2731   GimpToolRectanglePrivate *private = rectangle->private;
2732 
2733   if (private->function == GIMP_TOOL_RECTANGLE_MOVING)
2734     {
2735       /* Preserve width and height while moving the grab-point to where the
2736        * cursor is.
2737        */
2738       gdouble w = private->x2 - private->x1;
2739       gdouble h = private->y2 - private->y1;
2740 
2741       private->x1 = coord_x;
2742       private->y1 = coord_y;
2743 
2744       private->x2 = private->x1 + w;
2745       private->y2 = private->y1 + h;
2746 
2747       /* We are done already. */
2748       return;
2749     }
2750 
2751   switch (private->function)
2752     {
2753     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
2754     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
2755     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
2756       private->x1 = coord_x;
2757 
2758       if (private->fixed_center)
2759         private->x2 = 2 * private->center_x_on_fixed_center - private->x1;
2760 
2761       break;
2762 
2763     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
2764     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
2765     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
2766       private->x2 = coord_x;
2767 
2768       if (private->fixed_center)
2769         private->x1 = 2 * private->center_x_on_fixed_center - private->x2;
2770 
2771       break;
2772 
2773     default:
2774       break;
2775     }
2776 
2777   switch (private->function)
2778     {
2779     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
2780     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
2781     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
2782       private->y1 = coord_y;
2783 
2784       if (private->fixed_center)
2785         private->y2 = 2 * private->center_y_on_fixed_center - private->y1;
2786 
2787       break;
2788 
2789     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
2790     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
2791     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
2792       private->y2 = coord_y;
2793 
2794       if (private->fixed_center)
2795         private->y1 = 2 * private->center_y_on_fixed_center - private->y2;
2796 
2797       break;
2798 
2799     default:
2800       break;
2801     }
2802 }
2803 
2804 static void
gimp_tool_rectangle_setup_snap_offsets(GimpToolRectangle * rectangle,const GimpCoords * coords)2805 gimp_tool_rectangle_setup_snap_offsets (GimpToolRectangle *rectangle,
2806                                         const GimpCoords  *coords)
2807 {
2808   GimpToolWidget           *widget  = GIMP_TOOL_WIDGET (rectangle);
2809   GimpToolRectanglePrivate *private = rectangle->private;
2810   gdouble                   x1, y1, x2, y2;
2811   gdouble                   coord_x, coord_y;
2812 
2813   gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
2814   gimp_tool_rectangle_adjust_coord (rectangle,
2815                                     coords->x, coords->y,
2816                                     &coord_x, &coord_y);
2817 
2818   switch (private->function)
2819     {
2820     case GIMP_TOOL_RECTANGLE_CREATING:
2821       gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0);
2822       break;
2823 
2824     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
2825       gimp_tool_widget_set_snap_offsets (widget,
2826                                          x1 - coord_x,
2827                                          y1 - coord_y,
2828                                          0, 0);
2829       break;
2830 
2831     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
2832       gimp_tool_widget_set_snap_offsets (widget,
2833                                          x2 - coord_x,
2834                                          y1 - coord_y,
2835                                          0, 0);
2836       break;
2837 
2838     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
2839       gimp_tool_widget_set_snap_offsets (widget,
2840                                          x1 - coord_x,
2841                                          y2 - coord_y,
2842                                          0, 0);
2843       break;
2844 
2845     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
2846       gimp_tool_widget_set_snap_offsets (widget,
2847                                          x2 - coord_x,
2848                                          y2 - coord_y,
2849                                          0, 0);
2850       break;
2851 
2852     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
2853       gimp_tool_widget_set_snap_offsets (widget,
2854                                          x1 - coord_x, 0,
2855                                          0, 0);
2856       break;
2857 
2858     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
2859       gimp_tool_widget_set_snap_offsets (widget,
2860                                          x2 - coord_x, 0,
2861                                          0, 0);
2862       break;
2863 
2864     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
2865       gimp_tool_widget_set_snap_offsets (widget,
2866                                          0, y1 - coord_y,
2867                                          0, 0);
2868       break;
2869 
2870     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
2871       gimp_tool_widget_set_snap_offsets (widget,
2872                                          0, y2 - coord_y,
2873                                          0, 0);
2874       break;
2875 
2876     case GIMP_TOOL_RECTANGLE_MOVING:
2877       gimp_tool_widget_set_snap_offsets (widget,
2878                                          x1 - coord_x,
2879                                          y1 - coord_y,
2880                                          x2 - x1,
2881                                          y2 - y1);
2882       break;
2883 
2884     default:
2885       break;
2886     }
2887 }
2888 
2889 /**
2890  * gimp_tool_rectangle_clamp:
2891  * @rectangle:      A #GimpToolRectangle.
2892  * @clamped_sides:  Where to put contrainment information.
2893  * @constraint:     Constraint to use.
2894  * @symmetrically:  Whether or not to clamp symmetrically.
2895  *
2896  * Clamps rectangle inside specified bounds, providing information of
2897  * where clamping was done. Can also clamp symmetrically.
2898  */
2899 static void
gimp_tool_rectangle_clamp(GimpToolRectangle * rectangle,ClampedSide * clamped_sides,GimpRectangleConstraint constraint,gboolean symmetrically)2900 gimp_tool_rectangle_clamp (GimpToolRectangle       *rectangle,
2901                            ClampedSide             *clamped_sides,
2902                            GimpRectangleConstraint  constraint,
2903                            gboolean                 symmetrically)
2904 {
2905   gimp_tool_rectangle_clamp_width (rectangle,
2906                                    clamped_sides,
2907                                    constraint,
2908                                    symmetrically);
2909 
2910   gimp_tool_rectangle_clamp_height (rectangle,
2911                                     clamped_sides,
2912                                     constraint,
2913                                     symmetrically);
2914 }
2915 
2916 /**
2917  * gimp_tool_rectangle_clamp_width:
2918  * @rectangle:      A #GimpToolRectangle.
2919  * @clamped_sides:  Where to put contrainment information.
2920  * @constraint:     Constraint to use.
2921  * @symmetrically:  Whether or not to clamp symmetrically.
2922  *
2923  * Clamps height of rectangle. Set symmetrically to true when using
2924  * for fixed_center:ed rectangles, since that will clamp symmetrically
2925  * which is just what is needed.
2926  *
2927  * When this function constrains, it puts what it constrains in
2928  * @constraint. This information is essential when an aspect ratio is
2929  * to be applied.
2930  */
2931 static void
gimp_tool_rectangle_clamp_width(GimpToolRectangle * rectangle,ClampedSide * clamped_sides,GimpRectangleConstraint constraint,gboolean symmetrically)2932 gimp_tool_rectangle_clamp_width (GimpToolRectangle       *rectangle,
2933                                  ClampedSide             *clamped_sides,
2934                                  GimpRectangleConstraint  constraint,
2935                                  gboolean                 symmetrically)
2936 {
2937   GimpToolRectanglePrivate *private = rectangle->private;
2938   gint                      min_x;
2939   gint                      max_x;
2940 
2941   if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
2942     return;
2943 
2944   gimp_tool_rectangle_get_constraints (rectangle,
2945                                        &min_x, NULL,
2946                                        &max_x, NULL,
2947                                        constraint);
2948   if (private->x1 < min_x)
2949     {
2950       gdouble dx = min_x - private->x1;
2951 
2952       private->x1 += dx;
2953 
2954       if (symmetrically)
2955         private->x2 -= dx;
2956 
2957       if (private->x2 < min_x)
2958         private->x2 = min_x;
2959 
2960       if (clamped_sides)
2961         *clamped_sides |= CLAMPED_LEFT;
2962     }
2963 
2964   if (private->x2 > max_x)
2965     {
2966       gdouble dx = max_x - private->x2;
2967 
2968       private->x2 += dx;
2969 
2970       if (symmetrically)
2971         private->x1 -= dx;
2972 
2973       if (private->x1 > max_x)
2974         private->x1 = max_x;
2975 
2976       if (clamped_sides)
2977         *clamped_sides |= CLAMPED_RIGHT;
2978     }
2979 }
2980 
2981 /**
2982  * gimp_tool_rectangle_clamp_height:
2983  * @rectangle:      A #GimpToolRectangle.
2984  * @clamped_sides:  Where to put contrainment information.
2985  * @constraint:     Constraint to use.
2986  * @symmetrically:  Whether or not to clamp symmetrically.
2987  *
2988  * Clamps height of rectangle. Set symmetrically to true when using for
2989  * fixed_center:ed rectangles, since that will clamp symmetrically which is just
2990  * what is needed.
2991  *
2992  * When this function constrains, it puts what it constrains in
2993  * @constraint. This information is essential when an aspect ratio is to be
2994  * applied.
2995  */
2996 static void
gimp_tool_rectangle_clamp_height(GimpToolRectangle * rectangle,ClampedSide * clamped_sides,GimpRectangleConstraint constraint,gboolean symmetrically)2997 gimp_tool_rectangle_clamp_height (GimpToolRectangle       *rectangle,
2998                                   ClampedSide             *clamped_sides,
2999                                   GimpRectangleConstraint  constraint,
3000                                   gboolean                 symmetrically)
3001 {
3002   GimpToolRectanglePrivate *private = rectangle->private;
3003   gint                      min_y;
3004   gint                      max_y;
3005 
3006   if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
3007     return;
3008 
3009   gimp_tool_rectangle_get_constraints (rectangle,
3010                                        NULL, &min_y,
3011                                        NULL, &max_y,
3012                                        constraint);
3013   if (private->y1 < min_y)
3014     {
3015       gdouble dy = min_y - private->y1;
3016 
3017       private->y1 += dy;
3018 
3019       if (symmetrically)
3020         private->y2 -= dy;
3021 
3022       if (private->y2 < min_y)
3023         private->y2 = min_y;
3024 
3025       if (clamped_sides)
3026         *clamped_sides |= CLAMPED_TOP;
3027     }
3028 
3029   if (private->y2 > max_y)
3030     {
3031       gdouble dy = max_y - private->y2;
3032 
3033       private->y2 += dy;
3034 
3035       if (symmetrically)
3036         private->y1 -= dy;
3037 
3038       if (private->y1 > max_y)
3039         private->y1 = max_y;
3040 
3041       if (clamped_sides)
3042         *clamped_sides |= CLAMPED_BOTTOM;
3043     }
3044 }
3045 
3046 /**
3047  * gimp_tool_rectangle_keep_inside:
3048  * @rectangle: A #GimpToolRectangle.
3049  *
3050  * If the rectangle is outside of the canvas, move it into it. If the rectangle is
3051  * larger than the canvas in any direction, make it fill the canvas in that direction.
3052  */
3053 static void
gimp_tool_rectangle_keep_inside(GimpToolRectangle * rectangle,GimpRectangleConstraint constraint)3054 gimp_tool_rectangle_keep_inside (GimpToolRectangle      *rectangle,
3055                                  GimpRectangleConstraint constraint)
3056 {
3057   gimp_tool_rectangle_keep_inside_horizontally (rectangle, constraint);
3058   gimp_tool_rectangle_keep_inside_vertically   (rectangle, constraint);
3059 }
3060 
3061 /**
3062  * gimp_tool_rectangle_keep_inside_horizontally:
3063  * @rectangle:      A #GimpToolRectangle.
3064  * @constraint:     Constraint to use.
3065  *
3066  * If the rectangle is outside of the given constraint horizontally, move it
3067  * inside. If it is too big to fit inside, make it just as big as the width
3068  * limit.
3069  */
3070 static void
gimp_tool_rectangle_keep_inside_horizontally(GimpToolRectangle * rectangle,GimpRectangleConstraint constraint)3071 gimp_tool_rectangle_keep_inside_horizontally (GimpToolRectangle       *rectangle,
3072                                               GimpRectangleConstraint  constraint)
3073 {
3074   GimpToolRectanglePrivate *private = rectangle->private;
3075   gint                      min_x;
3076   gint                      max_x;
3077 
3078   if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
3079     return;
3080 
3081   gimp_tool_rectangle_get_constraints (rectangle,
3082                                        &min_x, NULL,
3083                                        &max_x, NULL,
3084                                        constraint);
3085 
3086   if (max_x - min_x < private->x2 - private->x1)
3087     {
3088       private->x1 = min_x;
3089       private->x2 = max_x;
3090     }
3091   else
3092     {
3093       if (private->x1 < min_x)
3094         {
3095           gdouble dx = min_x - private->x1;
3096 
3097           private->x1 += dx;
3098           private->x2 += dx;
3099         }
3100       if (private->x2 > max_x)
3101         {
3102           gdouble dx = max_x - private->x2;
3103 
3104           private->x1 += dx;
3105           private->x2 += dx;
3106         }
3107     }
3108 }
3109 
3110 /**
3111  * gimp_tool_rectangle_keep_inside_vertically:
3112  * @rectangle:      A #GimpToolRectangle.
3113  * @constraint:     Constraint to use.
3114  *
3115  * If the rectangle is outside of the given constraint vertically,
3116  * move it inside. If it is too big to fit inside, make it just as big
3117  * as the width limit.
3118  */
3119 static void
gimp_tool_rectangle_keep_inside_vertically(GimpToolRectangle * rectangle,GimpRectangleConstraint constraint)3120 gimp_tool_rectangle_keep_inside_vertically (GimpToolRectangle       *rectangle,
3121                                             GimpRectangleConstraint  constraint)
3122 {
3123   GimpToolRectanglePrivate *private = rectangle->private;
3124   gint                      min_y;
3125   gint                      max_y;
3126 
3127   if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
3128     return;
3129 
3130   gimp_tool_rectangle_get_constraints (rectangle,
3131                                        NULL, &min_y,
3132                                        NULL, &max_y,
3133                                        constraint);
3134 
3135   if (max_y - min_y < private->y2 - private->y1)
3136     {
3137       private->y1 = min_y;
3138       private->y2 = max_y;
3139     }
3140   else
3141     {
3142       if (private->y1 < min_y)
3143         {
3144           gdouble dy = min_y - private->y1;
3145 
3146           private->y1 += dy;
3147           private->y2 += dy;
3148         }
3149       if (private->y2 > max_y)
3150         {
3151           gdouble dy = max_y - private->y2;
3152 
3153           private->y1 += dy;
3154           private->y2 += dy;
3155         }
3156     }
3157 }
3158 
3159 /**
3160  * gimp_tool_rectangle_apply_fixed_width:
3161  * @rectangle:      A #GimpToolRectangle.
3162  * @constraint:     Constraint to use.
3163  * @width:
3164  *
3165  * Makes the rectangle have a fixed_width, following the constrainment
3166  * rules of fixed widths as well. Please refer to the rectangle tools
3167  * spec.
3168  */
3169 static void
gimp_tool_rectangle_apply_fixed_width(GimpToolRectangle * rectangle,GimpRectangleConstraint constraint,gdouble width)3170 gimp_tool_rectangle_apply_fixed_width (GimpToolRectangle      *rectangle,
3171                                        GimpRectangleConstraint constraint,
3172                                        gdouble                 width)
3173 {
3174   GimpToolRectanglePrivate *private = rectangle->private;
3175 
3176   switch (private->function)
3177     {
3178     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
3179     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
3180     case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
3181       /* We always want to center around fixed_center here, since we want the
3182        * anchor point to be directly on the opposite side.
3183        */
3184       private->x1 = private->center_x_on_fixed_center -
3185                     width / 2;
3186       private->x2 = private->x1 + width;
3187       break;
3188 
3189     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
3190     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
3191     case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
3192       /* We always want to center around fixed_center here, since we want the
3193        * anchor point to be directly on the opposite side.
3194        */
3195       private->x1 = private->center_x_on_fixed_center -
3196                     width / 2;
3197       private->x2 = private->x1 + width;
3198       break;
3199 
3200     default:
3201       break;
3202     }
3203 
3204   /* Width shall be kept even after constraints, so we move the
3205    * rectangle sideways rather than adjusting a side.
3206    */
3207   gimp_tool_rectangle_keep_inside_horizontally (rectangle, constraint);
3208 }
3209 
3210 /**
3211  * gimp_tool_rectangle_apply_fixed_height:
3212  * @rectangle:      A #GimpToolRectangle.
3213  * @constraint:     Constraint to use.
3214  * @height:
3215  *
3216  * Makes the rectangle have a fixed_height, following the
3217  * constrainment rules of fixed heights as well. Please refer to the
3218  * rectangle tools spec.
3219  */
3220 static void
gimp_tool_rectangle_apply_fixed_height(GimpToolRectangle * rectangle,GimpRectangleConstraint constraint,gdouble height)3221 gimp_tool_rectangle_apply_fixed_height (GimpToolRectangle      *rectangle,
3222                                         GimpRectangleConstraint constraint,
3223                                         gdouble                 height)
3224 
3225 {
3226   GimpToolRectanglePrivate *private = rectangle->private;
3227 
3228   switch (private->function)
3229     {
3230     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
3231     case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
3232     case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
3233       /* We always want to center around fixed_center here, since we
3234        * want the anchor point to be directly on the opposite side.
3235        */
3236       private->y1 = private->center_y_on_fixed_center -
3237                     height / 2;
3238       private->y2 = private->y1 + height;
3239       break;
3240 
3241     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
3242     case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
3243     case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
3244       /* We always want to center around fixed_center here, since we
3245        * want the anchor point to be directly on the opposite side.
3246        */
3247       private->y1 = private->center_y_on_fixed_center -
3248                     height / 2;
3249       private->y2 = private->y1 + height;
3250       break;
3251 
3252     default:
3253       break;
3254     }
3255 
3256   /* Width shall be kept even after constraints, so we move the
3257    * rectangle sideways rather than adjusting a side.
3258    */
3259   gimp_tool_rectangle_keep_inside_vertically (rectangle, constraint);
3260 }
3261 
3262 /**
3263  * gimp_tool_rectangle_apply_aspect:
3264  * @rectangle:      A #GimpToolRectangle.
3265  * @aspect:         The desired aspect.
3266  * @clamped_sides:  Bitfield of sides that have been clamped.
3267  *
3268  * Adjust the rectangle to the desired aspect.
3269  *
3270  * Sometimes, a side must not be moved outwards, for example if a the
3271  * RIGHT side has been clamped previously, we must not move the RIGHT
3272  * side to the right, since that would violate the constraint
3273  * again. The clamped_sides bitfield keeps track of sides that have
3274  * previously been clamped.
3275  *
3276  * If fixed_center is used, the function adjusts the aspect by
3277  * symmetrically adjusting the left and right, or top and bottom side.
3278  */
3279 static void
gimp_tool_rectangle_apply_aspect(GimpToolRectangle * rectangle,gdouble aspect,gint clamped_sides)3280 gimp_tool_rectangle_apply_aspect (GimpToolRectangle *rectangle,
3281                                   gdouble            aspect,
3282                                   gint               clamped_sides)
3283 {
3284   GimpToolRectanglePrivate *private = rectangle->private;
3285   gdouble                   current_w;
3286   gdouble                   current_h;
3287   gdouble                   current_aspect;
3288   SideToResize              side_to_resize = SIDE_TO_RESIZE_NONE;
3289 
3290   current_w = private->x2 - private->x1;
3291   current_h = private->y2 - private->y1;
3292 
3293   current_aspect = (gdouble) current_w / (gdouble) current_h;
3294 
3295   /* Do we have to do anything? */
3296   if (current_aspect == aspect)
3297     return;
3298 
3299   if (private->fixed_center)
3300     {
3301       /* We may only adjust the sides symmetrically to get desired aspect. */
3302       if (current_aspect > aspect)
3303         {
3304           /* We prefer to use top and bottom (since that will make the
3305            * cursor remain on the rectangle edge), unless that is what
3306            * the user has grabbed.
3307            */
3308           switch (private->function)
3309             {
3310             case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
3311             case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
3312             case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
3313             case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
3314             case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
3315             case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
3316               if (! (clamped_sides & CLAMPED_TOP) &&
3317                   ! (clamped_sides & CLAMPED_BOTTOM))
3318                 {
3319                   side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
3320                 }
3321               else
3322                 {
3323                   side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
3324                 }
3325               break;
3326 
3327             case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
3328             case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
3329             default:
3330               side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
3331               break;
3332             }
3333         }
3334       else /* (current_aspect < aspect) */
3335         {
3336           /* We prefer to use left and right (since that will make the
3337            * cursor remain on the rectangle edge), unless that is what
3338            * the user has grabbed.
3339            */
3340           switch (private->function)
3341             {
3342             case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
3343             case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
3344             case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
3345             case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
3346             case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
3347             case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
3348               if (! (clamped_sides & CLAMPED_LEFT) &&
3349                   ! (clamped_sides & CLAMPED_RIGHT))
3350                 {
3351                   side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
3352                 }
3353               else
3354                 {
3355                   side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
3356                 }
3357               break;
3358 
3359             case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
3360             case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
3361             default:
3362               side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
3363               break;
3364             }
3365         }
3366     }
3367   else if (current_aspect > aspect)
3368     {
3369       /* We can safely pick LEFT or RIGHT, since using those sides
3370        * will make the rectangle smaller, so we don't need to check
3371        * for clamped_sides. We may only use TOP and BOTTOM if not
3372        * those sides have been clamped, since using them will make the
3373        * rectangle bigger.
3374        */
3375       switch (private->function)
3376         {
3377         case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
3378           if (! (clamped_sides & CLAMPED_TOP))
3379             side_to_resize = SIDE_TO_RESIZE_TOP;
3380           else
3381             side_to_resize = SIDE_TO_RESIZE_LEFT;
3382           break;
3383 
3384         case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
3385           if (! (clamped_sides & CLAMPED_TOP))
3386             side_to_resize = SIDE_TO_RESIZE_TOP;
3387           else
3388             side_to_resize = SIDE_TO_RESIZE_RIGHT;
3389           break;
3390 
3391         case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
3392           if (! (clamped_sides & CLAMPED_BOTTOM))
3393             side_to_resize = SIDE_TO_RESIZE_BOTTOM;
3394           else
3395             side_to_resize = SIDE_TO_RESIZE_LEFT;
3396           break;
3397 
3398         case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
3399           if (! (clamped_sides & CLAMPED_BOTTOM))
3400             side_to_resize = SIDE_TO_RESIZE_BOTTOM;
3401           else
3402             side_to_resize = SIDE_TO_RESIZE_RIGHT;
3403           break;
3404 
3405         case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
3406           if (! (clamped_sides & CLAMPED_TOP) &&
3407               ! (clamped_sides & CLAMPED_BOTTOM))
3408             side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
3409           else
3410             side_to_resize = SIDE_TO_RESIZE_LEFT;
3411           break;
3412 
3413         case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
3414           if (! (clamped_sides & CLAMPED_TOP) &&
3415               ! (clamped_sides & CLAMPED_BOTTOM))
3416             side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
3417           else
3418             side_to_resize = SIDE_TO_RESIZE_RIGHT;
3419           break;
3420 
3421         case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
3422         case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
3423           side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
3424           break;
3425 
3426         case GIMP_TOOL_RECTANGLE_MOVING:
3427         default:
3428           if (! (clamped_sides & CLAMPED_BOTTOM))
3429             side_to_resize = SIDE_TO_RESIZE_BOTTOM;
3430           else if (! (clamped_sides & CLAMPED_RIGHT))
3431             side_to_resize = SIDE_TO_RESIZE_RIGHT;
3432           else if (! (clamped_sides & CLAMPED_TOP))
3433             side_to_resize = SIDE_TO_RESIZE_TOP;
3434           else if (! (clamped_sides & CLAMPED_LEFT))
3435             side_to_resize = SIDE_TO_RESIZE_LEFT;
3436           break;
3437         }
3438     }
3439   else /* (current_aspect < aspect) */
3440     {
3441       /* We can safely pick TOP or BOTTOM, since using those sides
3442        * will make the rectangle smaller, so we don't need to check
3443        * for clamped_sides. We may only use LEFT and RIGHT if not
3444        * those sides have been clamped, since using them will make the
3445        * rectangle bigger.
3446        */
3447       switch (private->function)
3448         {
3449         case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_LEFT:
3450           if (! (clamped_sides & CLAMPED_LEFT))
3451             side_to_resize = SIDE_TO_RESIZE_LEFT;
3452           else
3453             side_to_resize = SIDE_TO_RESIZE_TOP;
3454           break;
3455 
3456         case GIMP_TOOL_RECTANGLE_RESIZING_UPPER_RIGHT:
3457           if (! (clamped_sides & CLAMPED_RIGHT))
3458             side_to_resize = SIDE_TO_RESIZE_RIGHT;
3459           else
3460             side_to_resize = SIDE_TO_RESIZE_TOP;
3461           break;
3462 
3463         case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_LEFT:
3464           if (! (clamped_sides & CLAMPED_LEFT))
3465             side_to_resize = SIDE_TO_RESIZE_LEFT;
3466           else
3467             side_to_resize = SIDE_TO_RESIZE_BOTTOM;
3468           break;
3469 
3470         case GIMP_TOOL_RECTANGLE_RESIZING_LOWER_RIGHT:
3471           if (! (clamped_sides & CLAMPED_RIGHT))
3472             side_to_resize = SIDE_TO_RESIZE_RIGHT;
3473           else
3474             side_to_resize = SIDE_TO_RESIZE_BOTTOM;
3475           break;
3476 
3477         case GIMP_TOOL_RECTANGLE_RESIZING_TOP:
3478           if (! (clamped_sides & CLAMPED_LEFT) &&
3479               ! (clamped_sides & CLAMPED_RIGHT))
3480             side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
3481           else
3482             side_to_resize = SIDE_TO_RESIZE_TOP;
3483           break;
3484 
3485         case GIMP_TOOL_RECTANGLE_RESIZING_BOTTOM:
3486           if (! (clamped_sides & CLAMPED_LEFT) &&
3487               ! (clamped_sides & CLAMPED_RIGHT))
3488             side_to_resize = SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY;
3489           else
3490             side_to_resize = SIDE_TO_RESIZE_BOTTOM;
3491           break;
3492 
3493         case GIMP_TOOL_RECTANGLE_RESIZING_LEFT:
3494         case GIMP_TOOL_RECTANGLE_RESIZING_RIGHT:
3495           side_to_resize = SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY;
3496           break;
3497 
3498         case GIMP_TOOL_RECTANGLE_MOVING:
3499         default:
3500           if (! (clamped_sides & CLAMPED_BOTTOM))
3501             side_to_resize = SIDE_TO_RESIZE_BOTTOM;
3502           else if (! (clamped_sides & CLAMPED_RIGHT))
3503             side_to_resize = SIDE_TO_RESIZE_RIGHT;
3504           else if (! (clamped_sides & CLAMPED_TOP))
3505             side_to_resize = SIDE_TO_RESIZE_TOP;
3506           else if (! (clamped_sides & CLAMPED_LEFT))
3507             side_to_resize = SIDE_TO_RESIZE_LEFT;
3508           break;
3509         }
3510     }
3511 
3512   /* We now know what side(s) we should resize, so now we just solve
3513    * the aspect equation for that side(s).
3514    */
3515   switch (side_to_resize)
3516     {
3517     case SIDE_TO_RESIZE_NONE:
3518       return;
3519 
3520     case SIDE_TO_RESIZE_LEFT:
3521       private->x1 = private->x2 - aspect * current_h;
3522       break;
3523 
3524     case SIDE_TO_RESIZE_RIGHT:
3525       private->x2 = private->x1 + aspect * current_h;
3526       break;
3527 
3528     case SIDE_TO_RESIZE_TOP:
3529       private->y1 = private->y2 - current_w / aspect;
3530       break;
3531 
3532     case SIDE_TO_RESIZE_BOTTOM:
3533       private->y2 = private->y1 + current_w / aspect;
3534       break;
3535 
3536     case SIDE_TO_RESIZE_TOP_AND_BOTTOM_SYMMETRICALLY:
3537       {
3538         gdouble correct_h = current_w / aspect;
3539 
3540         private->y1 = private->center_y_on_fixed_center - correct_h / 2;
3541         private->y2 = private->y1 + correct_h;
3542       }
3543       break;
3544 
3545     case SIDE_TO_RESIZE_LEFT_AND_RIGHT_SYMMETRICALLY:
3546       {
3547         gdouble correct_w = current_h * aspect;
3548 
3549         private->x1 = private->center_x_on_fixed_center - correct_w / 2;
3550         private->x2 = private->x1 + correct_w;
3551       }
3552       break;
3553     }
3554 }
3555 
3556 /**
3557  * gimp_tool_rectangle_update_with_coord:
3558  * @rectangle:      A #GimpToolRectangle.
3559  * @new_x:          New X-coordinate in the context of the current function.
3560  * @new_y:          New Y-coordinate in the context of the current function.
3561  *
3562  * The core rectangle adjustment function. It updates the rectangle
3563  * for the passed cursor coordinate, taking current function and tool
3564  * options into account.  It also updates the current
3565  * private->function if necessary.
3566  */
3567 static void
gimp_tool_rectangle_update_with_coord(GimpToolRectangle * rectangle,gdouble new_x,gdouble new_y)3568 gimp_tool_rectangle_update_with_coord (GimpToolRectangle *rectangle,
3569                                        gdouble            new_x,
3570                                        gdouble            new_y)
3571 {
3572   GimpToolRectanglePrivate *private = rectangle->private;
3573 
3574   /* Move the corner or edge the user currently has grabbed. */
3575   gimp_tool_rectangle_apply_coord (rectangle, new_x, new_y);
3576 
3577   /* Update private->function. The function changes if the user
3578    * "flips" the rectangle.
3579    */
3580   gimp_tool_rectangle_check_function (rectangle);
3581 
3582   /* Clamp the rectangle if necessary */
3583   gimp_tool_rectangle_handle_general_clamping (rectangle);
3584 
3585   /* If the rectangle is being moved, do not run through any further
3586    * rectangle adjusting functions since it's shape should not change
3587    * then.
3588    */
3589   if (private->function != GIMP_TOOL_RECTANGLE_MOVING)
3590     {
3591       gimp_tool_rectangle_apply_fixed_rule (rectangle);
3592     }
3593 
3594   gimp_tool_rectangle_update_int_rect (rectangle);
3595 }
3596 
3597 static void
gimp_tool_rectangle_apply_fixed_rule(GimpToolRectangle * rectangle)3598 gimp_tool_rectangle_apply_fixed_rule (GimpToolRectangle *rectangle)
3599 {
3600   GimpToolRectanglePrivate    *private = rectangle->private;
3601   GimpRectangleConstraint      constraint_to_use;
3602   GimpDisplayShell            *shell;
3603   GimpImage                   *image;
3604 
3605   shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
3606   image = gimp_display_get_image (shell->display);
3607 
3608   /* Calculate what constraint to use when needed. */
3609   constraint_to_use = gimp_tool_rectangle_get_constraint (rectangle);
3610 
3611   if (private->fixed_rule_active &&
3612       private->fixed_rule == GIMP_RECTANGLE_FIXED_ASPECT)
3613     {
3614       gdouble aspect;
3615 
3616       aspect = CLAMP (private->aspect_numerator /
3617                       private->aspect_denominator,
3618                       1.0 / gimp_image_get_height (image),
3619                       gimp_image_get_width (image));
3620 
3621       if (constraint_to_use == GIMP_RECTANGLE_CONSTRAIN_NONE)
3622         {
3623           gimp_tool_rectangle_apply_aspect (rectangle,
3624                                             aspect,
3625                                             CLAMPED_NONE);
3626         }
3627       else
3628         {
3629           if (private->function != GIMP_TOOL_RECTANGLE_MOVING)
3630             {
3631               ClampedSide clamped_sides = CLAMPED_NONE;
3632 
3633               gimp_tool_rectangle_apply_aspect (rectangle,
3634                                                 aspect,
3635                                                 clamped_sides);
3636 
3637               /* After we have applied aspect, we might have taken the
3638                * rectangle outside of constraint, so clamp and apply
3639                * aspect again. We will get the right result this time,
3640                * since 'clamped_sides' will be setup correctly now.
3641                */
3642               gimp_tool_rectangle_clamp (rectangle,
3643                                          &clamped_sides,
3644                                          constraint_to_use,
3645                                          private->fixed_center);
3646 
3647               gimp_tool_rectangle_apply_aspect (rectangle,
3648                                                 aspect,
3649                                                 clamped_sides);
3650             }
3651           else
3652             {
3653               gimp_tool_rectangle_apply_aspect (rectangle,
3654                                                 aspect,
3655                                                 CLAMPED_NONE);
3656 
3657               gimp_tool_rectangle_keep_inside (rectangle,
3658                                                constraint_to_use);
3659             }
3660         }
3661     }
3662   else if (private->fixed_rule_active &&
3663            private->fixed_rule == GIMP_RECTANGLE_FIXED_SIZE)
3664     {
3665       gimp_tool_rectangle_apply_fixed_width (rectangle,
3666                                              constraint_to_use,
3667                                              private->desired_fixed_size_width);
3668       gimp_tool_rectangle_apply_fixed_height (rectangle,
3669                                               constraint_to_use,
3670                                               private->desired_fixed_size_height);
3671     }
3672   else if (private->fixed_rule_active &&
3673            private->fixed_rule == GIMP_RECTANGLE_FIXED_WIDTH)
3674     {
3675       gimp_tool_rectangle_apply_fixed_width (rectangle,
3676                                              constraint_to_use,
3677                                              private->desired_fixed_width);
3678     }
3679   else if (private->fixed_rule_active &&
3680            private->fixed_rule == GIMP_RECTANGLE_FIXED_HEIGHT)
3681     {
3682       gimp_tool_rectangle_apply_fixed_height (rectangle,
3683                                               constraint_to_use,
3684                                               private->desired_fixed_height);
3685     }
3686 }
3687 
3688 /**
3689  * gimp_tool_rectangle_get_constraints:
3690  * @rectangle:      A #GimpToolRectangle.
3691  * @min_x:
3692  * @min_y:
3693  * @max_x:
3694  * @max_y:          Pointers of where to put constraints. NULL allowed.
3695  * @constraint:     Whether to return image or layer constraints.
3696  *
3697  * Calculates constraint coordinates for image or layer.
3698  */
3699 static void
gimp_tool_rectangle_get_constraints(GimpToolRectangle * rectangle,gint * min_x,gint * min_y,gint * max_x,gint * max_y,GimpRectangleConstraint constraint)3700 gimp_tool_rectangle_get_constraints (GimpToolRectangle       *rectangle,
3701                                      gint                    *min_x,
3702                                      gint                    *min_y,
3703                                      gint                    *max_x,
3704                                      gint                    *max_y,
3705                                      GimpRectangleConstraint  constraint)
3706 {
3707   GimpDisplayShell *shell;
3708   GimpImage        *image;
3709   gint              min_x_dummy;
3710   gint              min_y_dummy;
3711   gint              max_x_dummy;
3712   gint              max_y_dummy;
3713 
3714   shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
3715   image = gimp_display_get_image (shell->display);
3716 
3717   if (! min_x) min_x = &min_x_dummy;
3718   if (! min_y) min_y = &min_y_dummy;
3719   if (! max_x) max_x = &max_x_dummy;
3720   if (! max_y) max_y = &max_y_dummy;
3721 
3722   *min_x = 0;
3723   *min_y = 0;
3724   *max_x = 0;
3725   *max_y = 0;
3726 
3727   switch (constraint)
3728     {
3729     case GIMP_RECTANGLE_CONSTRAIN_IMAGE:
3730       if (image)
3731         {
3732           *min_x = 0;
3733           *min_y = 0;
3734           *max_x = gimp_image_get_width  (image);
3735           *max_y = gimp_image_get_height (image);
3736         }
3737       break;
3738 
3739     case GIMP_RECTANGLE_CONSTRAIN_DRAWABLE:
3740       if (image)
3741         {
3742           GimpItem *item = GIMP_ITEM (gimp_image_get_active_drawable (image));
3743 
3744           if (item)
3745             {
3746               gimp_item_get_offset (item, min_x, min_y);
3747               *max_x = *min_x + gimp_item_get_width  (item);
3748               *max_y = *min_y + gimp_item_get_height (item);
3749             }
3750         }
3751       break;
3752 
3753     default:
3754       g_warning ("Invalid rectangle constraint.\n");
3755       return;
3756     }
3757 }
3758 
3759 /**
3760  * gimp_tool_rectangle_handle_general_clamping:
3761  * @rectangle: A #GimpToolRectangle.
3762  *
3763  * Make sure that constraints are applied to the rectangle, either by
3764  * manually doing it, or by looking at the rectangle tool options and
3765  * concluding it will be done later.
3766  */
3767 static void
gimp_tool_rectangle_handle_general_clamping(GimpToolRectangle * rectangle)3768 gimp_tool_rectangle_handle_general_clamping (GimpToolRectangle *rectangle)
3769 {
3770   GimpToolRectanglePrivate *private = rectangle->private;
3771   GimpRectangleConstraint   constraint;
3772 
3773   constraint = gimp_tool_rectangle_get_constraint (rectangle);
3774 
3775   /* fixed_aspect takes care of clamping by it self, so just return in
3776    * case that is in use. Also return if no constraints should be
3777    * enforced.
3778    */
3779   if (constraint == GIMP_RECTANGLE_CONSTRAIN_NONE)
3780     return;
3781 
3782   if (private->function != GIMP_TOOL_RECTANGLE_MOVING)
3783     {
3784       gimp_tool_rectangle_clamp (rectangle,
3785                                  NULL,
3786                                  constraint,
3787                                  private->fixed_center);
3788     }
3789   else
3790     {
3791       gimp_tool_rectangle_keep_inside (rectangle, constraint);
3792     }
3793 }
3794 
3795 /**
3796  * gimp_tool_rectangle_update_int_rect:
3797  * @rectangle:
3798  *
3799  * Update integer representation of rectangle.
3800  **/
3801 static void
gimp_tool_rectangle_update_int_rect(GimpToolRectangle * rectangle)3802 gimp_tool_rectangle_update_int_rect (GimpToolRectangle *rectangle)
3803 {
3804   GimpToolRectanglePrivate *private = rectangle->private;
3805 
3806   private->x1_int = SIGNED_ROUND (private->x1);
3807   private->y1_int = SIGNED_ROUND (private->y1);
3808 
3809   if (gimp_tool_rectangle_rect_rubber_banding_func (rectangle))
3810     {
3811       private->width_int  = (gint) SIGNED_ROUND (private->x2) - private->x1_int;
3812       private->height_int = (gint) SIGNED_ROUND (private->y2) - private->y1_int;
3813     }
3814 }
3815 
3816 /**
3817  * gimp_tool_rectangle_adjust_coord:
3818  * @rectangle:
3819  * @ccoord_x_input:
3820  * @ccoord_x_input:
3821  * @ccoord_x_output:
3822  * @ccoord_x_output:
3823  *
3824  * Transforms a coordinate to better fit the public behaviour of the
3825  * rectangle.
3826  */
3827 static void
gimp_tool_rectangle_adjust_coord(GimpToolRectangle * rectangle,gdouble coord_x_input,gdouble coord_y_input,gdouble * coord_x_output,gdouble * coord_y_output)3828 gimp_tool_rectangle_adjust_coord (GimpToolRectangle *rectangle,
3829                                   gdouble            coord_x_input,
3830                                   gdouble            coord_y_input,
3831                                   gdouble           *coord_x_output,
3832                                   gdouble           *coord_y_output)
3833 {
3834   GimpToolRectanglePrivate *priv = rectangle->private;
3835 
3836   switch (priv->precision)
3837     {
3838     case GIMP_RECTANGLE_PRECISION_INT:
3839       *coord_x_output = RINT (coord_x_input);
3840       *coord_y_output = RINT (coord_y_input);
3841       break;
3842 
3843     case GIMP_RECTANGLE_PRECISION_DOUBLE:
3844     default:
3845       *coord_x_output = coord_x_input;
3846       *coord_y_output = coord_y_input;
3847       break;
3848     }
3849 }
3850 
3851 static void
gimp_tool_rectangle_recalculate_center_xy(GimpToolRectangle * rectangle)3852 gimp_tool_rectangle_recalculate_center_xy (GimpToolRectangle *rectangle)
3853 {
3854   GimpToolRectanglePrivate *private = rectangle->private;
3855 
3856   private->center_x_on_fixed_center = (private->x1 + private->x2) / 2;
3857   private->center_y_on_fixed_center = (private->y1 + private->y2) / 2;
3858 }
3859 
3860 
3861 /*  public functions  */
3862 
3863 GimpToolWidget *
gimp_tool_rectangle_new(GimpDisplayShell * shell)3864 gimp_tool_rectangle_new (GimpDisplayShell  *shell)
3865 {
3866   g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
3867 
3868   return g_object_new (GIMP_TYPE_TOOL_RECTANGLE,
3869                        "shell",      shell,
3870                        NULL);
3871 }
3872 
3873 GimpRectangleFunction
gimp_tool_rectangle_get_function(GimpToolRectangle * rectangle)3874 gimp_tool_rectangle_get_function (GimpToolRectangle *rectangle)
3875 {
3876   g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle),
3877                         GIMP_TOOL_RECTANGLE_DEAD);
3878 
3879   return rectangle->private->function;
3880 }
3881 
3882 void
gimp_tool_rectangle_set_function(GimpToolRectangle * rectangle,GimpRectangleFunction function)3883 gimp_tool_rectangle_set_function (GimpToolRectangle     *rectangle,
3884                                   GimpRectangleFunction  function)
3885 {
3886   GimpToolRectanglePrivate *private;
3887 
3888   g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
3889 
3890   private = rectangle->private;
3891 
3892   if (private->function != function)
3893     {
3894       private->function = function;
3895 
3896       gimp_tool_rectangle_changed (GIMP_TOOL_WIDGET (rectangle));
3897     }
3898 }
3899 
3900 void
gimp_tool_rectangle_set_constraint(GimpToolRectangle * rectangle,GimpRectangleConstraint constraint)3901 gimp_tool_rectangle_set_constraint (GimpToolRectangle       *rectangle,
3902                                     GimpRectangleConstraint  constraint)
3903 {
3904   GimpToolRectanglePrivate *private;
3905 
3906   g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
3907 
3908   private = rectangle->private;
3909 
3910   if (constraint != private->constraint)
3911     {
3912       g_object_freeze_notify (G_OBJECT (rectangle));
3913 
3914       private->constraint = constraint;
3915       g_object_notify (G_OBJECT (rectangle), "constraint");
3916 
3917       gimp_tool_rectangle_clamp (rectangle, NULL, constraint, FALSE);
3918 
3919       g_object_thaw_notify (G_OBJECT (rectangle));
3920 
3921       gimp_tool_rectangle_change_complete (rectangle);
3922     }
3923 }
3924 
3925 GimpRectangleConstraint
gimp_tool_rectangle_get_constraint(GimpToolRectangle * rectangle)3926 gimp_tool_rectangle_get_constraint (GimpToolRectangle *rectangle)
3927 {
3928   g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), 0);
3929 
3930   return rectangle->private->constraint;
3931 }
3932 
3933 /**
3934  * gimp_tool_rectangle_get_public_rect:
3935  * @rectangle:
3936  * @x1:
3937  * @y1:
3938  * @x2:
3939  * @y2:
3940  *
3941  * This function returns the rectangle as it appears to be publicly
3942  * (based on integer or double precision-mode).
3943  **/
3944 void
gimp_tool_rectangle_get_public_rect(GimpToolRectangle * rectangle,gdouble * x1,gdouble * y1,gdouble * x2,gdouble * y2)3945 gimp_tool_rectangle_get_public_rect (GimpToolRectangle *rectangle,
3946                                      gdouble           *x1,
3947                                      gdouble           *y1,
3948                                      gdouble           *x2,
3949                                      gdouble           *y2)
3950 {
3951   GimpToolRectanglePrivate *priv;
3952 
3953   g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
3954   g_return_if_fail (x1 != NULL);
3955   g_return_if_fail (y1 != NULL);
3956   g_return_if_fail (x2 != NULL);
3957   g_return_if_fail (y2 != NULL);
3958 
3959   priv = rectangle->private;
3960 
3961   switch (priv->precision)
3962     {
3963     case GIMP_RECTANGLE_PRECISION_INT:
3964       *x1 = priv->x1_int;
3965       *y1 = priv->y1_int;
3966       *x2 = priv->x1_int + priv->width_int;
3967       *y2 = priv->y1_int + priv->height_int;
3968       break;
3969 
3970     case GIMP_RECTANGLE_PRECISION_DOUBLE:
3971     default:
3972       *x1 = priv->x1;
3973       *y1 = priv->y1;
3974       *x2 = priv->x2;
3975       *y2 = priv->y2;
3976       break;
3977     }
3978 }
3979 
3980 /**
3981  * gimp_tool_rectangle_pending_size_set:
3982  * @width_property:  Option property to set to pending rectangle width.
3983  * @height_property: Option property to set to pending rectangle height.
3984  *
3985  * Sets specified rectangle tool options properties to the width and
3986  * height of the current pending rectangle.
3987  */
3988 void
gimp_tool_rectangle_pending_size_set(GimpToolRectangle * rectangle,GObject * object,const gchar * width_property,const gchar * height_property)3989 gimp_tool_rectangle_pending_size_set (GimpToolRectangle *rectangle,
3990                                       GObject           *object,
3991                                       const gchar       *width_property,
3992                                       const gchar       *height_property)
3993 {
3994   GimpToolRectanglePrivate *private;
3995 
3996   g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
3997   g_return_if_fail (width_property  != NULL);
3998   g_return_if_fail (height_property != NULL);
3999 
4000   private = rectangle->private;
4001 
4002   g_object_set (object,
4003                 width_property,  MAX (private->x2 - private->x1, 1.0),
4004                 height_property, MAX (private->y2 - private->y1, 1.0),
4005                 NULL);
4006 }
4007 
4008 /**
4009  * gimp_tool_rectangle_constraint_size_set:
4010  * @width_property:  Option property to set to current constraint width.
4011  * @height_property: Option property to set to current constraint height.
4012  *
4013  * Sets specified rectangle tool options properties to the width and
4014  * height of the current constraint size.
4015  */
4016 void
gimp_tool_rectangle_constraint_size_set(GimpToolRectangle * rectangle,GObject * object,const gchar * width_property,const gchar * height_property)4017 gimp_tool_rectangle_constraint_size_set (GimpToolRectangle *rectangle,
4018                                          GObject           *object,
4019                                          const gchar       *width_property,
4020                                          const gchar       *height_property)
4021 {
4022   GimpDisplayShell *shell;
4023   GimpContext      *context;
4024   GimpImage        *image;
4025   gdouble           width;
4026   gdouble           height;
4027 
4028   g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
4029 
4030   shell   = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
4031   context = gimp_get_user_context (shell->display->gimp);
4032   image   = gimp_context_get_image (context);
4033 
4034   if (! image)
4035     {
4036       width  = 1.0;
4037       height = 1.0;
4038     }
4039   else
4040     {
4041       GimpRectangleConstraint constraint;
4042 
4043       constraint = gimp_tool_rectangle_get_constraint (rectangle);
4044 
4045       switch (constraint)
4046         {
4047         case GIMP_RECTANGLE_CONSTRAIN_DRAWABLE:
4048           {
4049             GimpItem *item = GIMP_ITEM (gimp_image_get_active_layer (image));
4050 
4051             if (! item)
4052               {
4053                 width  = 1.0;
4054                 height = 1.0;
4055               }
4056             else
4057               {
4058                 width  = gimp_item_get_width  (item);
4059                 height = gimp_item_get_height (item);
4060               }
4061           }
4062           break;
4063 
4064         case GIMP_RECTANGLE_CONSTRAIN_IMAGE:
4065         default:
4066           {
4067             width  = gimp_image_get_width  (image);
4068             height = gimp_image_get_height (image);
4069           }
4070           break;
4071         }
4072     }
4073 
4074   g_object_set (object,
4075                 width_property,  width,
4076                 height_property, height,
4077                 NULL);
4078 }
4079 
4080 /**
4081  * gimp_tool_rectangle_rectangle_is_first:
4082  * @rectangle:
4083  *
4084  * Returns: %TRUE if the user is creating the first rectangle with
4085  * this instance from scratch, %FALSE if modifying an existing
4086  * rectangle, or creating a new rectangle, discarding the existing
4087  * one. This function is only meaningful in _motion and
4088  * _button_release.
4089  */
4090 gboolean
gimp_tool_rectangle_rectangle_is_first(GimpToolRectangle * rectangle)4091 gimp_tool_rectangle_rectangle_is_first (GimpToolRectangle *rectangle)
4092 {
4093   g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), FALSE);
4094 
4095   return rectangle->private->is_first;
4096 }
4097 
4098 /**
4099  * gimp_tool_rectangle_rectangle_is_new:
4100  * @rectangle:
4101  *
4102  * Returns: %TRUE if the user is creating a new rectangle from
4103  * scratch, %FALSE if modifying n previously existing rectangle. This
4104  * function is only meaningful in _motion and _button_release.
4105  */
4106 gboolean
gimp_tool_rectangle_rectangle_is_new(GimpToolRectangle * rectangle)4107 gimp_tool_rectangle_rectangle_is_new (GimpToolRectangle *rectangle)
4108 {
4109   g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), FALSE);
4110 
4111   return rectangle->private->is_new;
4112 }
4113 
4114 /**
4115  * gimp_tool_rectangle_point_in_rectangle:
4116  * @rectangle:
4117  * @x:         X-coord of point to test (in image coordinates)
4118  * @y:         Y-coord of point to test (in image coordinates)
4119  *
4120  * Returns: %TRUE if the passed point was within the rectangle
4121  **/
4122 gboolean
gimp_tool_rectangle_point_in_rectangle(GimpToolRectangle * rectangle,gdouble x,gdouble y)4123 gimp_tool_rectangle_point_in_rectangle (GimpToolRectangle *rectangle,
4124                                         gdouble            x,
4125                                         gdouble            y)
4126 {
4127   gdouble  x1, y1, x2, y2;
4128 
4129   g_return_val_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle), FALSE);
4130 
4131   gimp_tool_rectangle_get_public_rect (rectangle, &x1, &y1, &x2, &y2);
4132 
4133   return (x >= x1 && x <= x2 &&
4134           y >= y1 && y <= y2);
4135 }
4136 
4137 /**
4138  * gimp_tool_rectangle_frame_item:
4139  * @rectangle: a #GimpToolRectangle interface
4140  * @item:      a #GimpItem attached to the image on which a
4141  *             rectangle is being shown.
4142  *
4143  * Convenience function to set the corners of the rectangle to
4144  * match the bounds of the specified item.  The rectangle interface
4145  * must be active (i.e., showing a rectangle), and the item must be
4146  * attached to the image on which the rectangle is active.
4147  **/
4148 void
gimp_tool_rectangle_frame_item(GimpToolRectangle * rectangle,GimpItem * item)4149 gimp_tool_rectangle_frame_item (GimpToolRectangle *rectangle,
4150                                 GimpItem          *item)
4151 {
4152   GimpDisplayShell *shell;
4153   gint              offset_x;
4154   gint              offset_y;
4155   gint              width;
4156   gint              height;
4157 
4158   g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
4159   g_return_if_fail (GIMP_IS_ITEM (item));
4160   g_return_if_fail (gimp_item_is_attached (item));
4161 
4162   shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
4163 
4164   g_return_if_fail (gimp_display_get_image (shell->display) ==
4165                     gimp_item_get_image (item));
4166 
4167   width  = gimp_item_get_width  (item);
4168   height = gimp_item_get_height (item);
4169 
4170   gimp_item_get_offset (item, &offset_x, &offset_y);
4171 
4172   gimp_tool_rectangle_set_function (rectangle, GIMP_TOOL_RECTANGLE_CREATING);
4173 
4174   g_object_set (rectangle,
4175                 "x1", (gdouble) offset_x,
4176                 "y1", (gdouble) offset_y,
4177                 "x2", (gdouble) (offset_x + width),
4178                 "y2", (gdouble) (offset_y + height),
4179                 NULL);
4180 
4181   /* kludge to force handle sizes to update.  This call may be harmful
4182    * if this function is ever moved out of the text tool code.
4183    */
4184   gimp_tool_rectangle_set_constraint (rectangle, GIMP_RECTANGLE_CONSTRAIN_NONE);
4185 }
4186 
4187 void
gimp_tool_rectangle_auto_shrink(GimpToolRectangle * rectangle,gboolean shrink_merged)4188 gimp_tool_rectangle_auto_shrink (GimpToolRectangle *rectangle,
4189                                  gboolean           shrink_merged)
4190 {
4191   GimpToolRectanglePrivate *private;
4192   GimpDisplayShell         *shell;
4193   GimpImage                *image;
4194   GimpPickable             *pickable;
4195   gint                      offset_x = 0;
4196   gint                      offset_y = 0;
4197   gint                      x1, y1;
4198   gint                      x2, y2;
4199   gint                      shrunk_x;
4200   gint                      shrunk_y;
4201   gint                      shrunk_width;
4202   gint                      shrunk_height;
4203 
4204   g_return_if_fail (GIMP_IS_TOOL_RECTANGLE (rectangle));
4205 
4206   private = rectangle->private;
4207 
4208   shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (rectangle));
4209   image = gimp_display_get_image (shell->display);
4210 
4211   if (shrink_merged)
4212     {
4213       pickable = GIMP_PICKABLE (image);
4214 
4215       x1 = private->x1;
4216       y1 = private->y1;
4217       x2 = private->x2;
4218       y2 = private->y2;
4219     }
4220   else
4221     {
4222       pickable = GIMP_PICKABLE (gimp_image_get_active_drawable (image));
4223 
4224       if (! pickable)
4225         return;
4226 
4227       gimp_item_get_offset (GIMP_ITEM (pickable), &offset_x, &offset_y);
4228 
4229       x1 = private->x1 - offset_x;
4230       y1 = private->y1 - offset_y;
4231       x2 = private->x2 - offset_x;
4232       y2 = private->y2 - offset_y;
4233     }
4234 
4235   switch (gimp_pickable_auto_shrink (pickable,
4236                                      x1, y1, x2 - x1, y2 - y1,
4237                                      &shrunk_x,
4238                                      &shrunk_y,
4239                                      &shrunk_width,
4240                                      &shrunk_height))
4241     {
4242     case GIMP_AUTO_SHRINK_SHRINK:
4243       {
4244         GimpRectangleFunction original_function = private->function;
4245 
4246         private->function = GIMP_TOOL_RECTANGLE_AUTO_SHRINK;
4247 
4248         private->x1 = offset_x + shrunk_x;
4249         private->y1 = offset_y + shrunk_y;
4250         private->x2 = offset_x + shrunk_x + shrunk_width;
4251         private->y2 = offset_y + shrunk_y + shrunk_height;
4252 
4253         gimp_tool_rectangle_update_int_rect (rectangle);
4254 
4255         gimp_tool_rectangle_change_complete (rectangle);
4256 
4257         private->function = original_function;
4258 
4259         gimp_tool_rectangle_update_options (rectangle);
4260       }
4261       break;
4262 
4263     default:
4264       break;
4265     }
4266 }
4267