summaryrefslogtreecommitdiffstats
path: root/app/tools/gimpselectiontool.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--app/tools/gimpselectiontool.c830
1 files changed, 830 insertions, 0 deletions
diff --git a/app/tools/gimpselectiontool.c b/app/tools/gimpselectiontool.c
new file mode 100644
index 0000000..c1cec9c
--- /dev/null
+++ b/app/tools/gimpselectiontool.c
@@ -0,0 +1,830 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "tools-types.h"
+
+#include "core/gimpchannel.h"
+#include "core/gimperror.h"
+#include "core/gimpimage.h"
+#include "core/gimpimage-pick-item.h"
+#include "core/gimpimage-undo.h"
+#include "core/gimppickable.h"
+#include "core/gimpundostack.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimpdisplayshell-appearance.h"
+
+#include "widgets/gimpwidgets-utils.h"
+
+#include "gimpeditselectiontool.h"
+#include "gimpselectiontool.h"
+#include "gimpselectionoptions.h"
+#include "gimptoolcontrol.h"
+#include "gimptools-utils.h"
+
+#include "gimp-intl.h"
+
+
+static void gimp_selection_tool_control (GimpTool *tool,
+ GimpToolAction action,
+ GimpDisplay *display);
+static void gimp_selection_tool_modifier_key (GimpTool *tool,
+ GdkModifierType key,
+ gboolean press,
+ GdkModifierType state,
+ GimpDisplay *display);
+static void gimp_selection_tool_oper_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ gboolean proximity,
+ GimpDisplay *display);
+static void gimp_selection_tool_cursor_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display);
+
+static gboolean gimp_selection_tool_real_have_selection (GimpSelectionTool *sel_tool,
+ GimpDisplay *display);
+
+static void gimp_selection_tool_commit (GimpSelectionTool *sel_tool);
+static void gimp_selection_tool_halt (GimpSelectionTool *sel_tool,
+ GimpDisplay *display);
+
+static gboolean gimp_selection_tool_check (GimpSelectionTool *sel_tool,
+ GimpDisplay *display,
+ GError **error);
+
+static gboolean gimp_selection_tool_have_selection (GimpSelectionTool *sel_tool,
+ GimpDisplay *display);
+
+static void gimp_selection_tool_set_undo_ptr (GimpUndo **undo_ptr,
+ GimpUndo *undo);
+
+
+G_DEFINE_TYPE (GimpSelectionTool, gimp_selection_tool, GIMP_TYPE_DRAW_TOOL)
+
+#define parent_class gimp_selection_tool_parent_class
+
+
+static void
+gimp_selection_tool_class_init (GimpSelectionToolClass *klass)
+{
+ GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
+
+ tool_class->control = gimp_selection_tool_control;
+ tool_class->modifier_key = gimp_selection_tool_modifier_key;
+ tool_class->key_press = gimp_edit_selection_tool_key_press;
+ tool_class->oper_update = gimp_selection_tool_oper_update;
+ tool_class->cursor_update = gimp_selection_tool_cursor_update;
+
+ klass->have_selection = gimp_selection_tool_real_have_selection;
+}
+
+static void
+gimp_selection_tool_init (GimpSelectionTool *selection_tool)
+{
+ selection_tool->function = SELECTION_SELECT;
+ selection_tool->saved_operation = GIMP_CHANNEL_OP_REPLACE;
+
+ selection_tool->saved_show_selection = FALSE;
+ selection_tool->undo = NULL;
+ selection_tool->redo = NULL;
+ selection_tool->idle_id = 0;
+
+ selection_tool->allow_move = TRUE;
+}
+
+static void
+gimp_selection_tool_control (GimpTool *tool,
+ GimpToolAction action,
+ GimpDisplay *display)
+{
+ GimpSelectionTool *selection_tool = GIMP_SELECTION_TOOL (tool);
+
+ switch (action)
+ {
+ case GIMP_TOOL_ACTION_PAUSE:
+ case GIMP_TOOL_ACTION_RESUME:
+ break;
+
+ case GIMP_TOOL_ACTION_HALT:
+ gimp_selection_tool_halt (selection_tool, display);
+ break;
+
+ case GIMP_TOOL_ACTION_COMMIT:
+ gimp_selection_tool_commit (selection_tool);
+ break;
+ }
+
+ GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
+}
+
+static void
+gimp_selection_tool_modifier_key (GimpTool *tool,
+ GdkModifierType key,
+ gboolean press,
+ GdkModifierType state,
+ GimpDisplay *display)
+{
+ GimpSelectionTool *selection_tool = GIMP_SELECTION_TOOL (tool);
+ GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
+ GdkModifierType extend_mask;
+ GdkModifierType modify_mask;
+
+ extend_mask = gimp_get_extend_selection_mask ();
+ modify_mask = gimp_get_modify_selection_mask ();
+
+ if (key == extend_mask ||
+ key == modify_mask ||
+ key == GDK_MOD1_MASK)
+ {
+ GimpChannelOps button_op = options->operation;
+
+ state &= extend_mask |
+ modify_mask |
+ GDK_MOD1_MASK;
+
+ if (press)
+ {
+ if (key == state ||
+ /* GimpPolygonSelectTool may mask-out part of the state, which
+ * can cause the wrong mode to be restored on release if we don't
+ * init saved_operation here.
+ *
+ * see issue #4992.
+ */
+ ! state)
+ {
+ /* first modifier pressed */
+
+ selection_tool->saved_operation = options->operation;
+ }
+ }
+ else
+ {
+ if (! state)
+ {
+ /* last modifier released */
+
+ button_op = selection_tool->saved_operation;
+ }
+ }
+
+ if (state & GDK_MOD1_MASK)
+ {
+ /* if alt is down, pretend that neither
+ * shift nor control are down
+ */
+ button_op = selection_tool->saved_operation;
+ }
+ else if (state & (extend_mask |
+ modify_mask))
+ {
+ /* else get the operation from the modifier state, but only
+ * if there is actually a modifier pressed, so we don't
+ * override the "last modifier released" assignment above
+ */
+ button_op = gimp_modifiers_to_channel_op (state);
+ }
+
+ if (button_op != options->operation)
+ {
+ g_object_set (options, "operation", button_op, NULL);
+ }
+ }
+}
+
+static void
+gimp_selection_tool_oper_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ gboolean proximity,
+ GimpDisplay *display)
+{
+ GimpSelectionTool *selection_tool = GIMP_SELECTION_TOOL (tool);
+ GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
+ GimpImage *image;
+ GimpDrawable *drawable;
+ GimpLayer *layer;
+ GimpLayer *floating_sel;
+ GdkModifierType extend_mask;
+ GdkModifierType modify_mask;
+ gboolean have_selection;
+ gboolean move_layer = FALSE;
+ gboolean move_floating_sel = FALSE;
+
+ image = gimp_display_get_image (display);
+ drawable = gimp_image_get_active_drawable (image);
+ layer = gimp_image_pick_layer (image, coords->x, coords->y, NULL);
+ floating_sel = gimp_image_get_floating_selection (image);
+
+ extend_mask = gimp_get_extend_selection_mask ();
+ modify_mask = gimp_get_modify_selection_mask ();
+
+ have_selection = gimp_selection_tool_have_selection (selection_tool, display);
+
+ if (drawable)
+ {
+ if (floating_sel)
+ {
+ if (layer == floating_sel)
+ move_floating_sel = TRUE;
+ }
+ else if (have_selection &&
+ gimp_item_mask_intersect (GIMP_ITEM (drawable),
+ NULL, NULL, NULL, NULL))
+ {
+ move_layer = TRUE;
+ }
+ }
+
+ selection_tool->function = SELECTION_SELECT;
+
+ if (selection_tool->allow_move &&
+ (state & GDK_MOD1_MASK) && (state & modify_mask) && move_layer)
+ {
+ /* move the selection */
+ selection_tool->function = SELECTION_MOVE;
+ }
+ else if (selection_tool->allow_move &&
+ (state & GDK_MOD1_MASK) && (state & extend_mask) && move_layer)
+ {
+ /* move a copy of the selection */
+ selection_tool->function = SELECTION_MOVE_COPY;
+ }
+ else if (selection_tool->allow_move &&
+ (state & GDK_MOD1_MASK) && have_selection)
+ {
+ /* move the selection mask */
+ selection_tool->function = SELECTION_MOVE_MASK;
+ }
+ else if (selection_tool->allow_move &&
+ ! (state & (extend_mask | modify_mask)) &&
+ move_floating_sel)
+ {
+ /* move the selection */
+ selection_tool->function = SELECTION_MOVE;
+ }
+ else if ((state & modify_mask) || (state & extend_mask))
+ {
+ /* select */
+ selection_tool->function = SELECTION_SELECT;
+ }
+ else if (floating_sel)
+ {
+ /* anchor the selection */
+ selection_tool->function = SELECTION_ANCHOR;
+ }
+
+ gimp_tool_pop_status (tool, display);
+
+ if (proximity)
+ {
+ const gchar *status = NULL;
+ gboolean free_status = FALSE;
+ GdkModifierType modifiers = (extend_mask | modify_mask);
+
+ if (have_selection)
+ modifiers |= GDK_MOD1_MASK;
+
+ switch (selection_tool->function)
+ {
+ case SELECTION_SELECT:
+ switch (options->operation)
+ {
+ case GIMP_CHANNEL_OP_REPLACE:
+ if (have_selection)
+ {
+ status = gimp_suggest_modifiers (_("Click-Drag to replace the "
+ "current selection"),
+ modifiers & ~state,
+ NULL, NULL, NULL);
+ free_status = TRUE;
+ }
+ else
+ {
+ status = _("Click-Drag to create a new selection");
+ }
+ break;
+
+ case GIMP_CHANNEL_OP_ADD:
+ status = gimp_suggest_modifiers (_("Click-Drag to add to the "
+ "current selection"),
+ modifiers
+ & ~(state | extend_mask),
+ NULL, NULL, NULL);
+ free_status = TRUE;
+ break;
+
+ case GIMP_CHANNEL_OP_SUBTRACT:
+ status = gimp_suggest_modifiers (_("Click-Drag to subtract from the "
+ "current selection"),
+ modifiers
+ & ~(state | modify_mask),
+ NULL, NULL, NULL);
+ free_status = TRUE;
+ break;
+
+ case GIMP_CHANNEL_OP_INTERSECT:
+ status = gimp_suggest_modifiers (_("Click-Drag to intersect with "
+ "the current selection"),
+ modifiers & ~state,
+ NULL, NULL, NULL);
+ free_status = TRUE;
+ break;
+ }
+ break;
+
+ case SELECTION_MOVE_MASK:
+ status = gimp_suggest_modifiers (_("Click-Drag to move the "
+ "selection mask"),
+ modifiers & ~state,
+ NULL, NULL, NULL);
+ free_status = TRUE;
+ break;
+
+ case SELECTION_MOVE:
+ status = _("Click-Drag to move the selected pixels");
+ break;
+
+ case SELECTION_MOVE_COPY:
+ status = _("Click-Drag to move a copy of the selected pixels");
+ break;
+
+ case SELECTION_ANCHOR:
+ status = _("Click to anchor the floating selection");
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ if (status)
+ gimp_tool_push_status (tool, display, "%s", status);
+
+ if (free_status)
+ g_free ((gchar *) status);
+ }
+}
+
+static void
+gimp_selection_tool_cursor_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display)
+{
+ GimpSelectionTool *selection_tool = GIMP_SELECTION_TOOL (tool);
+ GimpSelectionOptions *options;
+ GimpToolCursorType tool_cursor;
+ GimpCursorModifier modifier;
+
+ options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool);
+
+ tool_cursor = gimp_tool_control_get_tool_cursor (tool->control);
+ modifier = GIMP_CURSOR_MODIFIER_NONE;
+
+ switch (selection_tool->function)
+ {
+ case SELECTION_SELECT:
+ switch (options->operation)
+ {
+ case GIMP_CHANNEL_OP_REPLACE:
+ break;
+ case GIMP_CHANNEL_OP_ADD:
+ modifier = GIMP_CURSOR_MODIFIER_PLUS;
+ break;
+ case GIMP_CHANNEL_OP_SUBTRACT:
+ modifier = GIMP_CURSOR_MODIFIER_MINUS;
+ break;
+ case GIMP_CHANNEL_OP_INTERSECT:
+ modifier = GIMP_CURSOR_MODIFIER_INTERSECT;
+ break;
+ }
+ break;
+
+ case SELECTION_MOVE_MASK:
+ modifier = GIMP_CURSOR_MODIFIER_MOVE;
+ break;
+
+ case SELECTION_MOVE:
+ case SELECTION_MOVE_COPY:
+ tool_cursor = GIMP_TOOL_CURSOR_MOVE;
+ break;
+
+ case SELECTION_ANCHOR:
+ modifier = GIMP_CURSOR_MODIFIER_ANCHOR;
+ break;
+ }
+
+ /* our subclass might have set a BAD modifier, in which case we leave it
+ * there, since it's more important than what we have to say.
+ */
+ if (gimp_tool_control_get_cursor_modifier (tool->control) ==
+ GIMP_CURSOR_MODIFIER_BAD ||
+ ! gimp_selection_tool_check (selection_tool, display, NULL))
+ {
+ modifier = GIMP_CURSOR_MODIFIER_BAD;
+ }
+
+ gimp_tool_set_cursor (tool, display,
+ gimp_tool_control_get_cursor (tool->control),
+ tool_cursor,
+ modifier);
+}
+
+static gboolean
+gimp_selection_tool_real_have_selection (GimpSelectionTool *sel_tool,
+ GimpDisplay *display)
+{
+ GimpImage *image = gimp_display_get_image (display);
+ GimpChannel *selection = gimp_image_get_mask (image);
+
+ return ! gimp_channel_is_empty (selection);
+}
+
+static void
+gimp_selection_tool_commit (GimpSelectionTool *sel_tool)
+{
+ /* make sure gimp_selection_tool_halt() doesn't undo the change, if any */
+ gimp_selection_tool_set_undo_ptr (&sel_tool->undo, NULL);
+}
+
+static void
+gimp_selection_tool_halt (GimpSelectionTool *sel_tool,
+ GimpDisplay *display)
+{
+ g_warn_if_fail (sel_tool->change_count == 0);
+
+ if (display)
+ {
+ GimpTool *tool = GIMP_TOOL (sel_tool);
+ GimpImage *image = gimp_display_get_image (display);
+ GimpUndoStack *undo_stack = gimp_image_get_undo_stack (image);
+ GimpUndo *undo = gimp_undo_stack_peek (undo_stack);
+
+ /* if we have an existing selection in the current display, then
+ * we have already "executed", and need to undo at this point,
+ * unless the user has done something in the meantime
+ */
+ if (undo && sel_tool->undo == undo)
+ {
+ /* prevent this change from halting the tool */
+ gimp_tool_control_push_preserve (tool->control, TRUE);
+
+ gimp_image_undo (image);
+ gimp_image_flush (image);
+
+ gimp_tool_control_pop_preserve (tool->control);
+ }
+
+ /* reset the automatic undo/redo mechanism */
+ gimp_selection_tool_set_undo_ptr (&sel_tool->undo, NULL);
+ gimp_selection_tool_set_undo_ptr (&sel_tool->redo, NULL);
+ }
+}
+
+static gboolean
+gimp_selection_tool_check (GimpSelectionTool *sel_tool,
+ GimpDisplay *display,
+ GError **error)
+{
+ GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (sel_tool);
+ GimpImage *image = gimp_display_get_image (display);
+ GimpDrawable *drawable = gimp_image_get_active_drawable (image);
+
+ switch (sel_tool->function)
+ {
+ case SELECTION_SELECT:
+ switch (options->operation)
+ {
+ case GIMP_CHANNEL_OP_ADD:
+ case GIMP_CHANNEL_OP_REPLACE:
+ break;
+
+ case GIMP_CHANNEL_OP_SUBTRACT:
+ if (! gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
+ NULL, NULL, NULL, NULL))
+ {
+ g_set_error (error, GIMP_ERROR, GIMP_FAILED,
+ _("Cannot subtract from an empty selection."));
+
+ return FALSE;
+ }
+ break;
+
+ case GIMP_CHANNEL_OP_INTERSECT:
+ if (! gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
+ NULL, NULL, NULL, NULL))
+ {
+ g_set_error (error, GIMP_ERROR, GIMP_FAILED,
+ _("Cannot intersect with an empty selection."));
+
+ return FALSE;
+ }
+ break;
+ }
+ break;
+
+ case SELECTION_MOVE:
+ case SELECTION_MOVE_COPY:
+ if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
+ {
+ g_set_error (error, GIMP_ERROR, GIMP_FAILED,
+ _("Cannot modify the pixels of layer groups."));
+
+ return FALSE;
+ }
+ else if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
+ {
+ g_set_error (error, GIMP_ERROR, GIMP_FAILED,
+ _("The active layer's pixels are locked."));
+
+ if (error)
+ gimp_tools_blink_lock_box (display->gimp, GIMP_ITEM (drawable));
+
+ return FALSE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_selection_tool_have_selection (GimpSelectionTool *sel_tool,
+ GimpDisplay *display)
+{
+ return GIMP_SELECTION_TOOL_GET_CLASS (sel_tool)->have_selection (sel_tool,
+ display);
+}
+
+static void
+gimp_selection_tool_set_undo_ptr (GimpUndo **undo_ptr,
+ GimpUndo *undo)
+{
+ if (*undo_ptr)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (*undo_ptr),
+ (gpointer *) undo_ptr);
+ }
+
+ *undo_ptr = undo;
+
+ if (*undo_ptr)
+ {
+ g_object_add_weak_pointer (G_OBJECT (*undo_ptr),
+ (gpointer *) undo_ptr);
+ }
+}
+
+
+/* public functions */
+
+gboolean
+gimp_selection_tool_start_edit (GimpSelectionTool *sel_tool,
+ GimpDisplay *display,
+ const GimpCoords *coords)
+{
+ GimpTool *tool;
+ GimpSelectionOptions *options;
+ GError *error = NULL;
+
+ g_return_val_if_fail (GIMP_IS_SELECTION_TOOL (sel_tool), FALSE);
+ g_return_val_if_fail (GIMP_IS_DISPLAY (display), FALSE);
+ g_return_val_if_fail (coords != NULL, FALSE);
+
+ tool = GIMP_TOOL (sel_tool);
+ options = GIMP_SELECTION_TOOL_GET_OPTIONS (sel_tool);
+
+ g_return_val_if_fail (gimp_tool_control_is_active (tool->control) == FALSE,
+ FALSE);
+
+ if (! gimp_selection_tool_check (sel_tool, display, &error))
+ {
+ gimp_tool_message_literal (tool, display, error->message);
+
+ gimp_widget_blink (options->mode_box);
+
+ g_clear_error (&error);
+
+ return TRUE;
+ }
+
+ switch (sel_tool->function)
+ {
+ case SELECTION_MOVE_MASK:
+ gimp_edit_selection_tool_start (tool, display, coords,
+ GIMP_TRANSLATE_MODE_MASK, FALSE);
+ return TRUE;
+
+ case SELECTION_MOVE:
+ case SELECTION_MOVE_COPY:
+ {
+ GimpTranslateMode edit_mode;
+
+ gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display);
+
+ if (sel_tool->function == SELECTION_MOVE)
+ edit_mode = GIMP_TRANSLATE_MODE_MASK_TO_LAYER;
+ else
+ edit_mode = GIMP_TRANSLATE_MODE_MASK_COPY_TO_LAYER;
+
+ gimp_edit_selection_tool_start (tool, display, coords,
+ edit_mode, FALSE);
+
+ return TRUE;
+ }
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_selection_tool_idle (GimpSelectionTool *sel_tool)
+{
+ GimpTool *tool = GIMP_TOOL (sel_tool);
+ GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
+
+ gimp_display_shell_set_show_selection (shell, FALSE);
+
+ sel_tool->idle_id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+void
+gimp_selection_tool_start_change (GimpSelectionTool *sel_tool,
+ gboolean create,
+ GimpChannelOps operation)
+{
+ GimpTool *tool;
+ GimpDisplayShell *shell;
+ GimpImage *image;
+ GimpUndoStack *undo_stack;
+
+ g_return_if_fail (GIMP_IS_SELECTION_TOOL (sel_tool));
+
+ tool = GIMP_TOOL (sel_tool);
+
+ g_return_if_fail (tool->display != NULL);
+
+ if (sel_tool->change_count++ > 0)
+ return;
+
+ shell = gimp_display_get_shell (tool->display);
+ image = gimp_display_get_image (tool->display);
+ undo_stack = gimp_image_get_undo_stack (image);
+
+ sel_tool->saved_show_selection =
+ gimp_display_shell_get_show_selection (shell);
+
+ if (create)
+ {
+ gimp_selection_tool_set_undo_ptr (&sel_tool->undo, NULL);
+ }
+ else
+ {
+ GimpUndoStack *redo_stack = gimp_image_get_redo_stack (image);
+ GimpUndo *undo;
+
+ undo = gimp_undo_stack_peek (undo_stack);
+
+ if (undo && undo == sel_tool->undo)
+ {
+ /* prevent this change from halting the tool */
+ gimp_tool_control_push_preserve (tool->control, TRUE);
+
+ gimp_image_undo (image);
+
+ gimp_tool_control_pop_preserve (tool->control);
+
+ gimp_selection_tool_set_undo_ptr (&sel_tool->undo, NULL);
+
+ /* we will need to redo if the user cancels or executes */
+ gimp_selection_tool_set_undo_ptr (
+ &sel_tool->redo,
+ gimp_undo_stack_peek (redo_stack));
+ }
+
+ /* if the operation is "Replace", turn off the marching ants,
+ * because they are confusing ...
+ */
+ if (operation == GIMP_CHANNEL_OP_REPLACE)
+ {
+ /* ... however, do this in an idle function, to avoid unnecessarily
+ * restarting the selection if we don't visit the main loop between
+ * the start_change() and end_change() calls.
+ */
+ sel_tool->idle_id = g_idle_add_full (
+ G_PRIORITY_HIGH_IDLE,
+ (GSourceFunc) gimp_selection_tool_idle,
+ sel_tool, NULL);
+ }
+ }
+
+ gimp_selection_tool_set_undo_ptr (
+ &sel_tool->undo,
+ gimp_undo_stack_peek (undo_stack));
+}
+
+void
+gimp_selection_tool_end_change (GimpSelectionTool *sel_tool,
+ gboolean cancel)
+{
+ GimpTool *tool;
+ GimpDisplayShell *shell;
+ GimpImage *image;
+ GimpUndoStack *undo_stack;
+
+ g_return_if_fail (GIMP_IS_SELECTION_TOOL (sel_tool));
+ g_return_if_fail (sel_tool->change_count > 0);
+
+ tool = GIMP_TOOL (sel_tool);
+
+ g_return_if_fail (tool->display != NULL);
+
+ if (--sel_tool->change_count > 0)
+ return;
+
+ shell = gimp_display_get_shell (tool->display);
+ image = gimp_display_get_image (tool->display);
+ undo_stack = gimp_image_get_undo_stack (image);
+
+ if (cancel)
+ {
+ GimpUndoStack *redo_stack = gimp_image_get_redo_stack (image);
+ GimpUndo *redo = gimp_undo_stack_peek (redo_stack);
+
+ if (redo && redo == sel_tool->redo)
+ {
+ /* prevent this from halting the tool */
+ gimp_tool_control_push_preserve (tool->control, TRUE);
+
+ gimp_image_redo (image);
+
+ gimp_tool_control_pop_preserve (tool->control);
+
+ gimp_selection_tool_set_undo_ptr (
+ &sel_tool->undo,
+ gimp_undo_stack_peek (undo_stack));
+ }
+ else
+ {
+ gimp_selection_tool_set_undo_ptr (&sel_tool->undo, NULL);
+ }
+ }
+ else
+ {
+ GimpUndo *undo = gimp_undo_stack_peek (undo_stack);
+
+ /* save the undo that we got when executing, but only if
+ * we actually selected something
+ */
+ if (undo && undo != sel_tool->undo)
+ gimp_selection_tool_set_undo_ptr (&sel_tool->undo, undo);
+ else
+ gimp_selection_tool_set_undo_ptr (&sel_tool->undo, NULL);
+ }
+
+ gimp_selection_tool_set_undo_ptr (&sel_tool->redo, NULL);
+
+ if (sel_tool->idle_id)
+ {
+ g_source_remove (sel_tool->idle_id);
+ sel_tool->idle_id = 0;
+ }
+ else
+ {
+ gimp_display_shell_set_show_selection (shell,
+ sel_tool->saved_show_selection);
+ }
+
+ gimp_image_flush (image);
+}