app: move undo/redo logic for GimpRectangleSelectTool to GimpSelectionTool

Move GimpRectangleSelectTool's image undo/redo logic to
GimpSelectionTool, by adding a pair of
gimp_selection_tool_{start,end}_change() functions.  These
functions should be called by subclasses before/after changing the
selection, having the functions take care of undoing/redoing the
existing selection as necessary.  This allows us to reuse this
logic in other selection tools, specifically, the free-select tool.
This commit is contained in:
Ell 2019-04-25 04:05:12 -04:00
parent cf892ba2f0
commit 4612105e52
3 changed files with 325 additions and 168 deletions

View File

@ -28,17 +28,14 @@
#include "core/gimpchannel-select.h"
#include "core/gimpchannel.h"
#include "core/gimpimage.h"
#include "core/gimpimage-undo.h"
#include "core/gimplayer-floating-selection.h"
#include "core/gimppickable.h"
#include "core/gimpundostack.h"
#include "widgets/gimphelp-ids.h"
#include "widgets/gimpwidgets-utils.h"
#include "display/gimpdisplay.h"
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-appearance.h"
#include "display/gimptoolrectangle.h"
#include "gimpeditselectiontool.h"
@ -52,18 +49,15 @@
struct _GimpRectangleSelectToolPrivate
{
GimpChannelOps operation; /* remember for use when modifying */
gboolean use_saved_op; /* use operation or get from options */
gboolean saved_show_selection; /* used to remember existing value */
GimpUndo *undo;
GimpUndo *redo;
GimpChannelOps operation; /* remember for use when modifying */
gboolean use_saved_op; /* use operation or get from options */
gdouble press_x;
gdouble press_y;
gdouble press_x;
gdouble press_y;
GimpToolWidget *widget;
GimpToolWidget *grab_widget;
GList *bindings;
GimpToolWidget *widget;
GimpToolWidget *grab_widget;
GList *bindings;
};
@ -233,7 +227,6 @@ gimp_rectangle_select_tool_button_press (GimpTool *tool,
{
GimpRectangleSelectTool *rect_tool = GIMP_RECTANGLE_SELECT_TOOL (tool);
GimpRectangleSelectToolPrivate *private = rect_tool->private;
GimpDisplayShell *shell = gimp_display_get_shell (display);
GimpRectangleFunction function;
if (tool->display && display != tool->display)
@ -286,8 +279,6 @@ gimp_rectangle_select_tool_button_press (GimpTool *tool,
GIMP_TOOL_RECTANGLE_CREATING);
}
private->saved_show_selection = gimp_display_shell_get_show_selection (shell);
/* if the shift or ctrl keys are down, we don't want to adjust, we
* want to create a new rectangle, regardless of pointer loc
*/
@ -315,42 +306,12 @@ gimp_rectangle_select_tool_button_press (GimpTool *tool,
gimp_tool_rectangle_get_function (GIMP_TOOL_RECTANGLE (private->widget));
if (function == GIMP_TOOL_RECTANGLE_CREATING)
{
private->use_saved_op = FALSE;
}
else
{
GimpImage *image = gimp_display_get_image (tool->display);
GimpUndoStack *undo_stack = gimp_image_get_undo_stack (image);
GimpUndoStack *redo_stack = gimp_image_get_redo_stack (image);
GimpUndo *undo;
GimpChannelOps operation;
private->use_saved_op = FALSE;
undo = gimp_undo_stack_peek (undo_stack);
if (undo && private->undo == 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);
/* we will need to redo if the user cancels or executes */
private->redo = gimp_undo_stack_peek (redo_stack);
}
/* if the operation is "Replace", turn off the marching ants,
* because they are confusing
*/
operation = gimp_rectangle_select_tool_get_operation (rect_tool);
if (operation == GIMP_CHANNEL_OP_REPLACE)
gimp_display_shell_set_show_selection (shell, FALSE);
}
private->undo = NULL;
gimp_selection_tool_start_change (
GIMP_SELECTION_TOOL (tool),
function == GIMP_TOOL_RECTANGLE_CREATING,
gimp_rectangle_select_tool_get_operation (rect_tool));
gimp_tool_control_activate (tool->control);
}
@ -365,36 +326,17 @@ gimp_rectangle_select_tool_button_release (GimpTool *tool,
{
GimpRectangleSelectTool *rect_tool = GIMP_RECTANGLE_SELECT_TOOL (tool);
GimpRectangleSelectToolPrivate *priv = rect_tool->private;
GimpImage *image;
image = gimp_display_get_image (tool->display);
gimp_tool_control_halt (tool->control);
gimp_selection_tool_end_change (GIMP_SELECTION_TOOL (tool),
/* if the user has not moved the mouse,
* cancel the change
*/
release_type == GIMP_BUTTON_RELEASE_CLICK ||
release_type == GIMP_BUTTON_RELEASE_CANCEL);
gimp_tool_pop_status (tool, display);
gimp_display_shell_set_show_selection (gimp_display_get_shell (display),
priv->saved_show_selection);
/*
* if the user has not moved the mouse, we need to redo the operation
* that was undone on button press.
*/
if (release_type == GIMP_BUTTON_RELEASE_CLICK)
{
GimpUndoStack *redo_stack = gimp_image_get_redo_stack (image);
GimpUndo *redo = gimp_undo_stack_peek (redo_stack);
if (redo && priv->redo == redo)
{
/* prevent this from halting the tool */
gimp_tool_control_push_preserve (tool->control, TRUE);
gimp_image_redo (image);
priv->redo = NULL;
gimp_tool_control_pop_preserve (tool->control);
}
}
if (priv->grab_widget)
{
@ -402,23 +344,6 @@ gimp_rectangle_select_tool_button_release (GimpTool *tool,
coords, time, state, release_type);
priv->grab_widget = NULL;
}
if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
{
if (priv->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);
}
priv->use_saved_op = TRUE; /* is this correct? */
}
priv->redo = NULL;
}
static void
@ -546,9 +471,18 @@ gimp_rectangle_select_tool_select (GimpRectangleSelectTool *rect_tool,
/* if rectangle exists, turn it into a selection */
if (rectangle_exists)
GIMP_RECTANGLE_SELECT_TOOL_GET_CLASS (rect_tool)->select (rect_tool,
operation,
x, y, w, h);
{
gimp_selection_tool_start_change (GIMP_SELECTION_TOOL (rect_tool),
FALSE,
operation);
GIMP_RECTANGLE_SELECT_TOOL_GET_CLASS (rect_tool)->select (rect_tool,
operation,
x, y, w, h);
gimp_selection_tool_end_change (GIMP_SELECTION_TOOL (rect_tool),
FALSE);
}
return rectangle_exists;
}
@ -605,8 +539,7 @@ gimp_rectangle_select_tool_rectangle_response (GimpToolWidget *widget,
gint response_id,
GimpRectangleSelectTool *rect_tool)
{
GimpTool *tool = GIMP_TOOL (rect_tool);
GimpRectangleSelectToolPrivate *priv = rect_tool->private;
GimpTool *tool = GIMP_TOOL (rect_tool);
switch (response_id)
{
@ -644,31 +577,7 @@ gimp_rectangle_select_tool_rectangle_response (GimpToolWidget *widget,
break;
case GIMP_TOOL_WIDGET_RESPONSE_CANCEL:
{
GimpImage *image = gimp_display_get_image (tool->display);
GimpUndoStack *undo_stack = gimp_image_get_undo_stack (image);
GimpUndo *undo = gimp_undo_stack_peek (undo_stack);
/* if we have an existing rectangle 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 && priv->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);
}
priv->undo = NULL;
priv->redo = NULL;
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
}
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
break;
}
}
@ -857,10 +766,6 @@ gimp_rectangle_select_tool_commit (GimpRectangleSelectTool *rect_tool)
gimp_rectangle_select_tool_update_option_defaults (rect_tool, FALSE);
}
/* Reset the automatic undo/redo mechanism */
priv->undo = NULL;
priv->redo = NULL;
}
static void
@ -883,9 +788,6 @@ gimp_rectangle_select_tool_halt (GimpRectangleSelectTool *rect_tool)
rect_tool);
}
priv->undo = NULL;
priv->redo = NULL;
if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
@ -976,33 +878,13 @@ gimp_rectangle_select_tool_update (GimpRectangleSelectTool *rect_tool)
if (tool->display && ! gimp_tool_control_is_active (tool->control))
{
GimpImage *image = gimp_display_get_image (tool->display);
GimpUndoStack *undo_stack = gimp_image_get_undo_stack (image);
GimpUndo *undo = gimp_undo_stack_peek (undo_stack);
gdouble x1, y1, x2, y2;
/* if we got here via button release, we have already undone the
* previous operation. But if we got here by some other means,
* we need to undo it now.
*/
if (undo && priv->undo == undo)
{
gimp_image_undo (image);
priv->undo = NULL;
}
gdouble x1, y1, x2, y2;
gimp_tool_rectangle_get_public_rect (GIMP_TOOL_RECTANGLE (priv->widget),
&x1, &y1, &x2, &y2);
if (gimp_rectangle_select_tool_select (rect_tool,
x1, y1, x2 - x1, y2 - y1))
{
/* save the undo that we got when executing, but only if
* we actually selected something
*/
priv->undo = gimp_undo_stack_peek (undo_stack);
priv->redo = NULL;
}
gimp_rectangle_select_tool_select (rect_tool,
x1, y1, x2 - x1, y2 - y1);
if (! priv->use_saved_op)
{
@ -1014,8 +896,6 @@ gimp_rectangle_select_tool_update (GimpRectangleSelectTool *rect_tool)
priv->operation = options->operation;
priv->use_saved_op = TRUE;
}
gimp_image_flush (image);
}
gimp_tool_control_pop_preserve (tool->control);

View File

@ -28,9 +28,12 @@
#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"
@ -43,7 +46,9 @@
#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,
@ -59,10 +64,17 @@ static void gimp_selection_tool_cursor_update (GimpTool *tool,
GdkModifierType state,
GimpDisplay *display);
static void gimp_selection_tool_commit (GimpSelectionTool *sel_tool);
static void gimp_selection_tool_halt (GimpSelectionTool *sel_tool,
GimpDisplay *dispaly);
static gboolean gimp_selection_tool_check (GimpSelectionTool *sel_tool,
GimpDisplay *display,
GError **error);
static void gimp_selection_tool_set_undo_ptr (GimpUndo **undo_ptr,
GimpUndo *undo);
G_DEFINE_TYPE (GimpSelectionTool, gimp_selection_tool, GIMP_TYPE_DRAW_TOOL)
@ -74,6 +86,7 @@ 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;
@ -83,10 +96,40 @@ gimp_selection_tool_class_init (GimpSelectionToolClass *klass)
static void
gimp_selection_tool_init (GimpSelectionTool *selection_tool)
{
selection_tool->function = SELECTION_SELECT;
selection_tool->saved_operation = GIMP_CHANNEL_OP_REPLACE;
selection_tool->function = SELECTION_SELECT;
selection_tool->saved_operation = GIMP_CHANNEL_OP_REPLACE;
selection_tool->allow_move = TRUE;
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
@ -396,6 +439,47 @@ gimp_selection_tool_cursor_update (GimpTool *tool,
modifier);
}
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,
@ -466,6 +550,25 @@ gimp_selection_tool_check (GimpSelectionTool *sel_tool,
return TRUE;
}
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 */
@ -528,3 +631,165 @@ gimp_selection_tool_start_edit (GimpSelectionTool *sel_tool,
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 (
(GSourceFunc) gimp_selection_tool_idle,
sel_tool);
}
}
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);
}

