1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gegl.h>
21 #include <gtk/gtk.h>
22 
23 #include "libgimpmath/gimpmath.h"
24 #include "libgimpwidgets/gimpwidgets.h"
25 
26 #include "tools-types.h"
27 
28 #include "core/gimp-transform-utils.h"
29 #include "core/gimpimage.h"
30 
31 #include "paint/gimpperspectiveclone.h"
32 #include "paint/gimpperspectivecloneoptions.h"
33 
34 #include "widgets/gimphelp-ids.h"
35 #include "widgets/gimpviewablebox.h"
36 #include "widgets/gimpwidgets-utils.h"
37 
38 #include "display/gimpcanvasgroup.h"
39 #include "display/gimpdisplay.h"
40 #include "display/gimptooltransformgrid.h"
41 
42 #include "gimpperspectiveclonetool.h"
43 #include "gimpcloneoptions-gui.h"
44 #include "gimptoolcontrol.h"
45 
46 #include "gimp-intl.h"
47 
48 
49 /*  index into trans_info array  */
50 enum
51 {
52   X0,
53   Y0,
54   X1,
55   Y1,
56   X2,
57   Y2,
58   X3,
59   Y3,
60   PIVOT_X,
61   PIVOT_Y
62 };
63 
64 
65 static void     gimp_perspective_clone_tool_constructed    (GObject          *object);
66 
67 static gboolean gimp_perspective_clone_tool_initialize     (GimpTool         *tool,
68                                                             GimpDisplay      *display,
69                                                             GError          **error);
70 
71 static gboolean gimp_perspective_clone_tool_has_display    (GimpTool         *tool,
72                                                             GimpDisplay      *display);
73 static GimpDisplay *
74                 gimp_perspective_clone_tool_has_image      (GimpTool         *tool,
75                                                             GimpImage        *image);
76 static void     gimp_perspective_clone_tool_control        (GimpTool         *tool,
77                                                             GimpToolAction    action,
78                                                             GimpDisplay      *display);
79 static void     gimp_perspective_clone_tool_button_press   (GimpTool         *tool,
80                                                             const GimpCoords *coords,
81                                                             guint32           time,
82                                                             GdkModifierType   state,
83                                                             GimpButtonPressType  press_type,
84                                                             GimpDisplay      *display);
85 static void     gimp_perspective_clone_tool_button_release (GimpTool         *tool,
86                                                             const GimpCoords *coords,
87                                                             guint32           time,
88                                                             GdkModifierType   state,
89                                                             GimpButtonReleaseType  release_type,
90                                                             GimpDisplay      *display);
91 static void     gimp_perspective_clone_tool_motion         (GimpTool         *tool,
92                                                             const GimpCoords *coords,
93                                                             guint32           time,
94                                                             GdkModifierType   state,
95                                                             GimpDisplay      *display);
96 static void     gimp_perspective_clone_tool_modifier_key   (GimpTool         *tool,
97                                                             GdkModifierType   key,
98                                                             gboolean          press,
99                                                             GdkModifierType   state,
100                                                             GimpDisplay      *display);
101 static void     gimp_perspective_clone_tool_cursor_update  (GimpTool         *tool,
102                                                             const GimpCoords *coords,
103                                                             GdkModifierType   state,
104                                                             GimpDisplay      *display);
105 static void     gimp_perspective_clone_tool_oper_update    (GimpTool         *tool,
106                                                             const GimpCoords *coords,
107                                                             GdkModifierType   state,
108                                                             gboolean          proximity,
109                                                             GimpDisplay      *display);
110 static void     gimp_perspective_clone_tool_options_notify (GimpTool         *tool,
111                                                             GimpToolOptions  *options,
112                                                             const GParamSpec *pspec);
113 
114 static void     gimp_perspective_clone_tool_draw           (GimpDrawTool             *draw_tool);
115 
116 static void     gimp_perspective_clone_tool_halt           (GimpPerspectiveCloneTool *clone_tool);
117 static void     gimp_perspective_clone_tool_bounds         (GimpPerspectiveCloneTool *clone_tool,
118                                                             GimpDisplay              *display);
119 static void     gimp_perspective_clone_tool_prepare        (GimpPerspectiveCloneTool *clone_tool);
120 static void     gimp_perspective_clone_tool_recalc_matrix  (GimpPerspectiveCloneTool *clone_tool,
121                                                             GimpToolWidget           *widget);
122 
123 static void     gimp_perspective_clone_tool_widget_changed (GimpToolWidget           *widget,
124                                                             GimpPerspectiveCloneTool *clone_tool);
125 static void     gimp_perspective_clone_tool_widget_status  (GimpToolWidget           *widget,
126                                                             const gchar              *status,
127                                                             GimpPerspectiveCloneTool *clone_tool);
128 
129 static GtkWidget *
130                 gimp_perspective_clone_options_gui         (GimpToolOptions *tool_options);
131 
132 
G_DEFINE_TYPE(GimpPerspectiveCloneTool,gimp_perspective_clone_tool,GIMP_TYPE_BRUSH_TOOL)133 G_DEFINE_TYPE (GimpPerspectiveCloneTool, gimp_perspective_clone_tool,
134                GIMP_TYPE_BRUSH_TOOL)
135 
136 #define parent_class gimp_perspective_clone_tool_parent_class
137 
138 
139 void
140 gimp_perspective_clone_tool_register (GimpToolRegisterCallback  callback,
141                                       gpointer                  data)
142 {
143   (* callback) (GIMP_TYPE_PERSPECTIVE_CLONE_TOOL,
144                 GIMP_TYPE_PERSPECTIVE_CLONE_OPTIONS,
145                 gimp_perspective_clone_options_gui,
146                 GIMP_PAINT_OPTIONS_CONTEXT_MASK |
147                 GIMP_CONTEXT_PROP_MASK_PATTERN,
148                 "gimp-perspective-clone-tool",
149                 _("Perspective Clone"),
150                 _("Perspective Clone Tool: Clone from an image source "
151                   "after applying a perspective transformation"),
152                 N_("_Perspective Clone"), NULL,
153                 NULL, GIMP_HELP_TOOL_PERSPECTIVE_CLONE,
154                 GIMP_ICON_TOOL_PERSPECTIVE_CLONE,
155                 data);
156 }
157 
158 static void
gimp_perspective_clone_tool_class_init(GimpPerspectiveCloneToolClass * klass)159 gimp_perspective_clone_tool_class_init (GimpPerspectiveCloneToolClass *klass)
160 {
161   GObjectClass      *object_class    = G_OBJECT_CLASS (klass);
162   GimpToolClass     *tool_class      = GIMP_TOOL_CLASS (klass);
163   GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
164 
165   object_class->constructed  = gimp_perspective_clone_tool_constructed;
166 
167   tool_class->initialize     = gimp_perspective_clone_tool_initialize;
168   tool_class->has_display    = gimp_perspective_clone_tool_has_display;
169   tool_class->has_image      = gimp_perspective_clone_tool_has_image;
170   tool_class->control        = gimp_perspective_clone_tool_control;
171   tool_class->button_press   = gimp_perspective_clone_tool_button_press;
172   tool_class->button_release = gimp_perspective_clone_tool_button_release;
173   tool_class->motion         = gimp_perspective_clone_tool_motion;
174   tool_class->modifier_key   = gimp_perspective_clone_tool_modifier_key;
175   tool_class->cursor_update  = gimp_perspective_clone_tool_cursor_update;
176   tool_class->oper_update    = gimp_perspective_clone_tool_oper_update;
177   tool_class->options_notify = gimp_perspective_clone_tool_options_notify;
178 
179   draw_tool_class->draw      = gimp_perspective_clone_tool_draw;
180 }
181 
182 static void
gimp_perspective_clone_tool_init(GimpPerspectiveCloneTool * clone_tool)183 gimp_perspective_clone_tool_init (GimpPerspectiveCloneTool *clone_tool)
184 {
185   GimpTool *tool = GIMP_TOOL (clone_tool);
186 
187   gimp_tool_control_set_action_object_2 (tool->control,
188                                          "context/context-pattern-select-set");
189 
190   gimp_matrix3_identity (&clone_tool->transform);
191 }
192 
193 static void
gimp_perspective_clone_tool_constructed(GObject * object)194 gimp_perspective_clone_tool_constructed (GObject *object)
195 {
196   GimpPerspectiveCloneOptions *options;
197 
198   options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (object);
199 
200   G_OBJECT_CLASS (parent_class)->constructed (object);
201 
202   if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
203     gimp_paint_tool_set_active (GIMP_PAINT_TOOL (object), FALSE);
204 }
205 
206 static gboolean
gimp_perspective_clone_tool_initialize(GimpTool * tool,GimpDisplay * display,GError ** error)207 gimp_perspective_clone_tool_initialize (GimpTool     *tool,
208                                         GimpDisplay  *display,
209                                         GError      **error)
210 {
211   GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
212 
213   if (! GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error))
214     {
215       return FALSE;
216     }
217 
218   if (display != tool->display)
219     {
220       GimpDisplayShell *shell = gimp_display_get_shell (display);
221       GimpImage        *image = gimp_display_get_image (display);
222       gint              i;
223 
224       tool->display  = display;
225       tool->drawable = gimp_image_get_active_drawable (image);
226 
227       /*  Find the transform bounds initializing */
228       gimp_perspective_clone_tool_bounds (clone_tool, display);
229 
230       gimp_perspective_clone_tool_prepare (clone_tool);
231 
232       /*  Recalculate the transform tool  */
233       gimp_perspective_clone_tool_recalc_matrix (clone_tool, NULL);
234 
235       clone_tool->widget =
236         gimp_tool_transform_grid_new (shell,
237                                       &clone_tool->transform,
238                                       clone_tool->x1,
239                                       clone_tool->y1,
240                                       clone_tool->x2,
241                                       clone_tool->y2);
242 
243       g_object_set (clone_tool->widget,
244                     "pivot-x",                 (clone_tool->x1 + clone_tool->x2) / 2.0,
245                     "pivot-y",                 (clone_tool->y1 + clone_tool->y2) / 2.0,
246                     "inside-function",         GIMP_TRANSFORM_FUNCTION_MOVE,
247                     "outside-function",        GIMP_TRANSFORM_FUNCTION_ROTATE,
248                     "use-corner-handles",      TRUE,
249                     "use-perspective-handles", TRUE,
250                     "use-side-handles",        TRUE,
251                     "use-shear-handles",       TRUE,
252                     "use-pivot-handle",        TRUE,
253                     NULL);
254 
255       g_signal_connect (clone_tool->widget, "changed",
256                         G_CALLBACK (gimp_perspective_clone_tool_widget_changed),
257                         clone_tool);
258       g_signal_connect (clone_tool->widget, "status",
259                         G_CALLBACK (gimp_perspective_clone_tool_widget_status),
260                         clone_tool);
261 
262       /*  start drawing the bounding box and handles...  */
263       if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
264         gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
265 
266       gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
267 
268       /*  Save the current transformation info  */
269       for (i = 0; i < TRANS_INFO_SIZE; i++)
270         clone_tool->old_trans_info[i] = clone_tool->trans_info[i];
271     }
272 
273   return TRUE;
274 }
275 
276 static gboolean
gimp_perspective_clone_tool_has_display(GimpTool * tool,GimpDisplay * display)277 gimp_perspective_clone_tool_has_display (GimpTool    *tool,
278                                          GimpDisplay *display)
279 {
280   GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
281 
282   return (display == clone_tool->src_display ||
283           GIMP_TOOL_CLASS (parent_class)->has_display (tool, display));
284 }
285 
286 static GimpDisplay *
gimp_perspective_clone_tool_has_image(GimpTool * tool,GimpImage * image)287 gimp_perspective_clone_tool_has_image (GimpTool  *tool,
288                                        GimpImage *image)
289 {
290   GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
291   GimpDisplay              *display;
292 
293   display = GIMP_TOOL_CLASS (parent_class)->has_image (tool, image);
294 
295   if (! display && clone_tool->src_display)
296     {
297       if (image && gimp_display_get_image (clone_tool->src_display) == image)
298         display = clone_tool->src_display;
299 
300       /*  NULL image means any display  */
301       if (! image)
302         display = clone_tool->src_display;
303     }
304 
305   return display;
306 }
307 
308 static void
gimp_perspective_clone_tool_control(GimpTool * tool,GimpToolAction action,GimpDisplay * display)309 gimp_perspective_clone_tool_control (GimpTool       *tool,
310                                      GimpToolAction  action,
311                                      GimpDisplay    *display)
312 {
313   GimpPerspectiveCloneTool *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
314 
315   switch (action)
316     {
317     case GIMP_TOOL_ACTION_PAUSE:
318     case GIMP_TOOL_ACTION_RESUME:
319       break;
320 
321     case GIMP_TOOL_ACTION_HALT:
322       gimp_perspective_clone_tool_halt (clone_tool);
323       break;
324 
325     case GIMP_TOOL_ACTION_COMMIT:
326       break;
327     }
328 
329   GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
330 }
331 
332 static void
gimp_perspective_clone_tool_button_press(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type,GimpDisplay * display)333 gimp_perspective_clone_tool_button_press (GimpTool            *tool,
334                                           const GimpCoords    *coords,
335                                           guint32              time,
336                                           GdkModifierType      state,
337                                           GimpButtonPressType  press_type,
338                                           GimpDisplay         *display)
339 {
340   GimpPaintTool               *paint_tool  = GIMP_PAINT_TOOL (tool);
341   GimpPerspectiveCloneTool    *clone_tool  = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
342   GimpPerspectiveClone        *clone       = GIMP_PERSPECTIVE_CLONE (paint_tool->core);
343   GimpSourceCore              *source_core = GIMP_SOURCE_CORE (clone);
344   GimpPerspectiveCloneOptions *options;
345 
346   options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
347 
348   if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
349     {
350       if (clone_tool->widget)
351         {
352           gimp_tool_widget_hover (clone_tool->widget, coords, state, TRUE);
353 
354           if (gimp_tool_widget_button_press (clone_tool->widget, coords,
355                                              time, state, press_type))
356             {
357               clone_tool->grab_widget = clone_tool->widget;
358             }
359         }
360 
361       gimp_tool_control_activate (tool->control);
362     }
363   else
364     {
365       GdkModifierType extend_mask = gimp_get_extend_selection_mask ();
366       GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask ();
367       gdouble         nnx, nny;
368 
369       gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
370 
371       if ((state & (toggle_mask | extend_mask)) == toggle_mask)
372         {
373           source_core->set_source = TRUE;
374 
375           clone_tool->src_display = display;
376         }
377       else
378         {
379           source_core->set_source = FALSE;
380         }
381 
382       GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, state,
383                                                     press_type, display);
384 
385       /* Set the coordinates for the reference cross */
386       gimp_perspective_clone_get_source_point (clone,
387                                                coords->x, coords->y,
388                                                &nnx, &nny);
389 
390       clone_tool->src_x = floor (nnx);
391       clone_tool->src_y = floor (nny);
392 
393       gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
394     }
395 }
396 
397 static void
gimp_perspective_clone_tool_button_release(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type,GimpDisplay * display)398 gimp_perspective_clone_tool_button_release (GimpTool              *tool,
399                                             const GimpCoords      *coords,
400                                             guint32                time,
401                                             GdkModifierType        state,
402                                             GimpButtonReleaseType  release_type,
403                                             GimpDisplay           *display)
404 {
405   GimpPerspectiveCloneTool    *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
406   GimpPerspectiveCloneOptions *options;
407 
408   options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
409 
410   switch (options->clone_mode)
411     {
412     case GIMP_PERSPECTIVE_CLONE_MODE_ADJUST:
413       gimp_tool_control_halt (tool->control);
414 
415       if (clone_tool->grab_widget)
416         {
417           gimp_tool_widget_button_release (clone_tool->grab_widget,
418                                            coords, time, state, release_type);
419           clone_tool->grab_widget = NULL;
420         }
421       break;
422 
423     case GIMP_PERSPECTIVE_CLONE_MODE_PAINT:
424       GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time, state,
425                                                       release_type, display);
426       break;
427     }
428 }
429 
430 static void
gimp_perspective_clone_tool_motion(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpDisplay * display)431 gimp_perspective_clone_tool_motion (GimpTool         *tool,
432                                     const GimpCoords *coords,
433                                     guint32           time,
434                                     GdkModifierType   state,
435                                     GimpDisplay      *display)
436 {
437   GimpPerspectiveCloneTool    *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
438   GimpPaintTool               *paint_tool = GIMP_PAINT_TOOL (tool);
439   GimpPerspectiveClone        *clone      = GIMP_PERSPECTIVE_CLONE (paint_tool->core);
440   GimpPerspectiveCloneOptions *options;
441 
442   options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
443 
444   if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
445     {
446       if (clone_tool->grab_widget)
447         {
448           gimp_tool_widget_motion (clone_tool->grab_widget, coords, time, state);
449         }
450     }
451   else if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_PAINT)
452     {
453       gdouble nnx, nny;
454 
455       gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
456 
457       GIMP_TOOL_CLASS (parent_class)->motion (tool, coords, time, state,
458                                               display);
459 
460       /* Set the coordinates for the reference cross */
461       gimp_perspective_clone_get_source_point (clone,
462                                                coords->x, coords->y,
463                                                &nnx, &nny);
464 
465       clone_tool->src_x = floor (nnx);
466       clone_tool->src_y = floor (nny);
467 
468       gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
469     }
470 }
471 
472 static void
gimp_perspective_clone_tool_modifier_key(GimpTool * tool,GdkModifierType key,gboolean press,GdkModifierType state,GimpDisplay * display)473 gimp_perspective_clone_tool_modifier_key (GimpTool        *tool,
474                                           GdkModifierType  key,
475                                           gboolean         press,
476                                           GdkModifierType  state,
477                                           GimpDisplay     *display)
478 {
479   GimpPerspectiveCloneTool    *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
480   GimpPerspectiveCloneOptions *options;
481 
482   options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
483 
484   if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_PAINT &&
485       key == gimp_get_toggle_behavior_mask ())
486     {
487       if (press)
488         {
489           clone_tool->saved_precision =
490             gimp_tool_control_get_precision (tool->control);
491           gimp_tool_control_set_precision (tool->control,
492                                            GIMP_CURSOR_PRECISION_PIXEL_CENTER);
493         }
494       else
495         {
496           gimp_tool_control_set_precision (tool->control,
497                                            clone_tool->saved_precision);
498         }
499     }
500 
501   GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press, state,
502                                                 display);
503 }
504 
505 static void
gimp_perspective_clone_tool_cursor_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display)506 gimp_perspective_clone_tool_cursor_update (GimpTool         *tool,
507                                            const GimpCoords *coords,
508                                            GdkModifierType   state,
509                                            GimpDisplay      *display)
510 {
511   GimpPerspectiveCloneTool    *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
512   GimpPerspectiveCloneOptions *options;
513   GimpImage                   *image;
514   GimpToolClass               *tool_class;
515   GimpCursorType               cursor      = GIMP_CURSOR_MOUSE;
516   GimpToolCursorType           tool_cursor = GIMP_TOOL_CURSOR_NONE;
517   GimpCursorModifier           modifier    = GIMP_CURSOR_MODIFIER_NONE;
518 
519   options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
520 
521   image = gimp_display_get_image (display);
522 
523   if (gimp_image_coords_in_active_pickable (image, coords,
524                                             FALSE, FALSE, TRUE))
525     {
526       cursor = GIMP_CURSOR_MOUSE;
527     }
528 
529   if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
530     {
531       if (clone_tool->widget)
532         {
533           if (display == tool->display)
534             {
535               gimp_tool_widget_get_cursor (clone_tool->widget,
536                                            coords, state,
537                                            &cursor, &tool_cursor, &modifier);
538             }
539         }
540     }
541   else
542     {
543       GdkModifierType extend_mask = gimp_get_extend_selection_mask ();
544       GdkModifierType toggle_mask = gimp_get_toggle_behavior_mask ();
545 
546       if ((state & (toggle_mask | extend_mask)) == toggle_mask)
547         {
548           cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
549         }
550       else if (! GIMP_SOURCE_CORE (GIMP_PAINT_TOOL (tool)->core)->src_drawable)
551         {
552           modifier = GIMP_CURSOR_MODIFIER_BAD;
553         }
554 
555       tool_cursor = GIMP_TOOL_CURSOR_CLONE;
556     }
557 
558   gimp_tool_control_set_cursor          (tool->control, cursor);
559   gimp_tool_control_set_tool_cursor     (tool->control, tool_cursor);
560   gimp_tool_control_set_cursor_modifier (tool->control, modifier);
561 
562   /*  If we are in adjust mode, skip the GimpBrushClass when chaining up.
563    *  This ensures that the cursor will be set regardless of
564    *  GimpBrushTool::show_cursor (see bug #354933).
565    */
566   if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
567     tool_class = GIMP_TOOL_CLASS (g_type_class_peek_parent (parent_class));
568   else
569     tool_class = GIMP_TOOL_CLASS (parent_class);
570 
571   tool_class->cursor_update (tool, coords, state, display);
572 }
573 
574 static void
gimp_perspective_clone_tool_oper_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,gboolean proximity,GimpDisplay * display)575 gimp_perspective_clone_tool_oper_update (GimpTool         *tool,
576                                          const GimpCoords *coords,
577                                          GdkModifierType   state,
578                                          gboolean          proximity,
579                                          GimpDisplay      *display)
580 {
581   GimpPerspectiveCloneTool    *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
582   GimpPerspectiveCloneOptions *options;
583 
584   options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
585 
586   if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
587     {
588       if (clone_tool->widget)
589         {
590           if (display == tool->display)
591             {
592               gimp_tool_widget_hover (clone_tool->widget, coords, state,
593                                       proximity);
594             }
595         }
596     }
597   else
598     {
599       GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state,
600                                                    proximity, display);
601 
602       if (proximity)
603         {
604           GimpPaintCore        *core        = GIMP_PAINT_TOOL (tool)->core;
605           GimpPerspectiveClone *clone       = GIMP_PERSPECTIVE_CLONE (core);
606           GimpSourceCore       *source_core = GIMP_SOURCE_CORE (core);
607 
608           if (source_core->src_drawable == NULL)
609             {
610               gimp_tool_replace_status (tool, display,
611                                         _("Ctrl-Click to set a clone source"));
612             }
613           else
614             {
615               gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
616 
617               clone_tool->src_x = source_core->src_x;
618               clone_tool->src_y = source_core->src_y;
619 
620               if (! source_core->first_stroke)
621                 {
622                   if (GIMP_SOURCE_OPTIONS (options)->align_mode ==
623                       GIMP_SOURCE_ALIGN_YES)
624                     {
625                       gdouble nnx, nny;
626 
627                       /* Set the coordinates for the reference cross */
628                       gimp_perspective_clone_get_source_point (clone,
629                                                                coords->x,
630                                                                coords->y,
631                                                                &nnx, &nny);
632 
633                       clone_tool->src_x = floor (nnx);
634                       clone_tool->src_y = floor (nny);
635                     }
636                 }
637 
638               gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
639             }
640         }
641     }
642 }
643 
644 static void
gimp_perspective_clone_tool_options_notify(GimpTool * tool,GimpToolOptions * options,const GParamSpec * pspec)645 gimp_perspective_clone_tool_options_notify (GimpTool         *tool,
646                                             GimpToolOptions  *options,
647                                             const GParamSpec *pspec)
648 {
649   GimpPerspectiveCloneTool    *clone_tool = GIMP_PERSPECTIVE_CLONE_TOOL (tool);
650   GimpPaintTool               *paint_tool = GIMP_PAINT_TOOL (tool);
651   GimpPerspectiveCloneOptions *clone_options;
652 
653   clone_options = GIMP_PERSPECTIVE_CLONE_OPTIONS (options);
654 
655   GIMP_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec);
656 
657   if (! strcmp (pspec->name, "clone-mode"))
658     {
659       GimpPerspectiveClone *clone;
660 
661       clone = GIMP_PERSPECTIVE_CLONE (GIMP_PAINT_TOOL (tool)->core);
662 
663       gimp_draw_tool_pause (GIMP_DRAW_TOOL (clone_tool));
664 
665       if (clone_options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_PAINT)
666         {
667           gimp_perspective_clone_set_transform (clone, &clone_tool->transform);
668 
669           gimp_paint_tool_set_active (paint_tool, TRUE);
670         }
671       else
672         {
673           gimp_paint_tool_set_active (paint_tool, FALSE);
674 
675           gimp_tool_control_set_precision (tool->control,
676                                            GIMP_CURSOR_PRECISION_SUBPIXEL);
677 
678           /*  start drawing the bounding box and handles...  */
679           if (tool->display &&
680               ! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (clone_tool)))
681             {
682               gimp_draw_tool_start (GIMP_DRAW_TOOL (clone_tool), tool->display);
683             }
684         }
685 
686       gimp_draw_tool_resume (GIMP_DRAW_TOOL (clone_tool));
687     }
688 }
689 
690 static void
gimp_perspective_clone_tool_draw(GimpDrawTool * draw_tool)691 gimp_perspective_clone_tool_draw (GimpDrawTool *draw_tool)
692 {
693   GimpTool                    *tool        = GIMP_TOOL (draw_tool);
694   GimpPerspectiveCloneTool    *clone_tool  = GIMP_PERSPECTIVE_CLONE_TOOL (draw_tool);
695   GimpPerspectiveClone        *clone       = GIMP_PERSPECTIVE_CLONE (GIMP_PAINT_TOOL (tool)->core);
696   GimpSourceCore              *source_core = GIMP_SOURCE_CORE (clone);
697   GimpPerspectiveCloneOptions *options;
698 
699   options = GIMP_PERSPECTIVE_CLONE_TOOL_GET_OPTIONS (tool);
700 
701   if (options->clone_mode == GIMP_PERSPECTIVE_CLONE_MODE_ADJUST)
702     {
703       if (clone_tool->widget)
704         {
705           GimpCanvasItem *item = gimp_tool_widget_get_item (clone_tool->widget);
706 
707           gimp_draw_tool_add_item (draw_tool, item);
708         }
709     }
710   else
711     {
712       GimpCanvasGroup *stroke_group;
713 
714       stroke_group = gimp_draw_tool_add_stroke_group (draw_tool);
715 
716       /*  draw the bounding box  */
717       gimp_draw_tool_push_group (draw_tool, stroke_group);
718 
719       gimp_draw_tool_add_line (draw_tool,
720                                clone_tool->trans_info[X0],
721                                clone_tool->trans_info[Y0],
722                                clone_tool->trans_info[X1],
723                                clone_tool->trans_info[Y1]);
724       gimp_draw_tool_add_line (draw_tool,
725                                clone_tool->trans_info[X1],
726                                clone_tool->trans_info[Y1],
727                                clone_tool->trans_info[X3],
728                                clone_tool->trans_info[Y3]);
729       gimp_draw_tool_add_line (draw_tool,
730                                clone_tool->trans_info[X2],
731                                clone_tool->trans_info[Y2],
732                                clone_tool->trans_info[X3],
733                                clone_tool->trans_info[Y3]);
734       gimp_draw_tool_add_line (draw_tool,
735                                clone_tool->trans_info[X2],
736                                clone_tool->trans_info[Y2],
737                                clone_tool->trans_info[X0],
738                                clone_tool->trans_info[Y0]);
739 
740       gimp_draw_tool_pop_group (draw_tool);
741     }
742 
743   if (source_core->src_drawable && clone_tool->src_display)
744     {
745       GimpDisplay *tmp_display;
746 
747       tmp_display = draw_tool->display;
748       draw_tool->display = clone_tool->src_display;
749 
750       gimp_draw_tool_add_handle (draw_tool,
751                                  GIMP_HANDLE_CROSS,
752                                  clone_tool->src_x + 0.5,
753                                  clone_tool->src_y + 0.5,
754                                  GIMP_TOOL_HANDLE_SIZE_CROSS,
755                                  GIMP_TOOL_HANDLE_SIZE_CROSS,
756                                  GIMP_HANDLE_ANCHOR_CENTER);
757 
758       draw_tool->display = tmp_display;
759     }
760 
761   GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
762 }
763 
764 static void
gimp_perspective_clone_tool_halt(GimpPerspectiveCloneTool * clone_tool)765 gimp_perspective_clone_tool_halt (GimpPerspectiveCloneTool *clone_tool)
766 {
767   GimpTool *tool = GIMP_TOOL (clone_tool);
768 
769   clone_tool->src_display = NULL;
770 
771   g_object_set (GIMP_PAINT_TOOL (tool)->core,
772                 "src-drawable", NULL,
773                 NULL);
774 
775   if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
776     gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
777 
778   g_clear_object (&clone_tool->widget);
779 
780   tool->display  = NULL;
781   tool->drawable = NULL;
782 }
783 
784 static void
gimp_perspective_clone_tool_bounds(GimpPerspectiveCloneTool * clone_tool,GimpDisplay * display)785 gimp_perspective_clone_tool_bounds (GimpPerspectiveCloneTool *clone_tool,
786                                     GimpDisplay              *display)
787 {
788   GimpImage *image = gimp_display_get_image (display);
789 
790   clone_tool->x1 = 0;
791   clone_tool->y1 = 0;
792   clone_tool->x2 = gimp_image_get_width  (image);
793   clone_tool->y2 = gimp_image_get_height (image);
794 }
795 
796 static void
gimp_perspective_clone_tool_prepare(GimpPerspectiveCloneTool * clone_tool)797 gimp_perspective_clone_tool_prepare (GimpPerspectiveCloneTool *clone_tool)
798 {
799   clone_tool->trans_info[PIVOT_X] = (gdouble) (clone_tool->x1 + clone_tool->x2) / 2.0;
800   clone_tool->trans_info[PIVOT_Y] = (gdouble) (clone_tool->y1 + clone_tool->y2) / 2.0;
801 
802   clone_tool->trans_info[X0] = clone_tool->x1;
803   clone_tool->trans_info[Y0] = clone_tool->y1;
804   clone_tool->trans_info[X1] = clone_tool->x2;
805   clone_tool->trans_info[Y1] = clone_tool->y1;
806   clone_tool->trans_info[X2] = clone_tool->x1;
807   clone_tool->trans_info[Y2] = clone_tool->y2;
808   clone_tool->trans_info[X3] = clone_tool->x2;
809   clone_tool->trans_info[Y3] = clone_tool->y2;
810 }
811 
812 static void
gimp_perspective_clone_tool_recalc_matrix(GimpPerspectiveCloneTool * clone_tool,GimpToolWidget * widget)813 gimp_perspective_clone_tool_recalc_matrix (GimpPerspectiveCloneTool *clone_tool,
814                                            GimpToolWidget           *widget)
815 {
816   gimp_matrix3_identity (&clone_tool->transform);
817   gimp_transform_matrix_perspective (&clone_tool->transform,
818                                      clone_tool->x1,
819                                      clone_tool->y1,
820                                      clone_tool->x2 - clone_tool->x1,
821                                      clone_tool->y2 - clone_tool->y1,
822                                      clone_tool->trans_info[X0],
823                                      clone_tool->trans_info[Y0],
824                                      clone_tool->trans_info[X1],
825                                      clone_tool->trans_info[Y1],
826                                      clone_tool->trans_info[X2],
827                                      clone_tool->trans_info[Y2],
828                                      clone_tool->trans_info[X3],
829                                      clone_tool->trans_info[Y3]);
830 
831   if (widget)
832     g_object_set (widget,
833                   "transform", &clone_tool->transform,
834                   "x1",        (gdouble) clone_tool->x1,
835                   "y1",        (gdouble) clone_tool->y1,
836                   "x2",        (gdouble) clone_tool->x2,
837                   "y2",        (gdouble) clone_tool->y2,
838                   "pivot-x",   clone_tool->trans_info[PIVOT_X],
839                   "pivot-y",   clone_tool->trans_info[PIVOT_Y],
840                   NULL);
841 }
842 
843 static void
gimp_perspective_clone_tool_widget_changed(GimpToolWidget * widget,GimpPerspectiveCloneTool * clone_tool)844 gimp_perspective_clone_tool_widget_changed (GimpToolWidget           *widget,
845                                             GimpPerspectiveCloneTool *clone_tool)
846 {
847   GimpMatrix3 *transform;
848 
849   g_object_get (widget,
850                 "transform", &transform,
851                 "pivot-x",   &clone_tool->trans_info[PIVOT_X],
852                 "pivot-y",   &clone_tool->trans_info[PIVOT_Y],
853                 NULL);
854 
855   gimp_matrix3_transform_point (transform,
856                                 clone_tool->x1, clone_tool->y1,
857                                 &clone_tool->trans_info[X0],
858                                 &clone_tool->trans_info[Y0]);
859   gimp_matrix3_transform_point (transform,
860                                 clone_tool->x2, clone_tool->y1,
861                                 &clone_tool->trans_info[X1],
862                                 &clone_tool->trans_info[Y1]);
863   gimp_matrix3_transform_point (transform,
864                                 clone_tool->x1, clone_tool->y2,
865                                 &clone_tool->trans_info[X2],
866                                 &clone_tool->trans_info[Y2]);
867   gimp_matrix3_transform_point (transform,
868                                 clone_tool->x2, clone_tool->y2,
869                                 &clone_tool->trans_info[X3],
870                                 &clone_tool->trans_info[Y3]);
871 
872   g_free (transform);
873 
874   gimp_perspective_clone_tool_recalc_matrix (clone_tool, NULL);
875 }
876 
877 static void
gimp_perspective_clone_tool_widget_status(GimpToolWidget * widget,const gchar * status,GimpPerspectiveCloneTool * clone_tool)878 gimp_perspective_clone_tool_widget_status (GimpToolWidget           *widget,
879                                            const gchar              *status,
880                                            GimpPerspectiveCloneTool *clone_tool)
881 {
882   GimpTool *tool = GIMP_TOOL (clone_tool);
883 
884   if (status)
885     {
886       gimp_tool_replace_status (tool, tool->display, "%s", status);
887     }
888   else
889     {
890       gimp_tool_pop_status (tool, tool->display);
891     }
892 }
893 
894 
895 /*  tool options stuff  */
896 
897 static GtkWidget *
gimp_perspective_clone_options_gui(GimpToolOptions * tool_options)898 gimp_perspective_clone_options_gui (GimpToolOptions *tool_options)
899 {
900   GObject   *config = G_OBJECT (tool_options);
901   GtkWidget *vbox   = gimp_clone_options_gui (tool_options);
902   GtkWidget *mode;
903 
904   /* radio buttons to set if you are modifying the perspective plane
905    * or painting
906    */
907   mode = gimp_prop_enum_radio_box_new (config, "clone-mode", 0, 0);
908   gtk_box_pack_start (GTK_BOX (vbox), mode, FALSE, FALSE, 0);
909   gtk_box_reorder_child (GTK_BOX (vbox), mode, 0);
910   gtk_widget_show (mode);
911 
912   return vbox;
913 }
914