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 <string.h>
21
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24
25 #include "libgimpmath/gimpmath.h"
26 #include "libgimpwidgets/gimpwidgets.h"
27
28 #include "tools-types.h"
29
30 #include "config/gimpdisplayconfig.h"
31 #include "config/gimpguiconfig.h"
32
33 #include "core/gimp.h"
34 #include "core/gimp-cairo.h"
35 #include "core/gimp-utils.h"
36 #include "core/gimpguide.h"
37 #include "core/gimpimage.h"
38 #include "core/gimpimage-pick-item.h"
39 #include "core/gimplayer.h"
40 #include "core/gimpimage-undo.h"
41 #include "core/gimplayermask.h"
42 #include "core/gimplayer-floating-selection.h"
43 #include "core/gimpundostack.h"
44
45 #include "widgets/gimphelp-ids.h"
46 #include "widgets/gimpwidgets-utils.h"
47
48 #include "display/gimpcanvasitem.h"
49 #include "display/gimpdisplay.h"
50 #include "display/gimpdisplayshell.h"
51 #include "display/gimpdisplayshell-appearance.h"
52 #include "display/gimpdisplayshell-selection.h"
53 #include "display/gimpdisplayshell-transform.h"
54
55 #include "gimpeditselectiontool.h"
56 #include "gimpguidetool.h"
57 #include "gimpmoveoptions.h"
58 #include "gimpmovetool.h"
59 #include "gimptoolcontrol.h"
60 #include "gimptools-utils.h"
61
62 #include "gimp-intl.h"
63
64
65 /* local function prototypes */
66
67 static void gimp_move_tool_finalize (GObject *object);
68
69 static void gimp_move_tool_button_press (GimpTool *tool,
70 const GimpCoords *coords,
71 guint32 time,
72 GdkModifierType state,
73 GimpButtonPressType press_type,
74 GimpDisplay *display);
75 static void gimp_move_tool_button_release (GimpTool *tool,
76 const GimpCoords *coords,
77 guint32 time,
78 GdkModifierType state,
79 GimpButtonReleaseType release_type,
80 GimpDisplay *display);
81 static gboolean gimp_move_tool_key_press (GimpTool *tool,
82 GdkEventKey *kevent,
83 GimpDisplay *display);
84 static void gimp_move_tool_modifier_key (GimpTool *tool,
85 GdkModifierType key,
86 gboolean press,
87 GdkModifierType state,
88 GimpDisplay *display);
89 static void gimp_move_tool_oper_update (GimpTool *tool,
90 const GimpCoords *coords,
91 GdkModifierType state,
92 gboolean proximity,
93 GimpDisplay *display);
94 static void gimp_move_tool_cursor_update (GimpTool *tool,
95 const GimpCoords *coords,
96 GdkModifierType state,
97 GimpDisplay *display);
98
99 static void gimp_move_tool_draw (GimpDrawTool *draw_tool);
100
101
G_DEFINE_TYPE(GimpMoveTool,gimp_move_tool,GIMP_TYPE_DRAW_TOOL)102 G_DEFINE_TYPE (GimpMoveTool, gimp_move_tool, GIMP_TYPE_DRAW_TOOL)
103
104 #define parent_class gimp_move_tool_parent_class
105
106
107 void
108 gimp_move_tool_register (GimpToolRegisterCallback callback,
109 gpointer data)
110 {
111 (* callback) (GIMP_TYPE_MOVE_TOOL,
112 GIMP_TYPE_MOVE_OPTIONS,
113 gimp_move_options_gui,
114 0,
115 "gimp-move-tool",
116 C_("tool", "Move"),
117 _("Move Tool: Move layers, selections, and other objects"),
118 N_("_Move"), "M",
119 NULL, GIMP_HELP_TOOL_MOVE,
120 GIMP_ICON_TOOL_MOVE,
121 data);
122 }
123
124 static void
gimp_move_tool_class_init(GimpMoveToolClass * klass)125 gimp_move_tool_class_init (GimpMoveToolClass *klass)
126 {
127 GObjectClass *object_class = G_OBJECT_CLASS (klass);
128 GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
129 GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
130
131 object_class->finalize = gimp_move_tool_finalize;
132
133 tool_class->button_press = gimp_move_tool_button_press;
134 tool_class->button_release = gimp_move_tool_button_release;
135 tool_class->key_press = gimp_move_tool_key_press;
136 tool_class->modifier_key = gimp_move_tool_modifier_key;
137 tool_class->oper_update = gimp_move_tool_oper_update;
138 tool_class->cursor_update = gimp_move_tool_cursor_update;
139
140 draw_tool_class->draw = gimp_move_tool_draw;
141 }
142
143 static void
gimp_move_tool_init(GimpMoveTool * move_tool)144 gimp_move_tool_init (GimpMoveTool *move_tool)
145 {
146 GimpTool *tool = GIMP_TOOL (move_tool);
147
148 gimp_tool_control_set_snap_to (tool->control, FALSE);
149 gimp_tool_control_set_handle_empty_image (tool->control, TRUE);
150 gimp_tool_control_set_tool_cursor (tool->control,
151 GIMP_TOOL_CURSOR_MOVE);
152
153 move_tool->floating_layer = NULL;
154 move_tool->guides = NULL;
155
156 move_tool->saved_type = GIMP_TRANSFORM_TYPE_LAYER;
157
158 move_tool->old_active_layer = NULL;
159 move_tool->old_active_vectors = NULL;
160 }
161
162 static void
gimp_move_tool_finalize(GObject * object)163 gimp_move_tool_finalize (GObject *object)
164 {
165 GimpMoveTool *move = GIMP_MOVE_TOOL (object);
166
167 g_clear_pointer (&move->guides, g_list_free);
168
169 G_OBJECT_CLASS (parent_class)->finalize (object);
170 }
171
172 static void
gimp_move_tool_button_press(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type,GimpDisplay * display)173 gimp_move_tool_button_press (GimpTool *tool,
174 const GimpCoords *coords,
175 guint32 time,
176 GdkModifierType state,
177 GimpButtonPressType press_type,
178 GimpDisplay *display)
179 {
180 GimpMoveTool *move = GIMP_MOVE_TOOL (tool);
181 GimpMoveOptions *options = GIMP_MOVE_TOOL_GET_OPTIONS (tool);
182 GimpDisplayShell *shell = gimp_display_get_shell (display);
183 GimpImage *image = gimp_display_get_image (display);
184 GimpItem *active_item = NULL;
185 GimpTranslateMode translate_mode = GIMP_TRANSLATE_MODE_MASK;
186 const gchar *null_message = NULL;
187 const gchar *locked_message = NULL;
188
189 tool->display = display;
190
191 move->floating_layer = NULL;
192
193 g_clear_pointer (&move->guides, g_list_free);
194
195 if (! options->move_current)
196 {
197 const gint snap_distance = display->config->snap_distance;
198
199 if (options->move_type == GIMP_TRANSFORM_TYPE_PATH)
200 {
201 GimpVectors *vectors;
202
203 vectors = gimp_image_pick_vectors (image,
204 coords->x, coords->y,
205 FUNSCALEX (shell, snap_distance),
206 FUNSCALEY (shell, snap_distance));
207 if (vectors)
208 {
209 move->old_active_vectors =
210 gimp_image_get_active_vectors (image);
211
212 gimp_image_set_active_vectors (image, vectors);
213 }
214 else
215 {
216 /* no path picked */
217 return;
218 }
219 }
220 else if (options->move_type == GIMP_TRANSFORM_TYPE_LAYER)
221 {
222 GList *guides;
223 GimpLayer *layer;
224
225 if (gimp_display_shell_get_show_guides (shell) &&
226 (guides = gimp_image_pick_guides (image,
227 coords->x, coords->y,
228 FUNSCALEX (shell, snap_distance),
229 FUNSCALEY (shell, snap_distance))))
230 {
231 move->guides = guides;
232
233 gimp_guide_tool_start_edit_many (tool, display, guides);
234
235 return;
236 }
237 else if ((layer = gimp_image_pick_layer (image,
238 coords->x,
239 coords->y,
240 NULL)))
241 {
242 if (gimp_image_get_floating_selection (image) &&
243 ! gimp_layer_is_floating_sel (layer))
244 {
245 /* If there is a floating selection, and this aint it,
246 * use the move tool to anchor it.
247 */
248 move->floating_layer =
249 gimp_image_get_floating_selection (image);
250
251 gimp_tool_control_activate (tool->control);
252
253 return;
254 }
255 else
256 {
257 move->old_active_layer = gimp_image_get_active_layer (image);
258
259 gimp_image_set_active_layer (image, layer);
260 }
261 }
262 else
263 {
264 /* no guide and no layer picked */
265
266 return;
267 }
268 }
269 }
270
271 switch (options->move_type)
272 {
273 case GIMP_TRANSFORM_TYPE_PATH:
274 {
275 active_item = GIMP_ITEM (gimp_image_get_active_vectors (image));
276
277 translate_mode = GIMP_TRANSLATE_MODE_VECTORS;
278
279 if (! active_item)
280 {
281 null_message = _("There is no path to move.");
282 }
283 else if (gimp_item_is_position_locked (active_item))
284 {
285 locked_message = _("The active path's position is locked.");
286 }
287 }
288 break;
289
290 case GIMP_TRANSFORM_TYPE_SELECTION:
291 {
292 active_item = GIMP_ITEM (gimp_image_get_mask (image));
293
294 if (gimp_channel_is_empty (GIMP_CHANNEL (active_item)))
295 active_item = NULL;
296
297 translate_mode = GIMP_TRANSLATE_MODE_MASK;
298
299 if (! active_item)
300 {
301 /* cannot happen, don't translate this message */
302 null_message = "There is no selection to move.";
303 }
304 else if (gimp_item_is_position_locked (active_item))
305 {
306 locked_message = "The selection's position is locked.";
307 }
308 }
309 break;
310
311 case GIMP_TRANSFORM_TYPE_LAYER:
312 {
313 active_item = GIMP_ITEM (gimp_image_get_active_drawable (image));
314
315 if (! active_item)
316 {
317 null_message = _("There is no layer to move.");
318 }
319 else if (GIMP_IS_LAYER_MASK (active_item))
320 {
321 translate_mode = GIMP_TRANSLATE_MODE_LAYER_MASK;
322
323 if (gimp_item_is_position_locked (active_item))
324 locked_message = _("The active layer's position is locked.");
325 else if (gimp_item_is_content_locked (active_item))
326 locked_message = _("The active layer's pixels are locked.");
327 }
328 else if (GIMP_IS_CHANNEL (active_item))
329 {
330 translate_mode = GIMP_TRANSLATE_MODE_CHANNEL;
331
332 if (gimp_item_is_position_locked (active_item))
333 locked_message = _("The active channel's position is locked.");
334 else if (gimp_item_is_content_locked (active_item))
335 locked_message = _("The active channel's pixels are locked.");
336 }
337 else
338 {
339 translate_mode = GIMP_TRANSLATE_MODE_LAYER;
340
341 if (gimp_item_is_position_locked (active_item))
342 locked_message = _("The active layer's position is locked.");
343 }
344 }
345 break;
346
347 case GIMP_TRANSFORM_TYPE_IMAGE:
348 g_return_if_reached ();
349 }
350
351 if (! active_item)
352 {
353 gimp_tool_message_literal (tool, display, null_message);
354 gimp_widget_blink (options->type_box);
355 gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
356 return;
357 }
358 else if (locked_message)
359 {
360 gimp_tool_message_literal (tool, display, locked_message);
361 gimp_tools_blink_lock_box (display->gimp, active_item);
362 gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
363 return;
364 }
365
366 gimp_tool_control_activate (tool->control);
367
368 gimp_edit_selection_tool_start (tool, display, coords,
369 translate_mode,
370 TRUE);
371 }
372
373 static void
gimp_move_tool_button_release(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type,GimpDisplay * display)374 gimp_move_tool_button_release (GimpTool *tool,
375 const GimpCoords *coords,
376 guint32 time,
377 GdkModifierType state,
378 GimpButtonReleaseType release_type,
379 GimpDisplay *display)
380 {
381 GimpMoveTool *move = GIMP_MOVE_TOOL (tool);
382 GimpGuiConfig *config = GIMP_GUI_CONFIG (display->gimp->config);
383 GimpImage *image = gimp_display_get_image (display);
384 gboolean flush = FALSE;
385
386 gimp_tool_control_halt (tool->control);
387
388 if (! config->move_tool_changes_active ||
389 (release_type == GIMP_BUTTON_RELEASE_CANCEL))
390 {
391 if (move->old_active_layer)
392 {
393 gimp_image_set_active_layer (image, move->old_active_layer);
394 move->old_active_layer = NULL;
395
396 flush = TRUE;
397 }
398
399 if (move->old_active_vectors)
400 {
401 gimp_image_set_active_vectors (image, move->old_active_vectors);
402 move->old_active_vectors = NULL;
403
404 flush = TRUE;
405 }
406 }
407
408 if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
409 {
410 if (move->floating_layer)
411 {
412 floating_sel_anchor (move->floating_layer);
413
414 flush = TRUE;
415 }
416 }
417
418 if (flush)
419 gimp_image_flush (image);
420 }
421
422 static gboolean
gimp_move_tool_key_press(GimpTool * tool,GdkEventKey * kevent,GimpDisplay * display)423 gimp_move_tool_key_press (GimpTool *tool,
424 GdkEventKey *kevent,
425 GimpDisplay *display)
426 {
427 GimpMoveOptions *options = GIMP_MOVE_TOOL_GET_OPTIONS (tool);
428
429 return gimp_edit_selection_tool_translate (tool, kevent,
430 options->move_type,
431 display,
432 options->type_box);
433 }
434
435 static void
gimp_move_tool_modifier_key(GimpTool * tool,GdkModifierType key,gboolean press,GdkModifierType state,GimpDisplay * display)436 gimp_move_tool_modifier_key (GimpTool *tool,
437 GdkModifierType key,
438 gboolean press,
439 GdkModifierType state,
440 GimpDisplay *display)
441 {
442 GimpMoveTool *move = GIMP_MOVE_TOOL (tool);
443 GimpMoveOptions *options = GIMP_MOVE_TOOL_GET_OPTIONS (tool);
444
445 if (key == gimp_get_extend_selection_mask ())
446 {
447 g_object_set (options, "move-current", ! options->move_current, NULL);
448 }
449 else if (key == GDK_MOD1_MASK ||
450 key == gimp_get_toggle_behavior_mask ())
451 {
452 GimpTransformType button_type;
453
454 button_type = options->move_type;
455
456 if (press)
457 {
458 if (key == (state & (GDK_MOD1_MASK |
459 gimp_get_toggle_behavior_mask ())))
460 {
461 /* first modifier pressed */
462
463 move->saved_type = options->move_type;
464 }
465 }
466 else
467 {
468 if (! (state & (GDK_MOD1_MASK |
469 gimp_get_toggle_behavior_mask ())))
470 {
471 /* last modifier released */
472
473 button_type = move->saved_type;
474 }
475 }
476
477 if (state & GDK_MOD1_MASK)
478 {
479 button_type = GIMP_TRANSFORM_TYPE_SELECTION;
480 }
481 else if (state & gimp_get_toggle_behavior_mask ())
482 {
483 button_type = GIMP_TRANSFORM_TYPE_PATH;
484 }
485
486 if (button_type != options->move_type)
487 {
488 g_object_set (options, "move-type", button_type, NULL);
489 }
490 }
491 }
492
493 static void
gimp_move_tool_oper_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,gboolean proximity,GimpDisplay * display)494 gimp_move_tool_oper_update (GimpTool *tool,
495 const GimpCoords *coords,
496 GdkModifierType state,
497 gboolean proximity,
498 GimpDisplay *display)
499 {
500 GimpMoveTool *move = GIMP_MOVE_TOOL (tool);
501 GimpMoveOptions *options = GIMP_MOVE_TOOL_GET_OPTIONS (tool);
502 GimpDisplayShell *shell = gimp_display_get_shell (display);
503 GimpImage *image = gimp_display_get_image (display);
504 GList *guides = NULL;
505
506 if (options->move_type == GIMP_TRANSFORM_TYPE_LAYER &&
507 ! options->move_current &&
508 gimp_display_shell_get_show_guides (shell) &&
509 proximity)
510 {
511 gint snap_distance = display->config->snap_distance;
512
513 guides = gimp_image_pick_guides (image, coords->x, coords->y,
514 FUNSCALEX (shell, snap_distance),
515 FUNSCALEY (shell, snap_distance));
516 }
517
518 if (gimp_g_list_compare (guides, move->guides))
519 {
520 GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool);
521
522 gimp_draw_tool_pause (draw_tool);
523
524 if (gimp_draw_tool_is_active (draw_tool) &&
525 draw_tool->display != display)
526 gimp_draw_tool_stop (draw_tool);
527
528 g_clear_pointer (&move->guides, g_list_free);
529
530 move->guides = guides;
531
532 if (! gimp_draw_tool_is_active (draw_tool))
533 gimp_draw_tool_start (draw_tool, display);
534
535 gimp_draw_tool_resume (draw_tool);
536 }
537 else
538 {
539 g_list_free (guides);
540 }
541 }
542
543 static void
gimp_move_tool_cursor_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display)544 gimp_move_tool_cursor_update (GimpTool *tool,
545 const GimpCoords *coords,
546 GdkModifierType state,
547 GimpDisplay *display)
548 {
549 GimpMoveOptions *options = GIMP_MOVE_TOOL_GET_OPTIONS (tool);
550 GimpDisplayShell *shell = gimp_display_get_shell (display);
551 GimpImage *image = gimp_display_get_image (display);
552 GimpCursorType cursor = GIMP_CURSOR_MOUSE;
553 GimpToolCursorType tool_cursor = GIMP_TOOL_CURSOR_MOVE;
554 GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE;
555 gint snap_distance = display->config->snap_distance;
556
557 if (options->move_type == GIMP_TRANSFORM_TYPE_PATH)
558 {
559 tool_cursor = GIMP_TOOL_CURSOR_PATHS;
560 modifier = GIMP_CURSOR_MODIFIER_MOVE;
561
562 if (options->move_current)
563 {
564 GimpItem *item = GIMP_ITEM (gimp_image_get_active_vectors (image));
565
566 if (! item || gimp_item_is_position_locked (item))
567 modifier = GIMP_CURSOR_MODIFIER_BAD;
568 }
569 else
570 {
571 if (gimp_image_pick_vectors (image,
572 coords->x, coords->y,
573 FUNSCALEX (shell, snap_distance),
574 FUNSCALEY (shell, snap_distance)))
575 {
576 tool_cursor = GIMP_TOOL_CURSOR_HAND;
577 }
578 else
579 {
580 modifier = GIMP_CURSOR_MODIFIER_BAD;
581 }
582 }
583 }
584 else if (options->move_type == GIMP_TRANSFORM_TYPE_SELECTION)
585 {
586 tool_cursor = GIMP_TOOL_CURSOR_RECT_SELECT;
587 modifier = GIMP_CURSOR_MODIFIER_MOVE;
588
589 if (gimp_channel_is_empty (gimp_image_get_mask (image)))
590 modifier = GIMP_CURSOR_MODIFIER_BAD;
591 }
592 else if (options->move_current)
593 {
594 GimpItem *item = GIMP_ITEM (gimp_image_get_active_drawable (image));
595
596 if (! item || gimp_item_is_position_locked (item))
597 modifier = GIMP_CURSOR_MODIFIER_BAD;
598 }
599 else
600 {
601 GimpLayer *layer;
602
603 if (gimp_display_shell_get_show_guides (shell) &&
604 gimp_image_pick_guide (image, coords->x, coords->y,
605 FUNSCALEX (shell, snap_distance),
606 FUNSCALEY (shell, snap_distance)))
607 {
608 tool_cursor = GIMP_TOOL_CURSOR_HAND;
609 modifier = GIMP_CURSOR_MODIFIER_MOVE;
610 }
611 else if ((layer = gimp_image_pick_layer (image,
612 coords->x, coords->y,
613 NULL)))
614 {
615 /* if there is a floating selection, and this aint it... */
616 if (gimp_image_get_floating_selection (image) &&
617 ! gimp_layer_is_floating_sel (layer))
618 {
619 tool_cursor = GIMP_TOOL_CURSOR_MOVE;
620 modifier = GIMP_CURSOR_MODIFIER_ANCHOR;
621 }
622 else if (gimp_item_is_position_locked (GIMP_ITEM (layer)))
623 {
624 modifier = GIMP_CURSOR_MODIFIER_BAD;
625 }
626 else if (layer != gimp_image_get_active_layer (image))
627 {
628 tool_cursor = GIMP_TOOL_CURSOR_HAND;
629 modifier = GIMP_CURSOR_MODIFIER_MOVE;
630 }
631 }
632 else
633 {
634 modifier = GIMP_CURSOR_MODIFIER_BAD;
635 }
636 }
637
638 gimp_tool_control_set_cursor (tool->control, cursor);
639 gimp_tool_control_set_tool_cursor (tool->control, tool_cursor);
640 gimp_tool_control_set_cursor_modifier (tool->control, modifier);
641
642 GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
643 }
644
645 static void
gimp_move_tool_draw(GimpDrawTool * draw_tool)646 gimp_move_tool_draw (GimpDrawTool *draw_tool)
647 {
648 GimpMoveTool *move = GIMP_MOVE_TOOL (draw_tool);
649 GList *iter;
650
651 for (iter = move->guides; iter; iter = g_list_next (iter))
652 {
653 GimpGuide *guide = iter->data;
654 GimpCanvasItem *item;
655 GimpGuideStyle style;
656
657 style = gimp_guide_get_style (guide);
658
659 item = gimp_draw_tool_add_guide (draw_tool,
660 gimp_guide_get_orientation (guide),
661 gimp_guide_get_position (guide),
662 style);
663 gimp_canvas_item_set_highlight (item, TRUE);
664 }
665 }
666