View File

@ -37,12 +37,18 @@ typedef struct _GimpSelectionToolClass GimpSelectionToolClass;
struct _GimpSelectionTool
{
GimpDrawTool parent_instance;
GimpDrawTool parent_instance;
SelectFunction function; /* selection function */
GimpChannelOps saved_operation; /* saved tool options state */
SelectFunction function; /* selection function */
GimpChannelOps saved_operation; /* saved tool options state */
gboolean allow_move;
gint change_count;
gboolean saved_show_selection;
GimpUndo *undo;
GimpUndo *redo;
gint idle_id;
gboolean allow_move;
};
struct _GimpSelectionToolClass
@ -51,12 +57,18 @@ struct _GimpSelectionToolClass
};
GType gimp_selection_tool_get_type (void) G_GNUC_CONST;
GType gimp_selection_tool_get_type (void) G_GNUC_CONST;
/* protected function */
gboolean gimp_selection_tool_start_edit (GimpSelectionTool *sel_tool,
GimpDisplay *display,
const GimpCoords *coords);
gboolean gimp_selection_tool_start_edit (GimpSelectionTool *sel_tool,
GimpDisplay *display,
const GimpCoords *coords);
void gimp_selection_tool_start_change (GimpSelectionTool *sel_tool,
gboolean create,
GimpChannelOps operation);
void gimp_selection_tool_end_change (GimpSelectionTool *sel_tool,
gboolean cancel);
#endif /* __GIMP_SELECTION_TOOL_H__ */