From e56ff58b04dcb6e62c2d95d555a32bed0a9602aa Mon Sep 17 00:00:00 2001 From: Sven Neumann Date: Wed, 15 Sep 1999 10:54:25 +0000 Subject: [PATCH] applied a patch from Simon. --Sven --- ChangeLog | 13 + app/path_tool.c | 603 ++++++++++++++++++++++++++++++++++++----- app/path_toolP.h | 23 +- app/pixmaps2.h | 31 +++ app/tools.c | 2 +- app/tools/path_tool.c | 603 ++++++++++++++++++++++++++++++++++++----- app/tools/path_toolP.h | 23 +- app/tools/tools.c | 2 +- 8 files changed, 1142 insertions(+), 158 deletions(-) diff --git a/ChangeLog b/ChangeLog index c24540a7fb..9e4627a09c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Wed Sep 15 12:43:39 MEST 1999 Simon Budig + + * app/tools.c + * app/pixmaps2.h + * app/path_tool.c + * app/path_toolP.h + + Own pixmap for the Path Tool. It is now possible to connect + open ends of curves. Simply activate one end and shift click + on the other. Control-Click deletes points. + + Still no bezier/painting functions... + 1999-09-14 Tor Lillqvist * app/brush_select.c: Include config.h, guard inclusion of diff --git a/app/path_tool.c b/app/path_tool.c index f9d4411cb7..be20355790 100644 --- a/app/path_tool.c +++ b/app/path_tool.c @@ -29,6 +29,8 @@ * segments between two anchors. */ +#undef PATH_TOOL_DEBUG + #include /* #include "appenv.h" */ @@ -38,7 +40,9 @@ #include "path_tool.h" #include "path_toolP.h" +#ifdef PATH_TOOL_DEBUG #include +#endif PATH_TOOL_DEBUG #include "libgimp/gimpintl.h" @@ -68,15 +72,18 @@ static gdouble path_locate_point (Path *, PathCurve **, PathSegment * /* Tools to manipulate paths, curves, segments */ static PathCurve * path_add_curve (Path *, gint, gint); -static inline PathSegment * path_append_segment (Path *, PathCurve *, SegmentType, gint, gint); +static PathSegment * path_append_segment (Path *, PathCurve *, SegmentType, gint, gint); static PathSegment * path_prepend_segment (Path *, PathCurve *, SegmentType, gint, gint); static PathSegment * path_split_segment (PathSegment *, gdouble); +static void path_join_curves (PathSegment *, PathSegment *); +static void path_flip_curve (PathCurve *); static void path_free_path (Path *); static void path_free_curve (PathCurve *); static void path_free_segment (PathSegment *); +static void path_delete_segment (PathSegment *); static void path_print (Path *); static void path_offset_active (Path *, gdouble, gdouble); -static void path_clear_active (Path *, PathCurve *, PathSegment *); +static void path_set_flags (PathTool *, Path *, PathCurve *, PathSegment *, guint32, guint32); /* High level image-manipulation functions */ @@ -116,8 +123,9 @@ static ToolOptions *path_options = NULL; */ /* - * These functions are for applying a function over a complete path/curve/segment - * they can pass information to each other with a arbitrary data structure + * These functions are for applying a function over a complete + * path/curve/segment. They can pass information to each other + * with a arbitrary data structure * * The idea behind the three different functions is: * if pathfunc != NULL @@ -176,7 +184,9 @@ path_traverse_curve (Path *path, PathCurve *curve, CurveTraverseFunc curvefunc, static void path_traverse_segment (Path *path, PathCurve *curve, PathSegment *segment, SegmentTraverseFunc function, gpointer data) { +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "path_traverse_segment\n"); +#endif PATH_TOOL_DEBUG /* Something like: * for i = 1 to subsamples { @@ -190,34 +200,48 @@ path_traverse_segment (Path *path, PathCurve *curve, PathSegment *segment, Segme * Helper functions for manipulating the data-structures: */ -static PathCurve * path_add_curve (Path * cur_path, gint x, gint y) +static PathCurve *path_add_curve (Path * cur_path, gint x, gint y) { PathCurve * tmp = cur_path->curves; - PathCurve * new_curve; + PathCurve * new_curve = NULL; +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "path_add_curve\n"); +#endif PATH_TOOL_DEBUG - new_curve = g_new (PathCurve, 1); + if (cur_path) { + new_curve = g_new (PathCurve, 1); - new_curve->next = tmp; - new_curve->prev = NULL; - new_curve->cur_segment = NULL; - new_curve->segments = NULL; + new_curve->parent = cur_path; + new_curve->next = tmp; + new_curve->prev = NULL; + new_curve->cur_segment = NULL; + new_curve->segments = NULL; - if (tmp) tmp->prev = new_curve; + if (tmp) tmp->prev = new_curve; - cur_path->curves = cur_path->cur_curve = new_curve; + cur_path->curves = cur_path->cur_curve = new_curve; - new_curve->segments = path_append_segment (cur_path, new_curve, SEGMENT_LINE, x, y); + new_curve->segments = path_prepend_segment (cur_path, new_curve, SEGMENT_LINE, x, y); + } +#ifdef PATH_TOOL_DEBUG + else + fprintf (stderr, "Fatal Error: path_add_curve called without valid path\n"); +#endif PATH_TOOL_DEBUG + return new_curve; } -static inline PathSegment * path_append_segment (Path * cur_path, PathCurve * cur_curve, SegmentType type, gint x, gint y) +static PathSegment * path_append_segment (Path * cur_path, PathCurve * cur_curve, SegmentType type, gint x, gint y) { PathSegment * tmp = cur_curve->segments; PathSegment * new_segment = NULL; +#ifdef PATH_TOOL_DEBUG + fprintf(stderr, "path_append_segment\n"); +#endif PATH_TOOL_DEBUG + if (cur_curve) { tmp = cur_curve->segments; while (tmp && tmp->next && tmp->next != cur_curve->segments) { @@ -230,32 +254,239 @@ static inline PathSegment * path_append_segment (Path * cur_path, PathCurve * c new_segment->type = type; new_segment->x = x; new_segment->y = y; - new_segment->flags = SEGMENT_ACTIVE; + new_segment->flags = 0; + new_segment->parent = cur_curve; new_segment->next = NULL; new_segment->prev = tmp; new_segment->data = NULL; if (tmp) - { tmp->next = new_segment; - } cur_curve->cur_segment = new_segment; } +#ifdef PATH_TOOL_DEBUG else fprintf(stderr, "Fatal Error: path_append_segment called with a closed curve\n"); +#endif PATH_TOOL_DEBUG } +#ifdef PATH_TOOL_DEBUG else fprintf(stderr, "Fatal Error: path_append_segment called without valid curve\n"); +#endif PATH_TOOL_DEBUG return new_segment; } -/* static PathSegment * path_prepend_segment (Path *, PathCurve *, gint, gint); - * static PathSegment * path_split_segment (PathSegment *, gdouble); +static PathSegment * path_prepend_segment (Path * cur_path, PathCurve * cur_curve, SegmentType type, gint x, gint y) +{ + PathSegment * tmp = cur_curve->segments; + PathSegment * new_segment = NULL; + +#ifdef PATH_TOOL_DEBUG + fprintf(stderr, "path_prepend_segment\n"); +#endif PATH_TOOL_DEBUG + + if (cur_curve) { + tmp = cur_curve->segments; + + if (tmp == NULL || tmp->prev == NULL) { + new_segment = g_new (PathSegment, 1); + + new_segment->type = type; + new_segment->x = x; + new_segment->y = y; + new_segment->flags = 0; + new_segment->parent = cur_curve; + new_segment->next = tmp; + new_segment->prev = NULL; + new_segment->data = NULL; + + if (tmp) + tmp->prev = new_segment; + + cur_curve->segments = new_segment; + cur_curve->cur_segment = new_segment; + } +#ifdef PATH_TOOL_DEBUG + else + fprintf(stderr, "Fatal Error: path_prepend_segment called with a closed curve\n"); +#endif PATH_TOOL_DEBUG + } +#ifdef PATH_TOOL_DEBUG + else + fprintf(stderr, "Fatal Error: path_prepend_segment called without valid curve\n"); +#endif PATH_TOOL_DEBUG + + return new_segment; +} + +/* static PathSegment * path_split_segment (PathSegment *, gdouble); */ +/* + * Join two arbitrary endpoints and free the parent from the second + * segment, if it differs from the first parents. + */ + +static void +path_join_curves (PathSegment *segment1, PathSegment *segment2) { + PathCurve *curve1, *curve2; + PathSegment *tmp; + + if ((segment1->next && segment1->prev) || (segment2->next && segment2->prev)) { +#ifdef PATH_TOOL_DEBUG + fprintf(stderr, "Fatal Error: path_join_curves called with a closed segment\n"); +#endif PATH_TOOL_DEBUG + return; + } + if (segment1->parent == segment2->parent) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Joining beginning and end of the same curve...\n"); +#endif PATH_TOOL_DEBUG + if (segment2->next == NULL) { + segment2->next = segment1; + segment1->prev = segment2; + } else { + segment2->prev = segment1; + segment1->next = segment2; + } + return; + } + + if (segment1->next == NULL && segment2->next == NULL) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Flipping second curve (next, next)...\n"); +#endif PATH_TOOL_DEBUG + path_flip_curve (segment2->parent); + /* segment2 = segment2->parent->segments; + */ + } + + if (segment1->prev == NULL && segment2->prev == NULL) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Flipping second curve (prev, prev)...\n"); +#endif PATH_TOOL_DEBUG + path_flip_curve (segment2->parent); + /* segment2 = segment2->parent->segments; + * while (segment2->next) + * segment2 = segment2->next; + */ + } + + if (segment1->next == NULL && segment2->prev == NULL) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Appending second to first curve...\n"); +#endif PATH_TOOL_DEBUG + curve1 = segment1->parent; + curve2 = segment2->parent; + + segment1->next = segment2; + segment2->prev = segment1; + + curve2->segments = NULL; + + if (curve2->prev) + curve2->prev->next = curve2->next; + if (curve2->next) + curve2->next->prev = curve2->prev; + + if (curve2->parent->curves == curve2) + curve2->parent->curves = curve2->next; + + path_free_curve (curve2); + + tmp = segment2; + + while (tmp) { + tmp->parent = curve1; + tmp = tmp->next; + } + return; + } + + if (segment1->prev == NULL && segment2->next == NULL) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Prepending second to first curve...\n"); +#endif PATH_TOOL_DEBUG + curve1 = segment1->parent; + curve2 = segment2->parent; + + segment1->prev = segment2; + segment2->next = segment1; + + curve2->segments = NULL; + if (curve2->prev) + curve2->prev->next = curve2->next; + if (curve2->next) + curve2->next->prev = curve2->prev; + if (curve2->parent->curves == curve2) + curve2->parent->curves = curve2->next; + path_free_curve (curve2); + + tmp = segment2; + + while (tmp) { + tmp->parent = curve1; + curve1->segments = tmp; + tmp = tmp->prev; + } + return; + } + +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Cant join these curves yet...\n"); + return; +#endif PATH_TOOL_DEBUG + +} + +/* + * This function reverses the order of the anchors. This is + * necessary for some joining operations. + */ +static void +path_flip_curve (PathCurve *curve) +{ + gpointer *end_data; + SegmentType end_type; + + PathSegment *tmp, *tmp2; + + if (!curve && !curve->segments) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "path_flip_curve: No curve o no segments to flip!\n"); +#endif PATH_TOOL_DEBUG + return; + } + + tmp = curve->segments; + + while (tmp->next) + tmp = tmp->next; + + end_data = tmp->data; + end_type = tmp->type; + + tmp->parent->segments = tmp; + + while (tmp) { + tmp2 = tmp->next; + tmp->next = tmp->prev; + tmp->prev = tmp2; + if (tmp->next) { + tmp->type = tmp->next->type; + tmp->data = tmp->next->data; + } else { + tmp->type = end_type; + tmp->data = end_data; + } + tmp = tmp->next; + } +} + + static void path_free_path (Path * path) { @@ -283,13 +514,17 @@ path_free_curve (PathCurve *curve) if (curve) { tmp2 = curve->segments; - g_free(curve); + + /* break closed curves */ + if (tmp2 && tmp2->prev) + tmp2->prev->next = NULL; while ((tmp1 = tmp2) != NULL) { tmp2 = tmp1->next; path_free_segment (tmp1); } + g_free(curve); } } @@ -298,12 +533,62 @@ path_free_segment (PathSegment *segment) { if (segment) { + /* Clear the active flag to keep path_tool->single_active_segment consistent */ + path_set_flags (segment->parent->parent->path_tool, segment->parent->parent, + segment->parent, segment, 0, SEGMENT_ACTIVE); if (segment->data) g_free(segment->data); g_free (segment); } } +static void +path_delete_curve (PathCurve *curve) +{ + if (curve) + { + if (curve->next) + curve->next->prev = curve->prev; + if (curve->prev) + curve->prev->next = curve->next; + + if (curve == curve->parent->curves) { + curve->parent->curves = curve->next; + } + + path_free_curve (curve); + } +} + +static void +path_delete_segment (PathSegment *segment) +{ + if (segment) + { + if (segment->next) + segment->next->prev = segment->prev; + if (segment->prev) + segment->prev->next = segment->next; + + /* If the remaining curve is closed and has a + * single point only, open it. + */ + if (segment->next == segment->prev && segment->next) + segment->next->next = segment->next->prev = NULL; + + if (segment == segment->parent->segments) + segment->parent->segments = segment->next; + + if (segment->parent->segments == NULL) + path_delete_curve (segment->parent); + + path_free_segment (segment); + + /* + * here we have to update the surrounding segments + */ + } +} /* @@ -336,7 +621,9 @@ path_tool_button_press (Tool *tool, gint grab_pointer=0; gint x, y, halfwidth, dummy; +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "path_tool_button_press\n"); +#endif PATH_TOOL_DEBUG gdisp = (GDisplay *) gdisp_ptr; path_tool = (PathTool *) tool->private; @@ -344,7 +631,9 @@ path_tool_button_press (Tool *tool, /* Transform window-coordinates to canvas-coordinates */ gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y, TRUE, 0); +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "Clickcoordinates %d, %d\n",x,y); +#endif PATH_TOOL_DEBUG path_tool->click_x = x; path_tool->click_y = y; path_tool->click_modifier = bevent->state; @@ -395,20 +684,42 @@ path_tool_button_press_anchor (Tool *tool, GdkEventButton *bevent, GDisplay *gdisp) { + static guint32 last_click_time=0; + gboolean doubleclick=FALSE; PathTool *path_tool = tool->private; Path * cur_path = path_tool->cur_path; + PathSegment *p_sas; gint grab_pointer; +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "path_tool_button_press_anchor:\n"); +#endif PATH_TOOL_DEBUG grab_pointer = 1; if (!cur_path) { +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "Fatal error: No current Path\n"); +#endif PATH_TOOL_DEBUG return 0; } + /* + * We have to determine, if this was a doubleclick for ourself, because + * disp_callback.c ignores the GDK_[23]BUTTON_EVENT's and adding them to + * the switch statement confuses some tools. + */ + if (bevent->time - last_click_time < 250) { + doubleclick=TRUE; +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Doppelclick!\n"); +#endif PATH_TOOL_DEBUG + } else + doubleclick=FALSE; + last_click_time = bevent->time; + + draw_core_pause (path_tool->core, tool); /* The user pressed on an anchor: @@ -416,15 +727,75 @@ path_tool_button_press_anchor (Tool *tool, * + SHIFT toggles the activity of an anchor. * if this anchor is at the end of an open curve and the other * end is active, close the curve. + * + * Doubleclick (de)activates the whole curve (not Path!). */ - if (path_tool->click_modifier & GDK_SHIFT_MASK) - path_tool->click_segment->flags ^= SEGMENT_ACTIVE; - else { - if (!(path_tool->click_segment->flags & SEGMENT_ACTIVE)) - path_clear_active (cur_path, NULL, NULL); - path_tool->click_segment->flags |= SEGMENT_ACTIVE; + p_sas = path_tool->single_active_segment; + +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "p_sas: %p\n", p_sas); +#endif PATH_TOOL_DEBUG + + if (path_tool->click_modifier & GDK_SHIFT_MASK) { + if (path_tool->active_count == 1 && p_sas && p_sas != path_tool->click_segment && + (p_sas->next == NULL || p_sas->prev == NULL) && + (path_tool->click_segment->next == NULL || path_tool->click_segment->prev == NULL)) { + /* + * if this is the end of an open curve and the single active segment was another + * open end, connect those ends. + */ + path_join_curves (path_tool->click_segment, p_sas); + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + NULL, 0, SEGMENT_ACTIVE); + } + + if (doubleclick) + /* + * Doubleclick set the whole curve to the same state, depending on the + * state of the clicked anchor. + */ + if (path_tool->click_segment->flags & SEGMENT_ACTIVE) + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + NULL, SEGMENT_ACTIVE, 0); + else + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + NULL, 0, SEGMENT_ACTIVE); + else + /* + * Toggle the state of the clicked anchor. + */ + if (path_tool->click_segment->flags & SEGMENT_ACTIVE) + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + path_tool->click_segment, 0, SEGMENT_ACTIVE); + else + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + path_tool->click_segment, SEGMENT_ACTIVE, 0); } + /* + * Delete anchors, when CONTROL is pressed + */ + else if (path_tool->click_modifier & GDK_CONTROL_MASK) + { + if (path_tool->click_segment->flags & SEGMENT_ACTIVE) + { + if (path_tool->click_segment->prev) + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + path_tool->click_segment->prev, SEGMENT_ACTIVE, 0); + else if (path_tool->click_segment->next) + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + path_tool->click_segment->next, SEGMENT_ACTIVE, 0); + } + + path_delete_segment (path_tool->click_segment); + path_tool->click_segment = NULL; + } + else if (!(path_tool->click_segment->flags & SEGMENT_ACTIVE)) + { + path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE); + path_set_flags (path_tool, cur_path, path_tool->click_curve, path_tool->click_segment, SEGMENT_ACTIVE, 0); + } + /* Action goes here */ @@ -445,35 +816,47 @@ path_tool_button_press_canvas (Tool *tool, PathSegment * cur_segment; gint grab_pointer; +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "path_tool_button_press_canvas:\n"); +#endif PATH_TOOL_DEBUG grab_pointer = 1; if (!cur_path) { +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "Fatal error: No current Path\n"); +#endif PATH_TOOL_DEBUG return 0; } draw_core_pause (path_tool->core, tool); - - path_clear_active (cur_path, NULL, NULL); + + if (path_tool->active_count == 1 && path_tool->single_active_segment != NULL + && (path_tool->single_active_segment->prev == NULL || path_tool->single_active_segment->next == NULL)) { + cur_segment = path_tool->single_active_segment; + cur_curve = cur_segment->parent; - if (!(cur_curve = cur_path->cur_curve)) { + path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE); + + if (cur_segment->next == NULL) + cur_curve->cur_segment = path_append_segment(cur_path, cur_curve, SEGMENT_LINE, path_tool->click_x, path_tool->click_y); + else + cur_curve->cur_segment = path_prepend_segment(cur_path, cur_curve, SEGMENT_LINE, path_tool->click_x, path_tool->click_y); + if (cur_curve->cur_segment) { + path_set_flags (path_tool, cur_path, cur_curve, cur_curve->cur_segment, SEGMENT_ACTIVE, 0); + } + } else { + path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE); cur_path->cur_curve = path_add_curve(cur_path, path_tool->click_x, path_tool->click_y); - draw_core_resume(path_tool->core, tool); - return 0; + path_set_flags (path_tool, cur_path, cur_path->cur_curve, cur_path->cur_curve->segments, SEGMENT_ACTIVE, 0); + } - cur_curve->cur_segment = path_append_segment(cur_path, cur_curve, SEGMENT_LINE, path_tool->click_x, path_tool->click_y); - draw_core_resume(path_tool->core, tool); return 0; } - - - static void path_tool_button_release (Tool *tool, GdkEventButton *bevent, @@ -482,13 +865,14 @@ path_tool_button_release (Tool *tool, GDisplay * gdisp; PathTool * path_tool; +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "path_tool_button_release\n"); +#endif PATH_TOOL_DEBUG gdisp = (GDisplay *) gdisp_ptr; path_tool = (PathTool *) tool->private; path_tool->state &= ~PATH_TOOL_DRAG; - path_tool->state = 0; gdk_pointer_ungrab (bevent->time); gdk_flush (); @@ -507,6 +891,8 @@ path_tool_motion (Tool *tool, GDisplay * gdisp; PathTool * path_tool; + if (gtk_events_pending()) return; + gdisp = (GDisplay *) gdisp_ptr; path_tool = (PathTool *) tool->private; @@ -526,39 +912,68 @@ path_tool_motion_anchor (Tool *tool, GDisplay *gdisp) { PathTool * path_tool; - gdouble dx, dy; + gdouble dx, dy, d; gint x,y; - static gint lastx = 0; - static gint lasty = 0; - - if (gtk_events_pending()) return; /* Is this OK? I want to ignore just the motion-events... */ + static gint dxsum = 0; + static gint dysum = 0; path_tool = (PathTool *) tool->private; + /* + * Dont do anything, if the user clicked with pressed CONTROL-Key, + * because he deleted an anchor. + */ + if (path_tool->click_modifier & GDK_CONTROL_MASK) + return; + if (!(path_tool->state & PATH_TOOL_DRAG)) { path_tool->state |= PATH_TOOL_DRAG; - lastx = path_tool->click_x; - lasty = path_tool->click_y; + dxsum = 0; + dysum = 0; } gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, 0); - dx = x - lastx; - dy = y - lasty; + dx = x - path_tool->click_x - dxsum; + dy = y - path_tool->click_y - dysum; + /* restrict to horizontal/vertical lines, if modifiers are pressed + * I'm not sure, if this is intuitive for the user. Esp. When moving + * an endpoint of an curve I'd expect, that the *line* is + * horiz/vertical - not the delta to the point, where the point was + * originally... + */ + if (mevent->state & GDK_MOD1_MASK) + { + if (mevent->state & GDK_CONTROL_MASK) + { + d = (fabs(dx) + fabs(dy)) / 2; + d = (fabs(x - path_tool->click_x) + fabs(y - path_tool->click_y)) / 2; + + dx = ((x < path_tool->click_x) ? -d : d ) - dxsum; + dy = ((y < path_tool->click_y) ? -d : d ) - dysum; + } + else + dx = - dxsum; + } + else if (mevent->state & GDK_CONTROL_MASK) + dy = - dysum; + + path_tool->draw |= PATH_TOOL_REDRAW_ACTIVE; draw_core_pause(path_tool->core, tool); path_offset_active (path_tool->cur_path, dx, dy); + dxsum += dx; + dysum += dy; + draw_core_resume (path_tool->core, tool); path_tool->draw &= ~PATH_TOOL_REDRAW_ACTIVE; - lastx = x; - lasty = y; } @@ -571,8 +986,10 @@ path_tool_cursor_update (Tool *tool, GDisplay *gdisp; gint x, y, halfwidth, dummy, cursor_location; +#ifdef PATH_TOOL_DEBUG /* fprintf (stderr, "path_tool_cursor_update\n"); */ +#endif PATH_TOOL_DEBUG gdisp = (GDisplay *) gdisp_ptr; path_tool = (PathTool *) tool->private; @@ -609,7 +1026,9 @@ path_tool_control (Tool *tool, GDisplay * gdisp; PathTool * path_tool; +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "path_tool_control\n"); +#endif PATH_TOOL_DEBUG gdisp = (GDisplay *) tool->gdisp_ptr; path_tool = (PathTool *) tool->private; @@ -655,6 +1074,10 @@ tools_new_path_tool (void) private->click_y = 0; private->click_halfwidth = 0; private->click_modifier = 0; + + private->active_count = 0; + private->single_active_segment = NULL; + private->state = 0; private->draw = PATH_TOOL_REDRAW_ALL; private->core = draw_core_new (path_tool_draw); @@ -674,6 +1097,7 @@ tools_new_path_tool (void) private->cur_path->cur_curve = NULL; private->cur_path->name = g_string_new("Path 0"); private->cur_path->state = 0; + private->cur_path->path_tool = private; return tool; } @@ -701,7 +1125,7 @@ tools_free_path_tool (Tool *tool) /************************************************************** - * Set of tools to determine, if the click was on an anchor + * Set of function to determine, if the click was on an anchor */ typedef struct { @@ -767,7 +1191,7 @@ path_tool_on_anchors (Tool *tool, gint x, gint y, gint halfwidth, Path **ret_pat /************************************************************** - * Set of tools to offset all active anchors + * Set of function to offset all active anchors */ typedef struct { @@ -802,34 +1226,70 @@ path_offset_active (Path *path, gdouble dx, gdouble dy) /************************************************************** - * Set of tools to set the state of all anchors to inactive + * Set of function to set the state of all anchors to inactive */ +typedef struct { + guint32 bits_set; + guint32 bits_clear; + PathTool *path_tool; +} Path_set_flags_type; + /* This is a CurveTraverseFunc */ static void -path_clear_active_helper (Path *path, PathCurve *curve, PathSegment *segment, gpointer ptr) +path_set_flags_helper (Path *path, PathCurve *curve, PathSegment *segment, gpointer ptr) { - gint distance; + Path_set_flags_type *tmp = (Path_set_flags_type *) ptr; + guint32 oldflags; + + if (segment) { + oldflags = segment->flags; + segment->flags &= ~(tmp->bits_clear); + segment->flags |= tmp->bits_set; - if (segment) - segment->flags &= ~SEGMENT_ACTIVE; + /* + * Some black magic: We try to remember, which is the single active segment. + * We count, how many segments are active (in path_tool->active_count) and + * XOR path_tool->single_active_segment every time we select or deselect + * an anchor. So if exactly one anchor is active, path_tool->single_active_segment + * points to it. + */ + + /* If SEGMENT_ACTIVE state has changed change the PathTool data accordingly.*/ + if (((segment->flags ^ oldflags) & SEGMENT_ACTIVE) && tmp && tmp->path_tool) { + if (segment->flags & SEGMENT_ACTIVE) + tmp->path_tool->active_count++; + else + tmp->path_tool->active_count--; + + /* Does this work on all (16|32|64)-bit Machines? */ + + GPOINTER_TO_UINT(tmp->path_tool->single_active_segment) ^= GPOINTER_TO_UINT(segment); + } + } } static void -path_clear_active (Path *path, PathCurve *curve, PathSegment *segment) +path_set_flags (PathTool *path_tool, Path *path, PathCurve *curve, PathSegment *segment, guint32 bits_set, guint32 bits_clear) { - fprintf (stderr, "path_clear_active\n"); + Path_set_flags_type *tmp = g_new (Path_set_flags_type, 1); + tmp->bits_set=bits_set; + tmp->bits_clear=bits_clear; + tmp->path_tool = path_tool; + if (segment) - path_clear_active_helper (path, curve, segment, NULL); + path_set_flags_helper (path, curve, segment, tmp); else if (curve) - path_traverse_curve (path, curve, path_clear_active_helper, NULL, NULL); + path_traverse_curve (path, curve, path_set_flags_helper, NULL, tmp); else if (path) - path_traverse_path (path, NULL, path_clear_active_helper, NULL, NULL); + path_traverse_path (path, NULL, path_set_flags_helper, NULL, tmp); + + g_free (tmp); } /************************************************************** - * Set of tools to draw the segments to the window + * Set of functions to draw the segments to the window */ /* This is a CurveTraverseFunc */ @@ -840,10 +1300,13 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi GDisplay * gdisp; PathTool * path_tool; DrawCore * core; - gint x1, y1, x2, y2, draw; + gint x1, y1, x2, y2; + gboolean draw = TRUE; if (!tool) { +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "Fatal Error: path_tool_draw_segment called without valid tool *\n"); +#endif PATH_TOOL_DEBUG return; } @@ -852,15 +1315,8 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi path_tool = (PathTool *) tool->private; core = path_tool->core; - draw = 1; - if (path_tool->draw & PATH_TOOL_REDRAW_ACTIVE) - { - if (segment->flags & SEGMENT_ACTIVE || (segment->next && segment->next->flags & SEGMENT_ACTIVE)) - draw=1; - else - draw=0; - } + draw = (segment->flags & SEGMENT_ACTIVE || (segment->next && segment->next->flags & SEGMENT_ACTIVE)); if (segment && draw) { @@ -880,8 +1336,10 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi gdk_draw_line (core->win, core->gc, x1, y1, x2, y2); } } +#ifdef PATH_TOOL_DEBUG else if (!segment) fprintf(stderr, "path_tool_draw_segment: no segment to draw\n"); +#endif PATH_TOOL_DEBUG } static void @@ -892,11 +1350,14 @@ path_tool_draw (Tool *tool) PathTool * path_tool; PathCurve * cur_curve; +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "path_tool_draw\n"); +#endif PATH_TOOL_DEBUG gdisp = tool->gdisp_ptr; path_tool = tool->private; cur_path = path_tool->cur_path; path_traverse_path (cur_path, NULL, path_tool_draw_helper, NULL, tool); + } diff --git a/app/path_toolP.h b/app/path_toolP.h index 53f546c39b..0c26613b4d 100644 --- a/app/path_toolP.h +++ b/app/path_toolP.h @@ -40,45 +40,46 @@ typedef enum { SEGMENT_LINE, SEGMENT_BEZIER } SegmentType; typedef struct _path_segment PathSegment; +typedef struct _path_curve PathCurve; +typedef struct _path Path; + +typedef struct _path_tool PathTool; struct _path_segment { SegmentType type; /* What type of segment */ gdouble x, y; /* location of starting-point in image space */ + gpointer data; /* Additional data, dependant of segment-type */ guint32 flags; /* Various Flags: Is the Segment active? */ + PathCurve *parent; /* the parent Curve */ PathSegment *next; /* Next Segment or NULL */ PathSegment *prev; /* Previous Segment or NULL */ - gpointer data; /* Additional data, dependant of segment-type */ }; -typedef struct _path_curve PathCurve; - struct _path_curve { PathSegment * segments; /* The segments of the curve */ PathSegment * cur_segment; /* the current segment */ + Path * parent; /* the parent Path */ PathCurve * next; /* Next Curve or NULL */ PathCurve * prev; /* Previous Curve or NULL */ }; -typedef struct _path Path; - struct _path { PathCurve * curves; /* the curves */ PathCurve * cur_curve; /* the current curve */ GString * name; /* the name of the path */ guint32 state; /* is the path locked? */ + PathTool * path_tool; /* The parent Path Tool */ }; -typedef struct _path_tool PathTool; - struct _path_tool { gint click_pos; /* where did the user click? */ @@ -90,6 +91,14 @@ struct _path_tool PathCurve *click_curve; /* was the click? */ PathSegment *click_segment; + gint active_count; /* How many segments are active? */ + /* + * WARNING: single_active_segment may contain non NULL Values + * which point to the nirvana. But they are important! + * The pointer is garantueed to be valid, when active_count==1 + */ + PathSegment *single_active_segment; /* The only active segment */ + gint state; /* state of tool */ gint draw; /* all or part */ DrawCore *core; /* Core drawing object */ diff --git a/app/pixmaps2.h b/app/pixmaps2.h index 52bf8ff6a5..b02b405ec8 100644 --- a/app/pixmaps2.h +++ b/app/pixmaps2.h @@ -1004,3 +1004,34 @@ static char *xinput_airbrush_bits [] = "......................" }; +/* GIMP icon image format -- S. Kimball, P. Mattis */ +/* Image name: path_tool */ + + +#define path_tool_width 22 +#define path_tool_height 22 +static char *path_tool_bits [] = +{ + "......................", + "......aaa.............", + "......ahae............", + "......aaae............", + ".......ee..aaae.......", + "......a..aae..aae.....", + "......e.a.......ae....", + ".....a.a.........aa...", + "......a.........aaaa..", + "....aa..........aaaae.", + ".....a..a........aaee.", + "...aa....aa.......ee..", + "..aae....aaaa.........", + ".ahha.....aaaa........", + ".ahhae....aaa.........", + "..aaee.....a.a........", + "..aee.........a.......", + "..a...................", + "..a...................", + "..a...................", + "..a...................", + "......................" +}; diff --git a/app/tools.c b/app/tools.c index b135d3b70a..d150017b4a 100644 --- a/app/tools.c +++ b/app/tools.c @@ -520,7 +520,7 @@ ToolInfo tool_info[] = 26, N_("/Tools/Path"), "", - (char **) measure_bits, + (char **) path_tool_bits, N_("Manipulate paths"), "ContextHelp/path", PATH_TOOL, diff --git a/app/tools/path_tool.c b/app/tools/path_tool.c index f9d4411cb7..be20355790 100644 --- a/app/tools/path_tool.c +++ b/app/tools/path_tool.c @@ -29,6 +29,8 @@ * segments between two anchors. */ +#undef PATH_TOOL_DEBUG + #include /* #include "appenv.h" */ @@ -38,7 +40,9 @@ #include "path_tool.h" #include "path_toolP.h" +#ifdef PATH_TOOL_DEBUG #include +#endif PATH_TOOL_DEBUG #include "libgimp/gimpintl.h" @@ -68,15 +72,18 @@ static gdouble path_locate_point (Path *, PathCurve **, PathSegment * /* Tools to manipulate paths, curves, segments */ static PathCurve * path_add_curve (Path *, gint, gint); -static inline PathSegment * path_append_segment (Path *, PathCurve *, SegmentType, gint, gint); +static PathSegment * path_append_segment (Path *, PathCurve *, SegmentType, gint, gint); static PathSegment * path_prepend_segment (Path *, PathCurve *, SegmentType, gint, gint); static PathSegment * path_split_segment (PathSegment *, gdouble); +static void path_join_curves (PathSegment *, PathSegment *); +static void path_flip_curve (PathCurve *); static void path_free_path (Path *); static void path_free_curve (PathCurve *); static void path_free_segment (PathSegment *); +static void path_delete_segment (PathSegment *); static void path_print (Path *); static void path_offset_active (Path *, gdouble, gdouble); -static void path_clear_active (Path *, PathCurve *, PathSegment *); +static void path_set_flags (PathTool *, Path *, PathCurve *, PathSegment *, guint32, guint32); /* High level image-manipulation functions */ @@ -116,8 +123,9 @@ static ToolOptions *path_options = NULL; */ /* - * These functions are for applying a function over a complete path/curve/segment - * they can pass information to each other with a arbitrary data structure + * These functions are for applying a function over a complete + * path/curve/segment. They can pass information to each other + * with a arbitrary data structure * * The idea behind the three different functions is: * if pathfunc != NULL @@ -176,7 +184,9 @@ path_traverse_curve (Path *path, PathCurve *curve, CurveTraverseFunc curvefunc, static void path_traverse_segment (Path *path, PathCurve *curve, PathSegment *segment, SegmentTraverseFunc function, gpointer data) { +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "path_traverse_segment\n"); +#endif PATH_TOOL_DEBUG /* Something like: * for i = 1 to subsamples { @@ -190,34 +200,48 @@ path_traverse_segment (Path *path, PathCurve *curve, PathSegment *segment, Segme * Helper functions for manipulating the data-structures: */ -static PathCurve * path_add_curve (Path * cur_path, gint x, gint y) +static PathCurve *path_add_curve (Path * cur_path, gint x, gint y) { PathCurve * tmp = cur_path->curves; - PathCurve * new_curve; + PathCurve * new_curve = NULL; +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "path_add_curve\n"); +#endif PATH_TOOL_DEBUG - new_curve = g_new (PathCurve, 1); + if (cur_path) { + new_curve = g_new (PathCurve, 1); - new_curve->next = tmp; - new_curve->prev = NULL; - new_curve->cur_segment = NULL; - new_curve->segments = NULL; + new_curve->parent = cur_path; + new_curve->next = tmp; + new_curve->prev = NULL; + new_curve->cur_segment = NULL; + new_curve->segments = NULL; - if (tmp) tmp->prev = new_curve; + if (tmp) tmp->prev = new_curve; - cur_path->curves = cur_path->cur_curve = new_curve; + cur_path->curves = cur_path->cur_curve = new_curve; - new_curve->segments = path_append_segment (cur_path, new_curve, SEGMENT_LINE, x, y); + new_curve->segments = path_prepend_segment (cur_path, new_curve, SEGMENT_LINE, x, y); + } +#ifdef PATH_TOOL_DEBUG + else + fprintf (stderr, "Fatal Error: path_add_curve called without valid path\n"); +#endif PATH_TOOL_DEBUG + return new_curve; } -static inline PathSegment * path_append_segment (Path * cur_path, PathCurve * cur_curve, SegmentType type, gint x, gint y) +static PathSegment * path_append_segment (Path * cur_path, PathCurve * cur_curve, SegmentType type, gint x, gint y) { PathSegment * tmp = cur_curve->segments; PathSegment * new_segment = NULL; +#ifdef PATH_TOOL_DEBUG + fprintf(stderr, "path_append_segment\n"); +#endif PATH_TOOL_DEBUG + if (cur_curve) { tmp = cur_curve->segments; while (tmp && tmp->next && tmp->next != cur_curve->segments) { @@ -230,32 +254,239 @@ static inline PathSegment * path_append_segment (Path * cur_path, PathCurve * c new_segment->type = type; new_segment->x = x; new_segment->y = y; - new_segment->flags = SEGMENT_ACTIVE; + new_segment->flags = 0; + new_segment->parent = cur_curve; new_segment->next = NULL; new_segment->prev = tmp; new_segment->data = NULL; if (tmp) - { tmp->next = new_segment; - } cur_curve->cur_segment = new_segment; } +#ifdef PATH_TOOL_DEBUG else fprintf(stderr, "Fatal Error: path_append_segment called with a closed curve\n"); +#endif PATH_TOOL_DEBUG } +#ifdef PATH_TOOL_DEBUG else fprintf(stderr, "Fatal Error: path_append_segment called without valid curve\n"); +#endif PATH_TOOL_DEBUG return new_segment; } -/* static PathSegment * path_prepend_segment (Path *, PathCurve *, gint, gint); - * static PathSegment * path_split_segment (PathSegment *, gdouble); +static PathSegment * path_prepend_segment (Path * cur_path, PathCurve * cur_curve, SegmentType type, gint x, gint y) +{ + PathSegment * tmp = cur_curve->segments; + PathSegment * new_segment = NULL; + +#ifdef PATH_TOOL_DEBUG + fprintf(stderr, "path_prepend_segment\n"); +#endif PATH_TOOL_DEBUG + + if (cur_curve) { + tmp = cur_curve->segments; + + if (tmp == NULL || tmp->prev == NULL) { + new_segment = g_new (PathSegment, 1); + + new_segment->type = type; + new_segment->x = x; + new_segment->y = y; + new_segment->flags = 0; + new_segment->parent = cur_curve; + new_segment->next = tmp; + new_segment->prev = NULL; + new_segment->data = NULL; + + if (tmp) + tmp->prev = new_segment; + + cur_curve->segments = new_segment; + cur_curve->cur_segment = new_segment; + } +#ifdef PATH_TOOL_DEBUG + else + fprintf(stderr, "Fatal Error: path_prepend_segment called with a closed curve\n"); +#endif PATH_TOOL_DEBUG + } +#ifdef PATH_TOOL_DEBUG + else + fprintf(stderr, "Fatal Error: path_prepend_segment called without valid curve\n"); +#endif PATH_TOOL_DEBUG + + return new_segment; +} + +/* static PathSegment * path_split_segment (PathSegment *, gdouble); */ +/* + * Join two arbitrary endpoints and free the parent from the second + * segment, if it differs from the first parents. + */ + +static void +path_join_curves (PathSegment *segment1, PathSegment *segment2) { + PathCurve *curve1, *curve2; + PathSegment *tmp; + + if ((segment1->next && segment1->prev) || (segment2->next && segment2->prev)) { +#ifdef PATH_TOOL_DEBUG + fprintf(stderr, "Fatal Error: path_join_curves called with a closed segment\n"); +#endif PATH_TOOL_DEBUG + return; + } + if (segment1->parent == segment2->parent) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Joining beginning and end of the same curve...\n"); +#endif PATH_TOOL_DEBUG + if (segment2->next == NULL) { + segment2->next = segment1; + segment1->prev = segment2; + } else { + segment2->prev = segment1; + segment1->next = segment2; + } + return; + } + + if (segment1->next == NULL && segment2->next == NULL) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Flipping second curve (next, next)...\n"); +#endif PATH_TOOL_DEBUG + path_flip_curve (segment2->parent); + /* segment2 = segment2->parent->segments; + */ + } + + if (segment1->prev == NULL && segment2->prev == NULL) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Flipping second curve (prev, prev)...\n"); +#endif PATH_TOOL_DEBUG + path_flip_curve (segment2->parent); + /* segment2 = segment2->parent->segments; + * while (segment2->next) + * segment2 = segment2->next; + */ + } + + if (segment1->next == NULL && segment2->prev == NULL) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Appending second to first curve...\n"); +#endif PATH_TOOL_DEBUG + curve1 = segment1->parent; + curve2 = segment2->parent; + + segment1->next = segment2; + segment2->prev = segment1; + + curve2->segments = NULL; + + if (curve2->prev) + curve2->prev->next = curve2->next; + if (curve2->next) + curve2->next->prev = curve2->prev; + + if (curve2->parent->curves == curve2) + curve2->parent->curves = curve2->next; + + path_free_curve (curve2); + + tmp = segment2; + + while (tmp) { + tmp->parent = curve1; + tmp = tmp->next; + } + return; + } + + if (segment1->prev == NULL && segment2->next == NULL) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Prepending second to first curve...\n"); +#endif PATH_TOOL_DEBUG + curve1 = segment1->parent; + curve2 = segment2->parent; + + segment1->prev = segment2; + segment2->next = segment1; + + curve2->segments = NULL; + if (curve2->prev) + curve2->prev->next = curve2->next; + if (curve2->next) + curve2->next->prev = curve2->prev; + if (curve2->parent->curves == curve2) + curve2->parent->curves = curve2->next; + path_free_curve (curve2); + + tmp = segment2; + + while (tmp) { + tmp->parent = curve1; + curve1->segments = tmp; + tmp = tmp->prev; + } + return; + } + +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Cant join these curves yet...\n"); + return; +#endif PATH_TOOL_DEBUG + +} + +/* + * This function reverses the order of the anchors. This is + * necessary for some joining operations. + */ +static void +path_flip_curve (PathCurve *curve) +{ + gpointer *end_data; + SegmentType end_type; + + PathSegment *tmp, *tmp2; + + if (!curve && !curve->segments) { +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "path_flip_curve: No curve o no segments to flip!\n"); +#endif PATH_TOOL_DEBUG + return; + } + + tmp = curve->segments; + + while (tmp->next) + tmp = tmp->next; + + end_data = tmp->data; + end_type = tmp->type; + + tmp->parent->segments = tmp; + + while (tmp) { + tmp2 = tmp->next; + tmp->next = tmp->prev; + tmp->prev = tmp2; + if (tmp->next) { + tmp->type = tmp->next->type; + tmp->data = tmp->next->data; + } else { + tmp->type = end_type; + tmp->data = end_data; + } + tmp = tmp->next; + } +} + + static void path_free_path (Path * path) { @@ -283,13 +514,17 @@ path_free_curve (PathCurve *curve) if (curve) { tmp2 = curve->segments; - g_free(curve); + + /* break closed curves */ + if (tmp2 && tmp2->prev) + tmp2->prev->next = NULL; while ((tmp1 = tmp2) != NULL) { tmp2 = tmp1->next; path_free_segment (tmp1); } + g_free(curve); } } @@ -298,12 +533,62 @@ path_free_segment (PathSegment *segment) { if (segment) { + /* Clear the active flag to keep path_tool->single_active_segment consistent */ + path_set_flags (segment->parent->parent->path_tool, segment->parent->parent, + segment->parent, segment, 0, SEGMENT_ACTIVE); if (segment->data) g_free(segment->data); g_free (segment); } } +static void +path_delete_curve (PathCurve *curve) +{ + if (curve) + { + if (curve->next) + curve->next->prev = curve->prev; + if (curve->prev) + curve->prev->next = curve->next; + + if (curve == curve->parent->curves) { + curve->parent->curves = curve->next; + } + + path_free_curve (curve); + } +} + +static void +path_delete_segment (PathSegment *segment) +{ + if (segment) + { + if (segment->next) + segment->next->prev = segment->prev; + if (segment->prev) + segment->prev->next = segment->next; + + /* If the remaining curve is closed and has a + * single point only, open it. + */ + if (segment->next == segment->prev && segment->next) + segment->next->next = segment->next->prev = NULL; + + if (segment == segment->parent->segments) + segment->parent->segments = segment->next; + + if (segment->parent->segments == NULL) + path_delete_curve (segment->parent); + + path_free_segment (segment); + + /* + * here we have to update the surrounding segments + */ + } +} /* @@ -336,7 +621,9 @@ path_tool_button_press (Tool *tool, gint grab_pointer=0; gint x, y, halfwidth, dummy; +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "path_tool_button_press\n"); +#endif PATH_TOOL_DEBUG gdisp = (GDisplay *) gdisp_ptr; path_tool = (PathTool *) tool->private; @@ -344,7 +631,9 @@ path_tool_button_press (Tool *tool, /* Transform window-coordinates to canvas-coordinates */ gdisplay_untransform_coords (gdisp, bevent->x, bevent->y, &x, &y, TRUE, 0); +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "Clickcoordinates %d, %d\n",x,y); +#endif PATH_TOOL_DEBUG path_tool->click_x = x; path_tool->click_y = y; path_tool->click_modifier = bevent->state; @@ -395,20 +684,42 @@ path_tool_button_press_anchor (Tool *tool, GdkEventButton *bevent, GDisplay *gdisp) { + static guint32 last_click_time=0; + gboolean doubleclick=FALSE; PathTool *path_tool = tool->private; Path * cur_path = path_tool->cur_path; + PathSegment *p_sas; gint grab_pointer; +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "path_tool_button_press_anchor:\n"); +#endif PATH_TOOL_DEBUG grab_pointer = 1; if (!cur_path) { +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "Fatal error: No current Path\n"); +#endif PATH_TOOL_DEBUG return 0; } + /* + * We have to determine, if this was a doubleclick for ourself, because + * disp_callback.c ignores the GDK_[23]BUTTON_EVENT's and adding them to + * the switch statement confuses some tools. + */ + if (bevent->time - last_click_time < 250) { + doubleclick=TRUE; +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "Doppelclick!\n"); +#endif PATH_TOOL_DEBUG + } else + doubleclick=FALSE; + last_click_time = bevent->time; + + draw_core_pause (path_tool->core, tool); /* The user pressed on an anchor: @@ -416,15 +727,75 @@ path_tool_button_press_anchor (Tool *tool, * + SHIFT toggles the activity of an anchor. * if this anchor is at the end of an open curve and the other * end is active, close the curve. + * + * Doubleclick (de)activates the whole curve (not Path!). */ - if (path_tool->click_modifier & GDK_SHIFT_MASK) - path_tool->click_segment->flags ^= SEGMENT_ACTIVE; - else { - if (!(path_tool->click_segment->flags & SEGMENT_ACTIVE)) - path_clear_active (cur_path, NULL, NULL); - path_tool->click_segment->flags |= SEGMENT_ACTIVE; + p_sas = path_tool->single_active_segment; + +#ifdef PATH_TOOL_DEBUG + fprintf (stderr, "p_sas: %p\n", p_sas); +#endif PATH_TOOL_DEBUG + + if (path_tool->click_modifier & GDK_SHIFT_MASK) { + if (path_tool->active_count == 1 && p_sas && p_sas != path_tool->click_segment && + (p_sas->next == NULL || p_sas->prev == NULL) && + (path_tool->click_segment->next == NULL || path_tool->click_segment->prev == NULL)) { + /* + * if this is the end of an open curve and the single active segment was another + * open end, connect those ends. + */ + path_join_curves (path_tool->click_segment, p_sas); + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + NULL, 0, SEGMENT_ACTIVE); + } + + if (doubleclick) + /* + * Doubleclick set the whole curve to the same state, depending on the + * state of the clicked anchor. + */ + if (path_tool->click_segment->flags & SEGMENT_ACTIVE) + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + NULL, SEGMENT_ACTIVE, 0); + else + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + NULL, 0, SEGMENT_ACTIVE); + else + /* + * Toggle the state of the clicked anchor. + */ + if (path_tool->click_segment->flags & SEGMENT_ACTIVE) + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + path_tool->click_segment, 0, SEGMENT_ACTIVE); + else + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + path_tool->click_segment, SEGMENT_ACTIVE, 0); } + /* + * Delete anchors, when CONTROL is pressed + */ + else if (path_tool->click_modifier & GDK_CONTROL_MASK) + { + if (path_tool->click_segment->flags & SEGMENT_ACTIVE) + { + if (path_tool->click_segment->prev) + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + path_tool->click_segment->prev, SEGMENT_ACTIVE, 0); + else if (path_tool->click_segment->next) + path_set_flags (path_tool, path_tool->click_path, path_tool->click_curve, + path_tool->click_segment->next, SEGMENT_ACTIVE, 0); + } + + path_delete_segment (path_tool->click_segment); + path_tool->click_segment = NULL; + } + else if (!(path_tool->click_segment->flags & SEGMENT_ACTIVE)) + { + path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE); + path_set_flags (path_tool, cur_path, path_tool->click_curve, path_tool->click_segment, SEGMENT_ACTIVE, 0); + } + /* Action goes here */ @@ -445,35 +816,47 @@ path_tool_button_press_canvas (Tool *tool, PathSegment * cur_segment; gint grab_pointer; +#ifdef PATH_TOOL_DEBUG fprintf(stderr, "path_tool_button_press_canvas:\n"); +#endif PATH_TOOL_DEBUG grab_pointer = 1; if (!cur_path) { +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "Fatal error: No current Path\n"); +#endif PATH_TOOL_DEBUG return 0; } draw_core_pause (path_tool->core, tool); - - path_clear_active (cur_path, NULL, NULL); + + if (path_tool->active_count == 1 && path_tool->single_active_segment != NULL + && (path_tool->single_active_segment->prev == NULL || path_tool->single_active_segment->next == NULL)) { + cur_segment = path_tool->single_active_segment; + cur_curve = cur_segment->parent; - if (!(cur_curve = cur_path->cur_curve)) { + path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE); + + if (cur_segment->next == NULL) + cur_curve->cur_segment = path_append_segment(cur_path, cur_curve, SEGMENT_LINE, path_tool->click_x, path_tool->click_y); + else + cur_curve->cur_segment = path_prepend_segment(cur_path, cur_curve, SEGMENT_LINE, path_tool->click_x, path_tool->click_y); + if (cur_curve->cur_segment) { + path_set_flags (path_tool, cur_path, cur_curve, cur_curve->cur_segment, SEGMENT_ACTIVE, 0); + } + } else { + path_set_flags (path_tool, cur_path, NULL, NULL, 0, SEGMENT_ACTIVE); cur_path->cur_curve = path_add_curve(cur_path, path_tool->click_x, path_tool->click_y); - draw_core_resume(path_tool->core, tool); - return 0; + path_set_flags (path_tool, cur_path, cur_path->cur_curve, cur_path->cur_curve->segments, SEGMENT_ACTIVE, 0); + } - cur_curve->cur_segment = path_append_segment(cur_path, cur_curve, SEGMENT_LINE, path_tool->click_x, path_tool->click_y); - draw_core_resume(path_tool->core, tool); return 0; } - - - static void path_tool_button_release (Tool *tool, GdkEventButton *bevent, @@ -482,13 +865,14 @@ path_tool_button_release (Tool *tool, GDisplay * gdisp; PathTool * path_tool; +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "path_tool_button_release\n"); +#endif PATH_TOOL_DEBUG gdisp = (GDisplay *) gdisp_ptr; path_tool = (PathTool *) tool->private; path_tool->state &= ~PATH_TOOL_DRAG; - path_tool->state = 0; gdk_pointer_ungrab (bevent->time); gdk_flush (); @@ -507,6 +891,8 @@ path_tool_motion (Tool *tool, GDisplay * gdisp; PathTool * path_tool; + if (gtk_events_pending()) return; + gdisp = (GDisplay *) gdisp_ptr; path_tool = (PathTool *) tool->private; @@ -526,39 +912,68 @@ path_tool_motion_anchor (Tool *tool, GDisplay *gdisp) { PathTool * path_tool; - gdouble dx, dy; + gdouble dx, dy, d; gint x,y; - static gint lastx = 0; - static gint lasty = 0; - - if (gtk_events_pending()) return; /* Is this OK? I want to ignore just the motion-events... */ + static gint dxsum = 0; + static gint dysum = 0; path_tool = (PathTool *) tool->private; + /* + * Dont do anything, if the user clicked with pressed CONTROL-Key, + * because he deleted an anchor. + */ + if (path_tool->click_modifier & GDK_CONTROL_MASK) + return; + if (!(path_tool->state & PATH_TOOL_DRAG)) { path_tool->state |= PATH_TOOL_DRAG; - lastx = path_tool->click_x; - lasty = path_tool->click_y; + dxsum = 0; + dysum = 0; } gdisplay_untransform_coords (gdisp, mevent->x, mevent->y, &x, &y, TRUE, 0); - dx = x - lastx; - dy = y - lasty; + dx = x - path_tool->click_x - dxsum; + dy = y - path_tool->click_y - dysum; + /* restrict to horizontal/vertical lines, if modifiers are pressed + * I'm not sure, if this is intuitive for the user. Esp. When moving + * an endpoint of an curve I'd expect, that the *line* is + * horiz/vertical - not the delta to the point, where the point was + * originally... + */ + if (mevent->state & GDK_MOD1_MASK) + { + if (mevent->state & GDK_CONTROL_MASK) + { + d = (fabs(dx) + fabs(dy)) / 2; + d = (fabs(x - path_tool->click_x) + fabs(y - path_tool->click_y)) / 2; + + dx = ((x < path_tool->click_x) ? -d : d ) - dxsum; + dy = ((y < path_tool->click_y) ? -d : d ) - dysum; + } + else + dx = - dxsum; + } + else if (mevent->state & GDK_CONTROL_MASK) + dy = - dysum; + + path_tool->draw |= PATH_TOOL_REDRAW_ACTIVE; draw_core_pause(path_tool->core, tool); path_offset_active (path_tool->cur_path, dx, dy); + dxsum += dx; + dysum += dy; + draw_core_resume (path_tool->core, tool); path_tool->draw &= ~PATH_TOOL_REDRAW_ACTIVE; - lastx = x; - lasty = y; } @@ -571,8 +986,10 @@ path_tool_cursor_update (Tool *tool, GDisplay *gdisp; gint x, y, halfwidth, dummy, cursor_location; +#ifdef PATH_TOOL_DEBUG /* fprintf (stderr, "path_tool_cursor_update\n"); */ +#endif PATH_TOOL_DEBUG gdisp = (GDisplay *) gdisp_ptr; path_tool = (PathTool *) tool->private; @@ -609,7 +1026,9 @@ path_tool_control (Tool *tool, GDisplay * gdisp; PathTool * path_tool; +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "path_tool_control\n"); +#endif PATH_TOOL_DEBUG gdisp = (GDisplay *) tool->gdisp_ptr; path_tool = (PathTool *) tool->private; @@ -655,6 +1074,10 @@ tools_new_path_tool (void) private->click_y = 0; private->click_halfwidth = 0; private->click_modifier = 0; + + private->active_count = 0; + private->single_active_segment = NULL; + private->state = 0; private->draw = PATH_TOOL_REDRAW_ALL; private->core = draw_core_new (path_tool_draw); @@ -674,6 +1097,7 @@ tools_new_path_tool (void) private->cur_path->cur_curve = NULL; private->cur_path->name = g_string_new("Path 0"); private->cur_path->state = 0; + private->cur_path->path_tool = private; return tool; } @@ -701,7 +1125,7 @@ tools_free_path_tool (Tool *tool) /************************************************************** - * Set of tools to determine, if the click was on an anchor + * Set of function to determine, if the click was on an anchor */ typedef struct { @@ -767,7 +1191,7 @@ path_tool_on_anchors (Tool *tool, gint x, gint y, gint halfwidth, Path **ret_pat /************************************************************** - * Set of tools to offset all active anchors + * Set of function to offset all active anchors */ typedef struct { @@ -802,34 +1226,70 @@ path_offset_active (Path *path, gdouble dx, gdouble dy) /************************************************************** - * Set of tools to set the state of all anchors to inactive + * Set of function to set the state of all anchors to inactive */ +typedef struct { + guint32 bits_set; + guint32 bits_clear; + PathTool *path_tool; +} Path_set_flags_type; + /* This is a CurveTraverseFunc */ static void -path_clear_active_helper (Path *path, PathCurve *curve, PathSegment *segment, gpointer ptr) +path_set_flags_helper (Path *path, PathCurve *curve, PathSegment *segment, gpointer ptr) { - gint distance; + Path_set_flags_type *tmp = (Path_set_flags_type *) ptr; + guint32 oldflags; + + if (segment) { + oldflags = segment->flags; + segment->flags &= ~(tmp->bits_clear); + segment->flags |= tmp->bits_set; - if (segment) - segment->flags &= ~SEGMENT_ACTIVE; + /* + * Some black magic: We try to remember, which is the single active segment. + * We count, how many segments are active (in path_tool->active_count) and + * XOR path_tool->single_active_segment every time we select or deselect + * an anchor. So if exactly one anchor is active, path_tool->single_active_segment + * points to it. + */ + + /* If SEGMENT_ACTIVE state has changed change the PathTool data accordingly.*/ + if (((segment->flags ^ oldflags) & SEGMENT_ACTIVE) && tmp && tmp->path_tool) { + if (segment->flags & SEGMENT_ACTIVE) + tmp->path_tool->active_count++; + else + tmp->path_tool->active_count--; + + /* Does this work on all (16|32|64)-bit Machines? */ + + GPOINTER_TO_UINT(tmp->path_tool->single_active_segment) ^= GPOINTER_TO_UINT(segment); + } + } } static void -path_clear_active (Path *path, PathCurve *curve, PathSegment *segment) +path_set_flags (PathTool *path_tool, Path *path, PathCurve *curve, PathSegment *segment, guint32 bits_set, guint32 bits_clear) { - fprintf (stderr, "path_clear_active\n"); + Path_set_flags_type *tmp = g_new (Path_set_flags_type, 1); + tmp->bits_set=bits_set; + tmp->bits_clear=bits_clear; + tmp->path_tool = path_tool; + if (segment) - path_clear_active_helper (path, curve, segment, NULL); + path_set_flags_helper (path, curve, segment, tmp); else if (curve) - path_traverse_curve (path, curve, path_clear_active_helper, NULL, NULL); + path_traverse_curve (path, curve, path_set_flags_helper, NULL, tmp); else if (path) - path_traverse_path (path, NULL, path_clear_active_helper, NULL, NULL); + path_traverse_path (path, NULL, path_set_flags_helper, NULL, tmp); + + g_free (tmp); } /************************************************************** - * Set of tools to draw the segments to the window + * Set of functions to draw the segments to the window */ /* This is a CurveTraverseFunc */ @@ -840,10 +1300,13 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi GDisplay * gdisp; PathTool * path_tool; DrawCore * core; - gint x1, y1, x2, y2, draw; + gint x1, y1, x2, y2; + gboolean draw = TRUE; if (!tool) { +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "Fatal Error: path_tool_draw_segment called without valid tool *\n"); +#endif PATH_TOOL_DEBUG return; } @@ -852,15 +1315,8 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi path_tool = (PathTool *) tool->private; core = path_tool->core; - draw = 1; - if (path_tool->draw & PATH_TOOL_REDRAW_ACTIVE) - { - if (segment->flags & SEGMENT_ACTIVE || (segment->next && segment->next->flags & SEGMENT_ACTIVE)) - draw=1; - else - draw=0; - } + draw = (segment->flags & SEGMENT_ACTIVE || (segment->next && segment->next->flags & SEGMENT_ACTIVE)); if (segment && draw) { @@ -880,8 +1336,10 @@ path_tool_draw_helper (Path *path, PathCurve *curve, PathSegment * segment, gpoi gdk_draw_line (core->win, core->gc, x1, y1, x2, y2); } } +#ifdef PATH_TOOL_DEBUG else if (!segment) fprintf(stderr, "path_tool_draw_segment: no segment to draw\n"); +#endif PATH_TOOL_DEBUG } static void @@ -892,11 +1350,14 @@ path_tool_draw (Tool *tool) PathTool * path_tool; PathCurve * cur_curve; +#ifdef PATH_TOOL_DEBUG fprintf (stderr, "path_tool_draw\n"); +#endif PATH_TOOL_DEBUG gdisp = tool->gdisp_ptr; path_tool = tool->private; cur_path = path_tool->cur_path; path_traverse_path (cur_path, NULL, path_tool_draw_helper, NULL, tool); + } diff --git a/app/tools/path_toolP.h b/app/tools/path_toolP.h index 53f546c39b..0c26613b4d 100644 --- a/app/tools/path_toolP.h +++ b/app/tools/path_toolP.h @@ -40,45 +40,46 @@ typedef enum { SEGMENT_LINE, SEGMENT_BEZIER } SegmentType; typedef struct _path_segment PathSegment; +typedef struct _path_curve PathCurve; +typedef struct _path Path; + +typedef struct _path_tool PathTool; struct _path_segment { SegmentType type; /* What type of segment */ gdouble x, y; /* location of starting-point in image space */ + gpointer data; /* Additional data, dependant of segment-type */ guint32 flags; /* Various Flags: Is the Segment active? */ + PathCurve *parent; /* the parent Curve */ PathSegment *next; /* Next Segment or NULL */ PathSegment *prev; /* Previous Segment or NULL */ - gpointer data; /* Additional data, dependant of segment-type */ }; -typedef struct _path_curve PathCurve; - struct _path_curve { PathSegment * segments; /* The segments of the curve */ PathSegment * cur_segment; /* the current segment */ + Path * parent; /* the parent Path */ PathCurve * next; /* Next Curve or NULL */ PathCurve * prev; /* Previous Curve or NULL */ }; -typedef struct _path Path; - struct _path { PathCurve * curves; /* the curves */ PathCurve * cur_curve; /* the current curve */ GString * name; /* the name of the path */ guint32 state; /* is the path locked? */ + PathTool * path_tool; /* The parent Path Tool */ }; -typedef struct _path_tool PathTool; - struct _path_tool { gint click_pos; /* where did the user click? */ @@ -90,6 +91,14 @@ struct _path_tool PathCurve *click_curve; /* was the click? */ PathSegment *click_segment; + gint active_count; /* How many segments are active? */ + /* + * WARNING: single_active_segment may contain non NULL Values + * which point to the nirvana. But they are important! + * The pointer is garantueed to be valid, when active_count==1 + */ + PathSegment *single_active_segment; /* The only active segment */ + gint state; /* state of tool */ gint draw; /* all or part */ DrawCore *core; /* Core drawing object */ diff --git a/app/tools/tools.c b/app/tools/tools.c index b135d3b70a..d150017b4a 100644 --- a/app/tools/tools.c +++ b/app/tools/tools.c @@ -520,7 +520,7 @@ ToolInfo tool_info[] = 26, N_("/Tools/Path"), "", - (char **) measure_bits, + (char **) path_tool_bits, N_("Manipulate paths"), "ContextHelp/path", PATH_TOOL,