[gimp] Bug 670031 - Would like to undo intelligent scissors selections in progress
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] Bug 670031 - Would like to undo intelligent scissors selections in progress
- Date: Sat, 28 Mar 2015 20:34:23 +0000 (UTC)
commit 4842e2a14ff14f58f3eba6985fa7c77f961de24a
Author: Michael Natterer <mitch gimp org>
Date: Sat Mar 28 21:31:03 2015 +0100
Bug 670031 - Would like to undo intelligent scissors selections in progress
Add undo to the Isissors tool, along with some refactoring:
- Always modify the actual curve, instead of a set of obscure states
kept around in the tool instance
- On cancel, simply go back to the curve on the undo stack
- Draw handles on top of curve segments
- Draw the currently edited segments and handles in the highlight color
app/tools/gimpiscissorstool.c | 717 +++++++++++++++++++++++++++--------------
app/tools/gimpiscissorstool.h | 15 +-
2 files changed, 485 insertions(+), 247 deletions(-)
---
diff --git a/app/tools/gimpiscissorstool.c b/app/tools/gimpiscissorstool.c
index 6d6761c..0a613c7 100644
--- a/app/tools/gimpiscissorstool.c
+++ b/app/tools/gimpiscissorstool.c
@@ -68,6 +68,7 @@
#include "widgets/gimphelp-ids.h"
#include "widgets/gimpwidgets-utils.h"
+#include "display/gimpcanvasitem.h"
#include "display/gimpdisplay.h"
#include "gimpiscissorsoptions.h"
@@ -114,47 +115,59 @@ struct _ICurve
/* local function prototypes */
-static void gimp_iscissors_tool_finalize (GObject *object);
-
-static void gimp_iscissors_tool_control (GimpTool *tool,
- GimpToolAction action,
- GimpDisplay *display);
-static void gimp_iscissors_tool_button_press (GimpTool *tool,
- const GimpCoords *coords,
- guint32 time,
- GdkModifierType state,
- GimpButtonPressType press_type,
- GimpDisplay *display);
-static void gimp_iscissors_tool_button_release (GimpTool *tool,
- const GimpCoords *coords,
- guint32 time,
- GdkModifierType state,
- GimpButtonReleaseType release_type,
- GimpDisplay *display);
-static void gimp_iscissors_tool_motion (GimpTool *tool,
- const GimpCoords *coords,
- guint32 time,
- GdkModifierType state,
- GimpDisplay *display);
-static void gimp_iscissors_tool_oper_update (GimpTool *tool,
- const GimpCoords *coords,
- GdkModifierType state,
- gboolean proximity,
- GimpDisplay *display);
-static void gimp_iscissors_tool_cursor_update (GimpTool *tool,
- const GimpCoords *coords,
- GdkModifierType state,
- GimpDisplay *display);
-static gboolean gimp_iscissors_tool_key_press (GimpTool *tool,
- GdkEventKey *kevent,
- GimpDisplay *display);
-
-static void gimp_iscissors_tool_draw (GimpDrawTool *draw_tool);
-
-static void gimp_iscissors_tool_halt (GimpIscissorsTool *iscissors,
- GimpDisplay *display);
-static void gimp_iscissors_tool_commit (GimpIscissorsTool *iscissors,
- GimpDisplay *display);
+static void gimp_iscissors_tool_finalize (GObject *object);
+
+static void gimp_iscissors_tool_control (GimpTool *tool,
+ GimpToolAction action,
+ GimpDisplay *display);
+static void gimp_iscissors_tool_button_press (GimpTool *tool,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonPressType press_type,
+ GimpDisplay *display);
+static void gimp_iscissors_tool_button_release (GimpTool *tool,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonReleaseType release_type,
+ GimpDisplay *display);
+static void gimp_iscissors_tool_motion (GimpTool *tool,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpDisplay *display);
+static void gimp_iscissors_tool_oper_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ gboolean proximity,
+ GimpDisplay *display);
+static void gimp_iscissors_tool_cursor_update (GimpTool *tool,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display);
+static gboolean gimp_iscissors_tool_key_press (GimpTool *tool,
+ GdkEventKey *kevent,
+ GimpDisplay *display);
+static const gchar * gimp_iscissors_tool_get_undo_desc (GimpTool *tool,
+ GimpDisplay *display);
+static const gchar * gimp_iscissors_tool_get_redo_desc (GimpTool *tool,
+ GimpDisplay *display);
+static gboolean gimp_iscissors_tool_undo (GimpTool *tool,
+ GimpDisplay *display);
+static gboolean gimp_iscissors_tool_redo (GimpTool *tool,
+ GimpDisplay *display);
+
+static void gimp_iscissors_tool_draw (GimpDrawTool *draw_tool);
+
+static void gimp_iscissors_tool_push_undo (GimpIscissorsTool *iscissors);
+static void gimp_iscissors_tool_pop_undo (GimpIscissorsTool *iscissors);
+static void gimp_iscissors_tool_free_redo (GimpIscissorsTool *iscissors);
+
+ static void gimp_iscissors_tool_halt (GimpIscissorsTool *iscissors,
+ GimpDisplay *display);
+static void gimp_iscissors_tool_commit (GimpIscissorsTool *iscissors,
+ GimpDisplay *display);
static void iscissors_convert (GimpIscissorsTool *iscissors,
GimpDisplay *display);
@@ -174,7 +187,7 @@ static void find_max_gradient (GimpIscissorsTool *iscissors,
gint *y);
static void calculate_segment (GimpIscissorsTool *iscissors,
ISegment *segment);
-static void iscissors_draw_segment (GimpDrawTool *draw_tool,
+static GimpCanvasItem * iscissors_draw_segment (GimpDrawTool *draw_tool,
ISegment *segment);
static gint mouse_over_vertex (GimpIscissorsTool *iscissors,
@@ -203,9 +216,11 @@ static ISegment * isegment_new (gint x1,
gint y1,
gint x2,
gint y2);
+static ISegment * isegment_copy (ISegment *segment);
static void isegment_free (ISegment *segment);
static ICurve * icurve_new (void);
+static ICurve * icurve_copy (ICurve *curve);
static void icurve_clear (ICurve *curve);
static void icurve_free (ICurve *curve);
@@ -280,9 +295,13 @@ gimp_iscissors_tool_class_init (GimpIscissorsToolClass *klass)
tool_class->button_press = gimp_iscissors_tool_button_press;
tool_class->button_release = gimp_iscissors_tool_button_release;
tool_class->motion = gimp_iscissors_tool_motion;
+ tool_class->key_press = gimp_iscissors_tool_key_press;
tool_class->oper_update = gimp_iscissors_tool_oper_update;
tool_class->cursor_update = gimp_iscissors_tool_cursor_update;
- tool_class->key_press = gimp_iscissors_tool_key_press;
+ tool_class->get_undo_desc = gimp_iscissors_tool_get_undo_desc;
+ tool_class->get_redo_desc = gimp_iscissors_tool_get_redo_desc;
+ tool_class->undo = gimp_iscissors_tool_undo;
+ tool_class->redo = gimp_iscissors_tool_redo;
draw_tool_class->draw = gimp_iscissors_tool_draw;
@@ -376,8 +395,10 @@ gimp_iscissors_tool_button_press (GimpTool *tool,
GimpButtonPressType press_type,
GimpDisplay *display)
{
- GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
- GimpImage *image = gimp_display_get_image (display);
+ GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
+ GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (tool);
+ GimpImage *image = gimp_display_get_image (display);
+ ISegment *segment;
iscissors->x = RINT (coords->x);
iscissors->y = RINT (coords->y);
@@ -389,6 +410,8 @@ gimp_iscissors_tool_button_press (GimpTool *tool,
gimp_tool_control_activate (tool->control);
tool->display = display;
+ gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
+
switch (iscissors->state)
{
case NO_ACTION:
@@ -396,14 +419,19 @@ gimp_iscissors_tool_button_press (GimpTool *tool,
if (! (state & GDK_SHIFT_MASK))
find_max_gradient (iscissors, image,
- &iscissors->x,
- &iscissors->y);
+ &iscissors->x, &iscissors->y);
iscissors->x = CLAMP (iscissors->x, 0, gimp_image_get_width (image) - 1);
iscissors->y = CLAMP (iscissors->y, 0, gimp_image_get_height (image) - 1);
- iscissors->ix = iscissors->x;
- iscissors->iy = iscissors->y;
+ gimp_iscissors_tool_push_undo (iscissors);
+
+ segment = isegment_new (iscissors->x,
+ iscissors->y,
+ iscissors->x,
+ iscissors->y);
+
+ g_queue_push_tail (iscissors->curve->segments, segment);
/* Initialize the draw tool only on starting the tool */
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
@@ -413,13 +441,26 @@ gimp_iscissors_tool_button_press (GimpTool *tool,
/* Check if the mouse click occurred on a vertex or the curve itself */
if (clicked_on_vertex (iscissors, coords->x, coords->y))
{
- gimp_draw_tool_pause (GIMP_DRAW_TOOL (iscissors));
-
- iscissors->nx = iscissors->x;
- iscissors->ny = iscissors->y;
iscissors->state = SEED_ADJUSTMENT;
- gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+ /* recalculate both segments */
+ if (iscissors->segment1)
+ {
+ iscissors->segment1->x1 = iscissors->x;
+ iscissors->segment1->y1 = iscissors->y;
+
+ if (options->interactive)
+ calculate_segment (iscissors, iscissors->segment1);
+ }
+
+ if (iscissors->segment2)
+ {
+ iscissors->segment2->x2 = iscissors->x;
+ iscissors->segment2->y2 = iscissors->y;
+
+ if (options->interactive)
+ calculate_segment (iscissors, iscissors->segment2);
+ }
}
/* If the iscissors is connected, check if the click was inside */
else if (iscissors->curve->connected && iscissors->mask &&
@@ -434,14 +475,38 @@ gimp_iscissors_tool_button_press (GimpTool *tool,
{
/* if we're not connected, we're adding a new point */
- gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
+ ISegment *last = g_queue_peek_tail (iscissors->curve->segments);
iscissors->state = SEED_PLACEMENT;
- gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+ gimp_iscissors_tool_push_undo (iscissors);
+
+ if (last->x1 == last->x2 &&
+ last->y1 == last->y2)
+ {
+ last->x2 = iscissors->x;
+ last->y2 = iscissors->y;
+
+ if (options->interactive)
+ calculate_segment (iscissors, last);
+ }
+ else
+ {
+ segment = isegment_new (last->x2,
+ last->y2,
+ iscissors->x,
+ iscissors->y);
+
+ g_queue_push_tail (iscissors->curve->segments, segment);
+
+ if (options->interactive)
+ calculate_segment (iscissors, segment);
+ }
}
break;
}
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}
@@ -517,7 +582,8 @@ gimp_iscissors_tool_button_release (GimpTool *tool,
GimpButtonReleaseType release_type,
GimpDisplay *display)
{
- GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
+ GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
+ GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (tool);
gimp_tool_control_halt (tool->control);
@@ -539,44 +605,55 @@ gimp_iscissors_tool_button_release (GimpTool *tool,
if (! iscissors->curve->first_point)
{
/* Determine if we're connecting to the first point */
- if (! g_queue_is_empty (iscissors->curve->segments))
- {
- ISegment *segment = g_queue_peek_head (iscissors->curve->segments);
- if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (tool), display,
- iscissors->x, iscissors->y,
- GIMP_HANDLE_CIRCLE,
- segment->x1, segment->y1,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_HANDLE_ANCHOR_CENTER))
- {
- iscissors->x = segment->x1;
- iscissors->y = segment->y1;
- iscissors->curve->connected = TRUE;
- }
- }
+ ISegment *segment = g_queue_peek_head (iscissors->curve->segments);
- /* Create the new curve segment */
- if (iscissors->ix != iscissors->x ||
- iscissors->iy != iscissors->y)
+ if (gimp_draw_tool_on_handle (GIMP_DRAW_TOOL (tool), display,
+ iscissors->x, iscissors->y,
+ GIMP_HANDLE_CIRCLE,
+ segment->x1, segment->y1,
+ GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+ GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+ GIMP_HANDLE_ANCHOR_CENTER))
{
- ISegment *segment = isegment_new (iscissors->ix,
- iscissors->iy,
- iscissors->x,
- iscissors->y);
+ iscissors->x = segment->x1;
+ iscissors->y = segment->y1;
+
+ segment = g_queue_peek_tail (iscissors->curve->segments);
+
+ segment->x2 = iscissors->x;
+ segment->y2 = iscissors->y;
+
+ iscissors->curve->connected = TRUE;
- iscissors->ix = iscissors->x;
- iscissors->iy = iscissors->y;
+ if (! options->interactive)
+ calculate_segment (iscissors, segment);
- g_queue_push_tail (iscissors->curve->segments, segment);
+ gimp_iscissors_tool_free_redo (iscissors);
+ }
+ else
+ {
+ segment = g_queue_peek_tail (iscissors->curve->segments);
+
+ if (segment->x1 != segment->x2 ||
+ segment->y1 != segment->y2)
+ {
+ if (! options->interactive)
+ calculate_segment (iscissors, segment);
- calculate_segment (iscissors, segment);
+ gimp_iscissors_tool_free_redo (iscissors);
+ }
+ else
+ {
+ gimp_iscissors_tool_pop_undo (iscissors);
+ }
}
}
else /* this was our first point */
{
iscissors->curve->first_point = FALSE;
+
+ gimp_iscissors_tool_free_redo (iscissors);
}
break;
@@ -584,19 +661,33 @@ gimp_iscissors_tool_button_release (GimpTool *tool,
/* recalculate both segments */
if (iscissors->segment1)
{
- iscissors->segment1->x1 = iscissors->nx;
- iscissors->segment1->y1 = iscissors->ny;
-
- calculate_segment (iscissors, iscissors->segment1);
+ if (! options->interactive)
+ calculate_segment (iscissors, iscissors->segment1);
}
if (iscissors->segment2)
{
- iscissors->segment2->x2 = iscissors->nx;
- iscissors->segment2->y2 = iscissors->ny;
-
- calculate_segment (iscissors, iscissors->segment2);
+ if (! options->interactive)
+ calculate_segment (iscissors, iscissors->segment2);
}
+
+ gimp_iscissors_tool_free_redo (iscissors);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (iscissors->state)
+ {
+ case SEED_PLACEMENT:
+ gimp_iscissors_tool_pop_undo (iscissors);
+ break;
+
+ case SEED_ADJUSTMENT:
+ gimp_iscissors_tool_pop_undo (iscissors);
break;
default:
@@ -620,8 +711,10 @@ gimp_iscissors_tool_motion (GimpTool *tool,
GdkModifierType state,
GimpDisplay *display)
{
- GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
- GimpImage *image = gimp_display_get_image (display);
+ GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
+ GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (tool);
+ GimpImage *image = gimp_display_get_image (display);
+ ISegment *segment;
if (iscissors->state == NO_ACTION)
return;
@@ -631,35 +724,52 @@ gimp_iscissors_tool_motion (GimpTool *tool,
iscissors->x = RINT (coords->x);
iscissors->y = RINT (coords->y);
+ /* Hold the shift key down to disable the auto-edge snap feature */
+ if (! (state & GDK_SHIFT_MASK))
+ find_max_gradient (iscissors, image,
+ &iscissors->x, &iscissors->y);
+
+ iscissors->x = CLAMP (iscissors->x, 0, gimp_image_get_width (image) - 1);
+ iscissors->y = CLAMP (iscissors->y, 0, gimp_image_get_height (image) - 1);
+
switch (iscissors->state)
{
case SEED_PLACEMENT:
- /* Hold the shift key down to disable the auto-edge snap feature */
- if (! (state & GDK_SHIFT_MASK))
- find_max_gradient (iscissors, image,
- &iscissors->x, &iscissors->y);
+ segment = g_queue_peek_tail (iscissors->curve->segments);
- iscissors->x = CLAMP (iscissors->x, 0, gimp_image_get_width (image) - 1);
- iscissors->y = CLAMP (iscissors->y, 0, gimp_image_get_height (image) - 1);
+ segment->x2 = iscissors->x;
+ segment->y2 = iscissors->y;
if (iscissors->curve->first_point)
{
- iscissors->ix = iscissors->x;
- iscissors->iy = iscissors->y;
+ segment->x1 = segment->x2;
+ segment->y1 = segment->y2;
+ }
+ else
+ {
+ if (options->interactive)
+ calculate_segment (iscissors, segment);
}
break;
case SEED_ADJUSTMENT:
- /* Move the current seed to the location of the cursor */
- if (! (state & GDK_SHIFT_MASK))
- find_max_gradient (iscissors, image,
- &iscissors->x, &iscissors->y);
+ if (iscissors->segment1)
+ {
+ iscissors->segment1->x1 = iscissors->x;
+ iscissors->segment1->y1 = iscissors->y;
- iscissors->x = CLAMP (iscissors->x, 0, gimp_image_get_width (image) - 1);
- iscissors->y = CLAMP (iscissors->y, 0, gimp_image_get_height (image) - 1);
+ if (options->interactive)
+ calculate_segment (iscissors, iscissors->segment1);
+ }
- iscissors->nx = iscissors->x;
- iscissors->ny = iscissors->y;
+ if (iscissors->segment2)
+ {
+ iscissors->segment2->x2 = iscissors->x;
+ iscissors->segment2->y2 = iscissors->y;
+
+ if (options->interactive)
+ calculate_segment (iscissors, iscissors->segment2);
+ }
break;
default:
@@ -674,144 +784,97 @@ gimp_iscissors_tool_draw (GimpDrawTool *draw_tool)
{
GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (draw_tool);
GimpIscissorsOptions *options = GIMP_ISCISSORS_TOOL_GET_OPTIONS (draw_tool);
+ GimpCanvasItem *item;
+ GList *list;
- if (iscissors->state == SEED_PLACEMENT)
- {
- /* Draw the crosshairs target if we're placing a seed */
- gimp_draw_tool_add_handle (draw_tool,
- GIMP_HANDLE_CROSS,
- iscissors->x, iscissors->y,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_HANDLE_ANCHOR_CENTER);
-
- /* Draw a line boundary */
- if (! iscissors->curve->first_point)
- {
- if (! options->interactive)
- {
- gimp_draw_tool_add_line (draw_tool,
- iscissors->ix, iscissors->iy,
- iscissors->x, iscissors->y);
- }
- else
- {
- /* See if the mouse has moved. If so, create a new segment... */
- if (! iscissors->livewire ||
- (iscissors->ix != iscissors->livewire->x1 ||
- iscissors->iy != iscissors->livewire->y1 ||
- iscissors->x != iscissors->livewire->x2 ||
- iscissors->y != iscissors->livewire->y2))
- {
- if (iscissors->livewire)
- isegment_free (iscissors->livewire);
-
- iscissors->livewire = isegment_new (iscissors->ix,
- iscissors->iy,
- iscissors->x,
- iscissors->y);
-
- calculate_segment (iscissors, iscissors->livewire);
- }
-
- /* plot the segment */
- iscissors_draw_segment (draw_tool, iscissors->livewire);
- }
- }
- }
-
+ /* First, render all segments and lines */
if (! iscissors->curve->first_point)
{
- GList *list;
-
- /* Draw a point at the init point coordinates */
- if (! iscissors->curve->connected)
- {
- gimp_draw_tool_add_handle (draw_tool,
- GIMP_HANDLE_FILLED_CIRCLE,
- iscissors->ix,
- iscissors->iy,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_HANDLE_ANCHOR_CENTER);
- }
-
- /* Go through the list of isegments, and render each one... */
for (list = g_queue_peek_head_link (iscissors->curve->segments);
list;
list = g_list_next (list))
{
ISegment *segment = list->data;
- if (iscissors->state == SEED_ADJUSTMENT)
+ /* plot the segment */
+ item = iscissors_draw_segment (draw_tool, segment);
+
+ /* if this segment is currently being added or adjusted */
+ if ((iscissors->state == SEED_PLACEMENT &&
+ ! list->next)
+ ||
+ (iscissors->state == SEED_ADJUSTMENT &&
+ (segment == iscissors->segment1 ||
+ segment == iscissors->segment2)))
{
- /* don't draw segment1 at all */
- if (segment == iscissors->segment1)
- continue;
- }
-
- gimp_draw_tool_add_handle (draw_tool,
- GIMP_HANDLE_FILLED_CIRCLE,
- segment->x1,
- segment->y1,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_HANDLE_ANCHOR_CENTER);
+ if (! options->interactive)
+ item = gimp_draw_tool_add_line (draw_tool,
+ segment->x1, segment->y1,
+ segment->x2, segment->y2);
- if (iscissors->state == SEED_ADJUSTMENT)
- {
- /* draw only the start handle of segment2 */
- if (segment == iscissors->segment2)
- continue;
+ gimp_canvas_item_set_highlight (item, TRUE);
}
-
- /* plot the segment */
- iscissors_draw_segment (draw_tool, segment);
}
}
- if (iscissors->state == SEED_ADJUSTMENT)
+ /* Then, render the handles on top of the segments */
+ for (list = g_queue_peek_head_link (iscissors->curve->segments);
+ list;
+ list = g_list_next (list))
{
- /* plot both segments, and the control point between them */
- if (iscissors->segment1)
+ ISegment *segment = list->data;
+
+ if (! iscissors->curve->first_point)
{
- gimp_draw_tool_add_line (draw_tool,
- iscissors->segment1->x2,
- iscissors->segment1->y2,
- iscissors->nx,
- iscissors->ny);
+ gboolean adjustment = (iscissors->state == SEED_ADJUSTMENT &&
+ segment == iscissors->segment1);
+
+ item = gimp_draw_tool_add_handle (draw_tool,
+ adjustment ?
+ GIMP_HANDLE_CROSS :
+ GIMP_HANDLE_FILLED_CIRCLE,
+ segment->x1,
+ segment->y1,
+ GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+ GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+ GIMP_HANDLE_ANCHOR_CENTER);
+
+ if (adjustment)
+ gimp_canvas_item_set_highlight (item, TRUE);
}
- if (iscissors->segment2)
+ /* Draw the last point if the curve is not connected */
+ if (! list->next && ! iscissors->curve->connected)
{
- gimp_draw_tool_add_line (draw_tool,
- iscissors->segment2->x1,
- iscissors->segment2->y1,
- iscissors->nx,
- iscissors->ny);
+ gboolean placement = (iscissors->state == SEED_PLACEMENT);
+
+ item = gimp_draw_tool_add_handle (draw_tool,
+ placement ?
+ GIMP_HANDLE_CROSS :
+ GIMP_HANDLE_FILLED_CIRCLE,
+ segment->x2,
+ segment->y2,
+ GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+ GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+ GIMP_HANDLE_ANCHOR_CENTER);
+
+ if (placement)
+ gimp_canvas_item_set_highlight (item, TRUE);
}
-
- gimp_draw_tool_add_handle (draw_tool,
- GIMP_HANDLE_FILLED_CIRCLE,
- iscissors->nx,
- iscissors->ny,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_TOOL_HANDLE_SIZE_CIRCLE,
- GIMP_HANDLE_ANCHOR_CENTER);
}
}
-
-static void
+static GimpCanvasItem *
iscissors_draw_segment (GimpDrawTool *draw_tool,
ISegment *segment)
{
- GimpVector2 *points;
- gpointer *point;
- gint i, len;
+ GimpCanvasItem *item;
+ GimpVector2 *points;
+ gpointer *point;
+ gint i, len;
if (! segment->points)
- return;
+ return NULL;
len = segment->points->len;
@@ -825,9 +888,11 @@ iscissors_draw_segment (GimpDrawTool *draw_tool,
points[i].y = (coords >> 16);
}
- gimp_draw_tool_add_lines (draw_tool, points, len, FALSE);
+ item = gimp_draw_tool_add_lines (draw_tool, points, len, FALSE);
g_free (points);
+
+ return item;
}
static void
@@ -866,14 +931,14 @@ gimp_iscissors_tool_oper_update (GimpTool *tool,
GIMP_TOOL_HANDLE_SIZE_CIRCLE,
GIMP_HANDLE_ANCHOR_CENTER))
{
- gimp_tool_replace_status (tool, display, _("Click to close the"
- " curve"));
+ gimp_tool_replace_status (tool, display,
+ _("Click to close the curve"));
iscissors->op = ISCISSORS_OP_CONNECT;
}
else
{
- gimp_tool_replace_status (tool, display, _("Click to add a point"
- " on this segment"));
+ gimp_tool_replace_status (tool, display,
+ _("Click to add a point on this segment"));
iscissors->op = ISCISSORS_OP_ADD_POINT;
}
}
@@ -1021,37 +1086,159 @@ gimp_iscissors_tool_key_press (GimpTool *tool,
}
}
+static const gchar *
+gimp_iscissors_tool_get_undo_desc (GimpTool *tool,
+ GimpDisplay *display)
+{
+ GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
+
+ if (display != tool->display || ! iscissors->undo_stack)
+ return NULL;
+
+ return _("Modify Scissors Curve");
+}
+
+static const gchar *
+gimp_iscissors_tool_get_redo_desc (GimpTool *tool,
+ GimpDisplay *display)
+{
+ GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
+
+ if (display != tool->display || ! iscissors->redo_stack)
+ return NULL;
+
+ return _("Modify Scissors Curve");
+}
+
+static gboolean
+gimp_iscissors_tool_undo (GimpTool *tool,
+ GimpDisplay *display)
+{
+ GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
+
+ if (! gimp_iscissors_tool_get_undo_desc (tool, display))
+ return FALSE;
+
+ gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
+
+ iscissors->redo_stack = g_list_prepend (iscissors->redo_stack,
+ iscissors->curve);
+
+ iscissors->curve = iscissors->undo_stack->data;
+
+ iscissors->undo_stack = g_list_remove (iscissors->undo_stack,
+ iscissors->curve);
+
+ if (! iscissors->undo_stack)
+ {
+ iscissors->state = NO_ACTION;
+
+ gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
+ }
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+
+ return TRUE;
+}
+
+static gboolean
+gimp_iscissors_tool_redo (GimpTool *tool,
+ GimpDisplay *display)
+{
+ GimpIscissorsTool *iscissors = GIMP_ISCISSORS_TOOL (tool);
+
+ if (! gimp_iscissors_tool_get_redo_desc (tool, display))
+ return FALSE;
+
+ gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
+
+ if (! iscissors->undo_stack)
+ {
+ iscissors->state = WAITING;
+
+ gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
+ }
+
+ iscissors->undo_stack = g_list_prepend (iscissors->undo_stack,
+ iscissors->curve);
+
+ iscissors->curve = iscissors->redo_stack->data;
+
+ iscissors->redo_stack = g_list_remove (iscissors->redo_stack,
+ iscissors->curve);
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+
+ return TRUE;
+}
+
+static void
+gimp_iscissors_tool_push_undo (GimpIscissorsTool *iscissors)
+{
+ iscissors->undo_stack = g_list_prepend (iscissors->undo_stack,
+ icurve_copy (iscissors->curve));
+}
+
+static void
+gimp_iscissors_tool_pop_undo (GimpIscissorsTool *iscissors)
+{
+ icurve_free (iscissors->curve);
+ iscissors->curve = iscissors->undo_stack->data;
+
+ iscissors->undo_stack = g_list_remove (iscissors->undo_stack,
+ iscissors->curve);
+}
+
+static void
+gimp_iscissors_tool_free_redo (GimpIscissorsTool *iscissors)
+{
+ g_list_free_full (iscissors->redo_stack,
+ (GDestroyNotify) icurve_free);
+ iscissors->redo_stack = NULL;
+
+ /* update the undo actions / menu items */
+ gimp_image_flush (gimp_display_get_image (GIMP_TOOL (iscissors)->display));
+}
+
static void
gimp_iscissors_tool_halt (GimpIscissorsTool *iscissors,
GimpDisplay *display)
{
- /* Free and reset the curve */
icurve_clear (iscissors->curve);
- /* free mask */
- if (iscissors->mask)
+ iscissors->segment1 = NULL;
+ iscissors->segment2 = NULL;
+ iscissors->state = NO_ACTION;
+
+ if (iscissors->undo_stack)
{
- g_object_unref (iscissors->mask);
- iscissors->mask = NULL;
+ g_list_free_full (iscissors->undo_stack, (GDestroyNotify) icurve_free);
+ iscissors->undo_stack = NULL;
+ }
+
+ if (iscissors->redo_stack)
+ {
+ g_list_free_full (iscissors->redo_stack, (GDestroyNotify) icurve_free);
+ iscissors->redo_stack = NULL;
}
- /* free the gradient map */
if (iscissors->gradient_map)
{
g_object_unref (iscissors->gradient_map);
iscissors->gradient_map = NULL;
}
- iscissors->segment1 = NULL;
- iscissors->segment2 = NULL;
- iscissors->state = NO_ACTION;
-
- /* Reset the dp buffers */
if (iscissors->dp_buf)
{
gimp_temp_buf_unref (iscissors->dp_buf);
iscissors->dp_buf = NULL;
}
+
+ if (iscissors->mask)
+ {
+ g_object_unref (iscissors->mask);
+ iscissors->mask = NULL;
+ }
}
static void
@@ -1142,7 +1329,11 @@ clicked_on_vertex (GimpIscissorsTool *iscissors,
gint segments_found = mouse_over_vertex (iscissors, x, y);
if (segments_found > 1)
- return TRUE;
+ {
+ gimp_iscissors_tool_push_undo (iscissors);
+
+ return TRUE;
+ }
/* if only one segment was found, the segments are unconnected, and
* the user only wants to move either the first or last point
@@ -1173,6 +1364,9 @@ mouse_over_segment (GimpIscissorsTool *iscissors,
gpointer *pt;
gint len;
+ if (! segment->points)
+ continue;
+
pt = segment->points->pdata;
len = segment->points->len;
@@ -1217,6 +1411,8 @@ clicked_on_segment (GimpIscissorsTool *iscissors,
ISegment *segment = list->data;
ISegment *new_segment;
+ gimp_iscissors_tool_push_undo (iscissors);
+
/* Create the new segment */
new_segment = isegment_new (iscissors->x,
iscissors->y,
@@ -1653,8 +1849,9 @@ find_max_gradient (GimpIscissorsTool *iscissors,
gint x1, y1, x2, y2;
gfloat max_gradient;
- /* Initialise the gradient map tile manager for this image if we
- * don't already have one. */
+ /* Initialise the gradient map buffer for this image if we don't
+ * already have one.
+ */
if (! iscissors->gradient_map)
iscissors->gradient_map = gradient_map_new (image);
@@ -1726,6 +1923,31 @@ isegment_new (gint x1,
return segment;
}
+static ISegment *
+isegment_copy (ISegment *segment)
+{
+ ISegment *copy = isegment_new (segment->x1,
+ segment->y1,
+ segment->x2,
+ segment->y2);
+
+ if (segment->points)
+ {
+ gint i;
+
+ copy->points = g_ptr_array_sized_new (segment->points->len);
+
+ for (i = 0; i < segment->points->len; i++)
+ {
+ gpointer value = g_ptr_array_index (segment->points, i);
+
+ g_ptr_array_add (copy->points, value);
+ }
+ }
+
+ return copy;
+}
+
static void
isegment_free (ISegment *segment)
{
@@ -1746,6 +1968,25 @@ icurve_new (void)
return curve;
}
+static ICurve *
+icurve_copy (ICurve *curve)
+{
+ ICurve *copy = icurve_new ();
+ GList *link;
+
+ for (link = g_queue_peek_head_link (curve->segments);
+ link;
+ link = g_list_next (link))
+ {
+ g_queue_push_tail (copy->segments, isegment_copy (link->data));
+ }
+
+ copy->first_point = curve->first_point;
+ copy->connected = curve->connected;
+
+ return copy;
+}
+
static void
icurve_clear (ICurve *curve)
{
diff --git a/app/tools/gimpiscissorstool.h b/app/tools/gimpiscissorstool.h
index f1aa7f7..a3bffa6 100644
--- a/app/tools/gimpiscissorstool.h
+++ b/app/tools/gimpiscissorstool.h
@@ -65,24 +65,21 @@ struct _GimpIscissorsTool
IscissorsOps op;
- gint x, y; /* upper left hand coordinate */
- gint ix, iy; /* initial coordinates */
- gint nx, ny; /* new coordinates */
-
- GimpTempBuf *dp_buf; /* dynamic programming buffer */
-
- ISegment *livewire; /* livewire boundary segment */
+ gint x, y; /* mouse coordinates */
ISegment *segment1; /* 1st segment connected to current point */
ISegment *segment2; /* 2nd segment connected to current point */
ICurve *curve; /* the curve */
+ GList *undo_stack; /* stack of ICurves for undo */
+ GList *redo_stack; /* stack of ICurves for redo */
+
IscissorsState state; /* state of iscissors */
- /* XXX might be useful */
- GimpChannel *mask; /* selection mask */
GeglBuffer *gradient_map; /* lazily filled gradient map */
+ GimpTempBuf *dp_buf; /* dynamic programming buffer */
+ GimpChannel *mask; /* selection mask */
};
struct _GimpIscissorsToolClass
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]