Code

* on-canvas clip and mask editing :) in the object menu you can find how to edit...
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include <glibmm/i18n.h>
23 #include "libnr/n-art-bpath.h"
24 #include "libnr/nr-path.h"
25 #include "helper/units.h"
26 #include "knot.h"
27 #include "inkscape.h"
28 #include "document.h"
29 #include "sp-namedview.h"
30 #include "desktop.h"
31 #include "desktop-handles.h"
32 #include "snap.h"
33 #include "message-stack.h"
34 #include "message-context.h"
35 #include "node-context.h"
36 #include "shape-editor.h"
37 #include "selection-chemistry.h"
38 #include "selection.h"
39 #include "xml/repr.h"
40 #include "prefs-utils.h"
41 #include "sp-metrics.h"
42 #include "sp-path.h"
43 #include "libnr/nr-matrix-ops.h"
44 #include "splivarot.h"
45 #include "svg/svg.h"
46 #include "verbs.h"
47 #include "display/bezier-utils.h"
48 #include <vector>
49 #include <algorithm>
50 #include <cstring>
51 #include <string>
52 #include "live_effects/lpeobject.h"
53 #include "live_effects/parameter/parameter.h"
54 #include "util/mathfns.h"
56 class NR::Matrix;
58 /// \todo
59 /// evil evil evil. FIXME: conflict of two different Path classes!
60 /// There is a conflict in the namespace between two classes named Path.
61 /// #include "sp-flowtext.h"
62 /// #include "sp-flowregion.h"
64 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
65 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
66 GType sp_flowregion_get_type (void);
67 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
68 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
69 GType sp_flowtext_get_type (void);
70 // end evil workaround
72 #include "helper/stlport.h"
75 /// \todo fixme: Implement these via preferences */
77 #define NODE_FILL          0xbfbfbf00
78 #define NODE_STROKE        0x000000ff
79 #define NODE_FILL_HI       0xff000000
80 #define NODE_STROKE_HI     0x000000ff
81 #define NODE_FILL_SEL      0x0000ffff
82 #define NODE_STROKE_SEL    0x000000ff
83 #define NODE_FILL_SEL_HI   0xff000000
84 #define NODE_STROKE_SEL_HI 0x000000ff
85 #define KNOT_FILL          0xffffffff
86 #define KNOT_STROKE        0x000000ff
87 #define KNOT_FILL_HI       0xff000000
88 #define KNOT_STROKE_HI     0x000000ff
90 static GMemChunk *nodechunk = NULL;
92 /* Creation from object */
94 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
95 static gchar *parse_nodetypes(gchar const *types, gint length);
97 /* Object updating */
99 static void stamp_repr(Inkscape::NodePath::Path *np);
100 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
101 static gchar *create_typestr(Inkscape::NodePath::Path *np);
103 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
105 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
107 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
109 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
111 /* Adjust handle placement, if the node or the other handle is moved */
112 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
113 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
115 /* Node event callbacks */
116 static void node_clicked(SPKnot *knot, guint state, gpointer data);
117 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
118 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
119 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
121 /* Handle event callbacks */
122 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
123 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
124 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
125 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
126 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
127 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
129 /* Constructors and destructors */
131 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
132 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
133 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
134 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
135 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
136                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
137 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
139 /* Helpers */
141 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
142 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
143 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
145 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
146 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
148 // active_node indicates mouseover node
149 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
151 /**
152  * \brief Creates new nodepath from item
153  */
154 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
156     Inkscape::XML::Node *repr = object->repr;
158     /** \todo
159      * FIXME: remove this. We don't want to edit paths inside flowtext.
160      * Instead we will build our flowtext with cloned paths, so that the
161      * real paths are outside the flowtext and thus editable as usual.
162      */
163     if (SP_IS_FLOWTEXT(object)) {
164         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
165             if SP_IS_FLOWREGION(child) {
166                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
167                 if (grandchild && SP_IS_PATH(grandchild)) {
168                     object = SP_ITEM(grandchild);
169                     break;
170                 }
171             }
172         }
173     }
175     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
177     if (curve == NULL)
178         return NULL;
180     NArtBpath *bpath = sp_curve_first_bpath(curve);
181     gint length = curve->end;
182     if (length == 0) {
183         sp_curve_unref(curve);
184         return NULL; // prevent crash for one-node paths
185     }
187     //Create new nodepath
188     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
189     if (!np) {
190         sp_curve_unref(curve);
191         return NULL;
192     }
194     // Set defaults
195     np->desktop     = desktop;
196     np->object      = object;
197     np->subpaths    = NULL;
198     np->selected    = NULL;
199     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
200     np->livarot_path = NULL;
201     np->local_change = 0;
202     np->show_handles = show_handles;
203     np->helper_path = NULL;
204     np->helperpath_rgba = 0xff0000ff;
205     np->helperpath_width = 1.0;
206     np->curve = sp_curve_copy(curve);
207     np->show_helperpath = false;
208     np->straight_path = false;
209     if (IS_LIVEPATHEFFECT(object) && item) {
210         np->item = item;
211     } else {
212         np->item = SP_ITEM(object);
213     }
215     // we need to update item's transform from the repr here,
216     // because they may be out of sync when we respond
217     // to a change in repr by regenerating nodepath     --bb
218     sp_object_read_attr(SP_OBJECT(np->item), "transform");
220     np->i2d  = sp_item_i2d_affine(np->item);
221     np->d2i  = np->i2d.inverse();
223     np->repr = repr;
224     if (repr_key_in) { // apparantly the object is an LPEObject
225         np->repr_key = g_strdup(repr_key_in);
226         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
227         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
228         if (lpeparam) {
229             lpeparam->param_setup_nodepath(np);
230         }
231     } else {
232         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
233         if ( SP_SHAPE(np->object)->path_effect_href ) {
234             np->repr_key = g_strdup("inkscape:original-d");
236             LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(np->object));
237             if (lpeobj && lpeobj->lpe) {
238                 lpeobj->lpe->setup_nodepath(np);
239             }
240         } else {
241             np->repr_key = g_strdup("d");
242         }
243     }
245     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
246     gchar *typestr = parse_nodetypes(nodetypes, length);
248     // create the subpath(s) from the bpath
249     NArtBpath *b = bpath;
250     while (b->code != NR_END) {
251         b = subpath_from_bpath(np, b, typestr + (b - bpath));
252     }
254     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
255     np->subpaths = g_list_reverse(np->subpaths);
257     g_free(typestr);
258     sp_curve_unref(curve);
260     // create the livarot representation from the same item
261     sp_nodepath_ensure_livarot_path(np);
263     // Draw helper curve
264     if (np->show_helperpath) {
265         SPCurve *helper_curve = sp_curve_copy(np->curve);
266         sp_curve_transform(helper_curve, np->i2d );
267         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
268         sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
269         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
270         sp_canvas_item_show(np->helper_path);
271         sp_curve_unref(helper_curve);
272     }
274     return np;
277 /**
278  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
279  */
280 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
282     if (!np)  //soft fail, like delete
283         return;
285     while (np->subpaths) {
286         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
287     }
289     //Inform the ShapeEditor that made me, if any, that I am gone.
290     if (np->shape_editor)
291         np->shape_editor->nodepath_destroyed();
293     g_assert(!np->selected);
295     if (np->livarot_path) {
296         delete np->livarot_path;
297         np->livarot_path = NULL;
298     }
300     if (np->helper_path) {
301         GtkObject *temp = np->helper_path;
302         np->helper_path = NULL;
303         gtk_object_destroy(temp);
304     }
305     if (np->curve) {
306         sp_curve_unref(np->curve);
307         np->curve = NULL;
308     }
310     if (np->repr_key) {
311         g_free(np->repr_key);
312         np->repr_key = NULL;
313     }
314     if (np->repr_nodetypes_key) {
315         g_free(np->repr_nodetypes_key);
316         np->repr_nodetypes_key = NULL;
317     }
319     np->desktop = NULL;
321     g_free(np);
325 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
327     if (np && np->livarot_path == NULL) {
328         SPCurve *curve = create_curve(np);
329         NArtBpath *bpath = SP_CURVE_BPATH(curve);
330         np->livarot_path = bpath_to_Path(bpath);
332         if (np->livarot_path)
333             np->livarot_path->ConvertWithBackData(0.01);
335         sp_curve_unref(curve);
336     }
340 /**
341  *  Return the node count of a given NodeSubPath.
342  */
343 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
345     if (!subpath)
346         return 0;
347     gint nodeCount = g_list_length(subpath->nodes);
348     return nodeCount;
351 /**
352  *  Return the node count of a given NodePath.
353  */
354 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
356     if (!np)
357         return 0;
358     gint nodeCount = 0;
359     for (GList *item = np->subpaths ; item ; item=item->next) {
360        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
361         nodeCount += g_list_length(subpath->nodes);
362     }
363     return nodeCount;
366 /**
367  *  Return the subpath count of a given NodePath.
368  */
369 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
371     if (!np)
372         return 0;
373     return g_list_length (np->subpaths);
376 /**
377  *  Return the selected node count of a given NodePath.
378  */
379 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
381     if (!np)
382         return 0;
383     return g_list_length (np->selected);
386 /**
387  *  Return the number of subpaths where nodes are selected in a given NodePath.
388  */
389 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
391     if (!np)
392         return 0;
393     if (!np->selected)
394         return 0;
395     if (!np->selected->next)
396         return 1;
397     gint count = 0;
398     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
399         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
400         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
401             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
402             if (node->selected) {
403                 count ++;
404                 break;
405             }
406         }
407     }
408     return count;
411 /**
412  * Clean up a nodepath after editing.
413  *
414  * Currently we are deleting trivial subpaths.
415  */
416 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
418     GList *badSubPaths = NULL;
420     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
421     for (GList *l = nodepath->subpaths; l ; l=l->next) {
422        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
423        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
424             badSubPaths = g_list_append(badSubPaths, sp);
425     }
427     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
428     //also removes the subpath from nodepath->subpaths
429     for (GList *l = badSubPaths; l ; l=l->next) {
430        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
431         sp_nodepath_subpath_destroy(sp);
432     }
434     g_list_free(badSubPaths);
437 /**
438  * Create new nodepath from b, make it subpath of np.
439  * \param t The node type.
440  * \todo Fixme: t should be a proper type, rather than gchar
441  */
442 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
444     NR::Point ppos, pos, npos;
446     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
448     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
449     bool const closed = (b->code == NR_MOVETO);
451     pos = NR::Point(b->x3, b->y3) * np->i2d;
452     if (b[1].code == NR_CURVETO) {
453         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
454     } else {
455         npos = pos;
456     }
457     Inkscape::NodePath::Node *n;
458     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
459     g_assert(sp->first == n);
460     g_assert(sp->last  == n);
462     b++;
463     t++;
464     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
465         pos = NR::Point(b->x3, b->y3) * np->i2d;
466         if (b->code == NR_CURVETO) {
467             ppos = NR::Point(b->x2, b->y2) * np->i2d;
468         } else {
469             ppos = pos;
470         }
471         if (b[1].code == NR_CURVETO) {
472             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
473         } else {
474             npos = pos;
475         }
476         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
477         b++;
478         t++;
479     }
481     if (closed) sp_nodepath_subpath_close(sp);
483     return b;
486 /**
487  * Convert from sodipodi:nodetypes to new style type string.
488  */
489 static gchar *parse_nodetypes(gchar const *types, gint length)
491     g_assert(length > 0);
493     gchar *typestr = g_new(gchar, length + 1);
495     gint pos = 0;
497     if (types) {
498         for (gint i = 0; types[i] && ( i < length ); i++) {
499             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
500             if (types[i] != '\0') {
501                 switch (types[i]) {
502                     case 's':
503                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
504                         break;
505                     case 'z':
506                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
507                         break;
508                     case 'c':
509                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
510                         break;
511                     default:
512                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
513                         break;
514                 }
515             }
516         }
517     }
519     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
521     return typestr;
524 /**
525  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
526  * updated but repr is not (for speed). Used during curve and node drag.
527  */
528 static void update_object(Inkscape::NodePath::Path *np)
530     g_assert(np);
532     sp_curve_unref(np->curve);
533     np->curve = create_curve(np);
535     sp_nodepath_set_curve(np, np->curve);
537     if (np->show_helperpath) {
538         SPCurve * helper_curve = sp_curve_copy(np->curve);
539         sp_curve_transform(helper_curve, np->i2d );
540         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
541         sp_curve_unref(helper_curve);
542     }
545 /**
546  * Update XML path node with data from path object.
547  */
548 static void update_repr_internal(Inkscape::NodePath::Path *np)
550     g_assert(np);
552     Inkscape::XML::Node *repr = np->object->repr;
554     sp_curve_unref(np->curve);
555     np->curve = create_curve(np);
557     gchar *typestr = create_typestr(np);
558     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
560     // determine if path has an effect applied and write to correct "d" attribute.
561     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
562         np->local_change++;
563         repr->setAttribute(np->repr_key, svgpath);
564     }
566     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
567         np->local_change++;
568         repr->setAttribute(np->repr_nodetypes_key, typestr);
569     }
571     g_free(svgpath);
572     g_free(typestr);
574     if (np->show_helperpath) {
575         SPCurve * helper_curve = sp_curve_copy(np->curve);
576         sp_curve_transform(helper_curve, np->i2d );
577         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
578         sp_curve_unref(helper_curve);
579     }
580  }
582 /**
583  * Update XML path node with data from path object, commit changes forever.
584  */
585 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
587     //fixme: np can be NULL, so check before proceeding
588     g_return_if_fail(np != NULL);
590     if (np->livarot_path) {
591         delete np->livarot_path;
592         np->livarot_path = NULL;
593     }
595     update_repr_internal(np);
596     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
598     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
599                      annotation);
602 /**
603  * Update XML path node with data from path object, commit changes with undo.
604  */
605 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
607     if (np->livarot_path) {
608         delete np->livarot_path;
609         np->livarot_path = NULL;
610     }
612     update_repr_internal(np);
613     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
614                            annotation);
617 /**
618  * Make duplicate of path, replace corresponding XML node in tree, commit.
619  */
620 static void stamp_repr(Inkscape::NodePath::Path *np)
622     g_assert(np);
624     Inkscape::XML::Node *old_repr = np->object->repr;
625     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
627     // remember the position of the item
628     gint pos = old_repr->position();
629     // remember parent
630     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
632     SPCurve *curve = create_curve(np);
633     gchar *typestr = create_typestr(np);
635     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
637     new_repr->setAttribute(np->repr_key, svgpath);
638     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
640     // add the new repr to the parent
641     parent->appendChild(new_repr);
642     // move to the saved position
643     new_repr->setPosition(pos > 0 ? pos : 0);
645     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
646                      _("Stamp"));
648     Inkscape::GC::release(new_repr);
649     g_free(svgpath);
650     g_free(typestr);
651     sp_curve_unref(curve);
654 /**
655  * Create curve from path.
656  */
657 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
659     SPCurve *curve = sp_curve_new();
661     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
662        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
663         sp_curve_moveto(curve,
664                         sp->first->pos * np->d2i);
665        Inkscape::NodePath::Node *n = sp->first->n.other;
666         while (n) {
667             NR::Point const end_pt = n->pos * np->d2i;
668             switch (n->code) {
669                 case NR_LINETO:
670                     sp_curve_lineto(curve, end_pt);
671                     break;
672                 case NR_CURVETO:
673                     sp_curve_curveto(curve,
674                                      n->p.other->n.pos * np->d2i,
675                                      n->p.pos * np->d2i,
676                                      end_pt);
677                     break;
678                 default:
679                     g_assert_not_reached();
680                     break;
681             }
682             if (n != sp->last) {
683                 n = n->n.other;
684             } else {
685                 n = NULL;
686             }
687         }
688         if (sp->closed) {
689             sp_curve_closepath(curve);
690         }
691     }
693     return curve;
696 /**
697  * Convert path type string to sodipodi:nodetypes style.
698  */
699 static gchar *create_typestr(Inkscape::NodePath::Path *np)
701     gchar *typestr = g_new(gchar, 32);
702     gint len = 32;
703     gint pos = 0;
705     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
706        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
708         if (pos >= len) {
709             typestr = g_renew(gchar, typestr, len + 32);
710             len += 32;
711         }
713         typestr[pos++] = 'c';
715        Inkscape::NodePath::Node *n;
716         n = sp->first->n.other;
717         while (n) {
718             gchar code;
720             switch (n->type) {
721                 case Inkscape::NodePath::NODE_CUSP:
722                     code = 'c';
723                     break;
724                 case Inkscape::NodePath::NODE_SMOOTH:
725                     code = 's';
726                     break;
727                 case Inkscape::NodePath::NODE_SYMM:
728                     code = 'z';
729                     break;
730                 default:
731                     g_assert_not_reached();
732                     code = '\0';
733                     break;
734             }
736             if (pos >= len) {
737                 typestr = g_renew(gchar, typestr, len + 32);
738                 len += 32;
739             }
741             typestr[pos++] = code;
743             if (n != sp->last) {
744                 n = n->n.other;
745             } else {
746                 n = NULL;
747             }
748         }
749     }
751     if (pos >= len) {
752         typestr = g_renew(gchar, typestr, len + 1);
753         len += 1;
754     }
756     typestr[pos++] = '\0';
758     return typestr;
761 /**
762  * Returns current path in context. // later eliminate this function at all!
763  */
764 static Inkscape::NodePath::Path *sp_nodepath_current()
766     if (!SP_ACTIVE_DESKTOP) {
767         return NULL;
768     }
770     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
772     if (!SP_IS_NODE_CONTEXT(event_context)) {
773         return NULL;
774     }
776     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
781 /**
782  \brief Fills node and handle positions for three nodes, splitting line
783   marked by end at distance t.
784  */
785 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
787     g_assert(new_path != NULL);
788     g_assert(end      != NULL);
790     g_assert(end->p.other == new_path);
791    Inkscape::NodePath::Node *start = new_path->p.other;
792     g_assert(start);
794     if (end->code == NR_LINETO) {
795         new_path->type =Inkscape::NodePath::NODE_CUSP;
796         new_path->code = NR_LINETO;
797         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
798     } else {
799         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
800         new_path->code = NR_CURVETO;
801         gdouble s      = 1 - t;
802         for (int dim = 0; dim < 2; dim++) {
803             NR::Coord const f000 = start->pos[dim];
804             NR::Coord const f001 = start->n.pos[dim];
805             NR::Coord const f011 = end->p.pos[dim];
806             NR::Coord const f111 = end->pos[dim];
807             NR::Coord const f00t = s * f000 + t * f001;
808             NR::Coord const f01t = s * f001 + t * f011;
809             NR::Coord const f11t = s * f011 + t * f111;
810             NR::Coord const f0tt = s * f00t + t * f01t;
811             NR::Coord const f1tt = s * f01t + t * f11t;
812             NR::Coord const fttt = s * f0tt + t * f1tt;
813             start->n.pos[dim]    = f00t;
814             new_path->p.pos[dim] = f0tt;
815             new_path->pos[dim]   = fttt;
816             new_path->n.pos[dim] = f1tt;
817             end->p.pos[dim]      = f11t;
818         }
819     }
822 /**
823  * Adds new node on direct line between two nodes, activates handles of all
824  * three nodes.
825  */
826 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
828     g_assert(end);
829     g_assert(end->subpath);
830     g_assert(g_list_find(end->subpath->nodes, end));
832    Inkscape::NodePath::Node *start = end->p.other;
833     g_assert( start->n.other == end );
834    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
835                                                end,
836                                                (NRPathcode)end->code == NR_LINETO?
837                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
838                                                (NRPathcode)end->code,
839                                                &start->pos, &start->pos, &start->n.pos);
840     sp_nodepath_line_midpoint(newnode, end, t);
842     sp_node_adjust_handles(start);
843     sp_node_update_handles(start);
844     sp_node_update_handles(newnode);
845     sp_node_adjust_handles(end);
846     sp_node_update_handles(end);
848     return newnode;
851 /**
852 \brief Break the path at the node: duplicate the argument node, start a new subpath with the duplicate, and copy all nodes after the argument node to it
853 */
854 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
856     g_assert(node);
857     g_assert(node->subpath);
858     g_assert(g_list_find(node->subpath->nodes, node));
860    Inkscape::NodePath::SubPath *sp = node->subpath;
861     Inkscape::NodePath::Path *np    = sp->nodepath;
863     if (sp->closed) {
864         sp_nodepath_subpath_open(sp, node);
865         return sp->first;
866     } else {
867         // no break for end nodes
868         if (node == sp->first) return NULL;
869         if (node == sp->last ) return NULL;
871         // create a new subpath
872        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
874         // duplicate the break node as start of the new subpath
875        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
877         while (node->n.other) { // copy the remaining nodes into the new subpath
878            Inkscape::NodePath::Node *n  = node->n.other;
879            Inkscape::NodePath::Node *nn = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
880             if (n->selected) {
881                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
882             }
883             sp_nodepath_node_destroy(n); // remove the point on the original subpath
884         }
886         return newnode;
887     }
890 /**
891  * Duplicate node and connect to neighbours.
892  */
893 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
895     g_assert(node);
896     g_assert(node->subpath);
897     g_assert(g_list_find(node->subpath->nodes, node));
899    Inkscape::NodePath::SubPath *sp = node->subpath;
901     NRPathcode code = (NRPathcode) node->code;
902     if (code == NR_MOVETO) { // if node is the endnode,
903         node->code = NR_LINETO; // new one is inserted before it, so change that to line
904     }
906     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
908     if (!node->n.other || !node->p.other) // if node is an endnode, select it
909         return node;
910     else
911         return newnode; // otherwise select the newly created node
914 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
916     node->p.pos = (node->pos + (node->pos - node->n.pos));
919 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
921     node->n.pos = (node->pos + (node->pos - node->p.pos));
924 /**
925  * Change line type at node, with side effects on neighbours.
926  */
927 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
929     g_assert(end);
930     g_assert(end->subpath);
931     g_assert(end->p.other);
933     if (end->code == static_cast< guint > ( code ) )
934         return;
936    Inkscape::NodePath::Node *start = end->p.other;
938     end->code = code;
940     if (code == NR_LINETO) {
941         if (start->code == NR_LINETO) {
942             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
943         }
944         if (end->n.other) {
945             if (end->n.other->code == NR_LINETO) {
946                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
947             }
948         }
949     } else {
950         NR::Point delta = end->pos - start->pos;
951         start->n.pos = start->pos + delta / 3;
952         end->p.pos = end->pos - delta / 3;
953         sp_node_adjust_handle(start, 1);
954         sp_node_adjust_handle(end, -1);
955     }
957     sp_node_update_handles(start);
958     sp_node_update_handles(end);
961 /**
962  * Change node type, and its handles accordingly.
963  */
964 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
966     g_assert(node);
967     g_assert(node->subpath);
969     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
970         return node;
972     if ((node->p.other != NULL) && (node->n.other != NULL)) {
973         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
974             type =Inkscape::NodePath::NODE_CUSP;
975         }
976     }
978     node->type = type;
980     if (node->type == Inkscape::NodePath::NODE_CUSP) {
981         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
982         node->knot->setSize (node->selected? 11 : 9);
983         sp_knot_update_ctrl(node->knot);
984     } else {
985         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
986         node->knot->setSize (node->selected? 9 : 7);
987         sp_knot_update_ctrl(node->knot);
988     }
990     // if one of handles is mouseovered, preserve its position
991     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
992         sp_node_adjust_handle(node, 1);
993     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
994         sp_node_adjust_handle(node, -1);
995     } else {
996         sp_node_adjust_handles(node);
997     }
999     sp_node_update_handles(node);
1001     sp_nodepath_update_statusbar(node->subpath->nodepath);
1003     return node;
1006 /**
1007  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
1008  * adjacent segments from lines to curves.
1009 */
1010 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1012     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
1013     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
1015     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1016         if (p_line && n_line) {
1017             // only if both adjacent segments are lines,
1018             // convert both to curves:
1020             node->code = NR_CURVETO;
1021             node->n.other->code = NR_CURVETO;
1023             NR::Point leg_prev = node->pos - node->p.other->pos;
1024             NR::Point leg_next = node->pos - node->n.other->pos;
1026             double norm_leg_prev = L2(leg_prev);
1027             double norm_leg_next = L2(leg_next);
1029             // delta has length 1 and is orthogonal to bisecting line
1030             NR::Point delta;
1031             if (norm_leg_next > 0.0) {
1032                 delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1033                 (&delta)->normalize();
1034             }
1036             if (type == Inkscape::NodePath::NODE_SYMM) {
1037                 double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1038                 node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1039                 node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1040             } else {
1041                 // length of handle is proportional to distance to adjacent node
1042                 node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1043                 node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1044             }
1046             sp_node_update_handles(node);
1047         }
1048     }
1050     sp_nodepath_set_node_type (node, type);
1053 /**
1054  * Move node to point, and adjust its and neighbouring handles.
1055  */
1056 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1058     NR::Point delta = p - node->pos;
1059     node->pos = p;
1061     node->p.pos += delta;
1062     node->n.pos += delta;
1064     Inkscape::NodePath::Node *node_p = NULL;
1065     Inkscape::NodePath::Node *node_n = NULL;
1067     if (node->p.other) {
1068         if (node->code == NR_LINETO) {
1069             sp_node_adjust_handle(node, 1);
1070             sp_node_adjust_handle(node->p.other, -1);
1071             node_p = node->p.other;
1072         }
1073     }
1074     if (node->n.other) {
1075         if (node->n.other->code == NR_LINETO) {
1076             sp_node_adjust_handle(node, -1);
1077             sp_node_adjust_handle(node->n.other, 1);
1078             node_n = node->n.other;
1079         }
1080     }
1082     // this function is only called from batch movers that will update display at the end
1083     // themselves, so here we just move all the knots without emitting move signals, for speed
1084     sp_node_update_handles(node, false);
1085     if (node_n) {
1086         sp_node_update_handles(node_n, false);
1087     }
1088     if (node_p) {
1089         sp_node_update_handles(node_p, false);
1090     }
1093 /**
1094  * Call sp_node_moveto() for node selection and handle possible snapping.
1095  */
1096 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1097                                             bool const snap = true)
1099     NR::Coord best = NR_HUGE;
1100     NR::Point delta(dx, dy);
1101     NR::Point best_pt = delta;
1103     if (snap) {
1104         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
1106         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1107             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1108             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item));
1109             if (s.getDistance() < best) {
1110                 best = s.getDistance();
1111                 best_pt = s.getPoint() - n->pos;
1112             }
1113         }
1114     }
1116     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1117         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1118         sp_node_moveto(n, n->pos + best_pt);
1119     }
1121     // do not update repr here so that node dragging is acceptably fast
1122     update_object(nodepath);
1125 /**
1126 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1127 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1128 near x = 0.
1129  */
1130 double
1131 sculpt_profile (double x, double alpha, guint profile)
1133     if (x >= 1)
1134         return 0;
1135     if (x <= 0)
1136         return 1;
1138     switch (profile) {
1139         case SCULPT_PROFILE_LINEAR:
1140         return 1 - x;
1141         case SCULPT_PROFILE_BELL:
1142         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1143         case SCULPT_PROFILE_ELLIPTIC:
1144         return sqrt(1 - x*x);
1145     }
1147     return 1;
1150 double
1151 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1153     // extremely primitive for now, don't have time to look for the real one
1154     double lower = NR::L2(b - a);
1155     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1156     return (lower + upper)/2;
1159 void
1160 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1162     n->pos = n->origin + delta;
1163     n->n.pos = n->n.origin + delta_n;
1164     n->p.pos = n->p.origin + delta_p;
1165     sp_node_adjust_handles(n);
1166     sp_node_update_handles(n, false);
1169 /**
1170  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1171  * on how far they are from the dragged node n.
1172  */
1173 static void
1174 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1176     g_assert (n);
1177     g_assert (nodepath);
1178     g_assert (n->subpath->nodepath == nodepath);
1180     double pressure = n->knot->pressure;
1181     if (pressure == 0)
1182         pressure = 0.5; // default
1183     pressure = CLAMP (pressure, 0.2, 0.8);
1185     // map pressure to alpha = 1/5 ... 5
1186     double alpha = 1 - 2 * fabs(pressure - 0.5);
1187     if (pressure > 0.5)
1188         alpha = 1/alpha;
1190     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1192     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1193         // Only one subpath has selected nodes:
1194         // use linear mode, where the distance from n to node being dragged is calculated along the path
1196         double n_sel_range = 0, p_sel_range = 0;
1197         guint n_nodes = 0, p_nodes = 0;
1198         guint n_sel_nodes = 0, p_sel_nodes = 0;
1200         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1201         {
1202             double n_range = 0, p_range = 0;
1203             bool n_going = true, p_going = true;
1204             Inkscape::NodePath::Node *n_node = n;
1205             Inkscape::NodePath::Node *p_node = n;
1206             do {
1207                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1208                 if (n_node && n_going)
1209                     n_node = n_node->n.other;
1210                 if (n_node == NULL) {
1211                     n_going = false;
1212                 } else {
1213                     n_nodes ++;
1214                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1215                     if (n_node->selected) {
1216                         n_sel_nodes ++;
1217                         n_sel_range = n_range;
1218                     }
1219                     if (n_node == p_node) {
1220                         n_going = false;
1221                         p_going = false;
1222                     }
1223                 }
1224                 if (p_node && p_going)
1225                     p_node = p_node->p.other;
1226                 if (p_node == NULL) {
1227                     p_going = false;
1228                 } else {
1229                     p_nodes ++;
1230                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1231                     if (p_node->selected) {
1232                         p_sel_nodes ++;
1233                         p_sel_range = p_range;
1234                     }
1235                     if (p_node == n_node) {
1236                         n_going = false;
1237                         p_going = false;
1238                     }
1239                 }
1240             } while (n_going || p_going);
1241         }
1243         // Second pass: actually move nodes in this subpath
1244         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1245         {
1246             double n_range = 0, p_range = 0;
1247             bool n_going = true, p_going = true;
1248             Inkscape::NodePath::Node *n_node = n;
1249             Inkscape::NodePath::Node *p_node = n;
1250             do {
1251                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1252                 if (n_node && n_going)
1253                     n_node = n_node->n.other;
1254                 if (n_node == NULL) {
1255                     n_going = false;
1256                 } else {
1257                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1258                     if (n_node->selected) {
1259                         sp_nodepath_move_node_and_handles (n_node,
1260                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1261                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1262                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1263                     }
1264                     if (n_node == p_node) {
1265                         n_going = false;
1266                         p_going = false;
1267                     }
1268                 }
1269                 if (p_node && p_going)
1270                     p_node = p_node->p.other;
1271                 if (p_node == NULL) {
1272                     p_going = false;
1273                 } else {
1274                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1275                     if (p_node->selected) {
1276                         sp_nodepath_move_node_and_handles (p_node,
1277                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1278                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1279                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1280                     }
1281                     if (p_node == n_node) {
1282                         n_going = false;
1283                         p_going = false;
1284                     }
1285                 }
1286             } while (n_going || p_going);
1287         }
1289     } else {
1290         // Multiple subpaths have selected nodes:
1291         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1292         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1293         // fix the pear-like shape when sculpting e.g. a ring
1295         // First pass: calculate range
1296         gdouble direct_range = 0;
1297         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1298             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1299             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1300                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1301                 if (node->selected) {
1302                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1303                 }
1304             }
1305         }
1307         // Second pass: actually move nodes
1308         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1309             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1310             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1311                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1312                 if (node->selected) {
1313                     if (direct_range > 1e-6) {
1314                         sp_nodepath_move_node_and_handles (node,
1315                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1316                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1317                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1318                     } else {
1319                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1320                     }
1322                 }
1323             }
1324         }
1325     }
1327     // do not update repr here so that node dragging is acceptably fast
1328     update_object(nodepath);
1332 /**
1333  * Move node selection to point, adjust its and neighbouring handles,
1334  * handle possible snapping, and commit the change with possible undo.
1335  */
1336 void
1337 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1339     if (!nodepath) return;
1341     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1343     if (dx == 0) {
1344         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1345     } else if (dy == 0) {
1346         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1347     } else {
1348         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1349     }
1352 /**
1353  * Move node selection off screen and commit the change.
1354  */
1355 void
1356 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1358     // borrowed from sp_selection_move_screen in selection-chemistry.c
1359     // we find out the current zoom factor and divide deltas by it
1360     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1362     gdouble zoom = desktop->current_zoom();
1363     gdouble zdx = dx / zoom;
1364     gdouble zdy = dy / zoom;
1366     if (!nodepath) return;
1368     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1370     if (dx == 0) {
1371         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1372     } else if (dy == 0) {
1373         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1374     } else {
1375         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1376     }
1379 /**
1380  * Move selected nodes to the absolute position given
1381  */
1382 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1384     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1385         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1386         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1387         sp_node_moveto(n, npos);
1388     }
1390     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1393 /**
1394  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1395  */
1396 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1398     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1399     g_return_val_if_fail(nodepath->selected, no_coord);
1401     // determine coordinate of first selected node
1402     GList *nsel = nodepath->selected;
1403     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1404     NR::Coord coord = n->pos[axis];
1405     bool coincide = true;
1407     // compare it to the coordinates of all the other selected nodes
1408     for (GList *l = nsel->next; l != NULL; l = l->next) {
1409         n = (Inkscape::NodePath::Node *) l->data;
1410         if (n->pos[axis] != coord) {
1411             coincide = false;
1412         }
1413     }
1414     if (coincide) {
1415         return coord;
1416     } else {
1417         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1418         // currently we return the coordinate of the bounding box midpoint because I don't know how
1419         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1420         return bbox.midpoint()[axis];
1421     }
1424 /** If they don't yet exist, creates knot and line for the given side of the node */
1425 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1427     if (!side->knot) {
1428         side->knot = sp_knot_new(desktop, _("<b>Node handle</b>: drag to shape the curve; with <b>Ctrl</b> to snap angle; with <b>Alt</b> to lock length; with <b>Shift</b> to rotate both handles"));
1430         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1431         side->knot->setSize (7);
1432         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1433         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1434         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1435         sp_knot_update_ctrl(side->knot);
1437         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1438         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1439         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1440         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1441         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1442         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1443     }
1445     if (!side->line) {
1446         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1447                                         SP_TYPE_CTRLLINE, NULL);
1448     }
1451 /**
1452  * Ensure the given handle of the node is visible/invisible, update its screen position
1453  */
1454 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1456     g_assert(node != NULL);
1458    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1459     NRPathcode code = sp_node_path_code_from_side(node, side);
1461     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1463     if (show_handle) {
1464         if (!side->knot) { // No handle knot at all
1465             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1466             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1467             side->knot->pos = side->pos;
1468             if (side->knot->item)
1469                 SP_CTRL(side->knot->item)->moveto(side->pos);
1470             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1471             sp_knot_show(side->knot);
1472         } else {
1473             if (side->knot->pos != side->pos) { // only if it's really moved
1474                 if (fire_move_signals) {
1475                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1476                 } else {
1477                     sp_knot_moveto(side->knot, &side->pos);
1478                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1479                 }
1480             }
1481             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1482                 sp_knot_show(side->knot);
1483             }
1484         }
1485         sp_canvas_item_show(side->line);
1486     } else {
1487         if (side->knot) {
1488             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1489                 sp_knot_hide(side->knot);
1490             }
1491         }
1492         if (side->line) {
1493             sp_canvas_item_hide(side->line);
1494         }
1495     }
1498 /**
1499  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1500  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1501  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1502  * updated; otherwise, just move the knots silently (used in batch moves).
1503  */
1504 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1506     g_assert(node != NULL);
1508     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1509         sp_knot_show(node->knot);
1510     }
1512     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1513         if (fire_move_signals)
1514             sp_knot_set_position(node->knot, &node->pos, 0);
1515         else
1516             sp_knot_moveto(node->knot, &node->pos);
1517     }
1519     gboolean show_handles = node->selected;
1520     if (node->p.other != NULL) {
1521         if (node->p.other->selected) show_handles = TRUE;
1522     }
1523     if (node->n.other != NULL) {
1524         if (node->n.other->selected) show_handles = TRUE;
1525     }
1527     if (node->subpath->nodepath->show_handles == false)
1528         show_handles = FALSE;
1530     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1531     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1534 /**
1535  * Call sp_node_update_handles() for all nodes on subpath.
1536  */
1537 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1539     g_assert(subpath != NULL);
1541     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1542         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1543     }
1546 /**
1547  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1548  */
1549 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1551     g_assert(nodepath != NULL);
1553     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1554         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1555     }
1558 void
1559 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1561     if (nodepath == NULL) return;
1563     nodepath->show_handles = show;
1564     sp_nodepath_update_handles(nodepath);
1567 /**
1568  * Adds all selected nodes in nodepath to list.
1569  */
1570 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1572     StlConv<Node *>::list(l, selected);
1573 /// \todo this adds a copying, rework when the selection becomes a stl list
1576 /**
1577  * Align selected nodes on the specified axis.
1578  */
1579 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1581     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1582         return;
1583     }
1585     if ( !nodepath->selected->next ) { // only one node selected
1586         return;
1587     }
1588    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1589     NR::Point dest(pNode->pos);
1590     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1591         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1592         if (pNode) {
1593             dest[axis] = pNode->pos[axis];
1594             sp_node_moveto(pNode, dest);
1595         }
1596     }
1598     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1601 /// Helper struct.
1602 struct NodeSort
1604    Inkscape::NodePath::Node *_node;
1605     NR::Coord _coord;
1606     /// \todo use vectorof pointers instead of calling copy ctor
1607     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1608         _node(node), _coord(node->pos[axis])
1609     {}
1611 };
1613 static bool operator<(NodeSort const &a, NodeSort const &b)
1615     return (a._coord < b._coord);
1618 /**
1619  * Distribute selected nodes on the specified axis.
1620  */
1621 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1623     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1624         return;
1625     }
1627     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1628         return;
1629     }
1631    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1632     std::vector<NodeSort> sorted;
1633     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1634         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1635         if (pNode) {
1636             NodeSort n(pNode, axis);
1637             sorted.push_back(n);
1638             //dest[axis] = pNode->pos[axis];
1639             //sp_node_moveto(pNode, dest);
1640         }
1641     }
1642     std::sort(sorted.begin(), sorted.end());
1643     unsigned int len = sorted.size();
1644     //overall bboxes span
1645     float dist = (sorted.back()._coord -
1646                   sorted.front()._coord);
1647     //new distance between each bbox
1648     float step = (dist) / (len - 1);
1649     float pos = sorted.front()._coord;
1650     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1651           it < sorted.end();
1652           it ++ )
1653     {
1654         NR::Point dest((*it)._node->pos);
1655         dest[axis] = pos;
1656         sp_node_moveto((*it)._node, dest);
1657         pos += step;
1658     }
1660     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1664 /**
1665  * Call sp_nodepath_line_add_node() for all selected segments.
1666  */
1667 void
1668 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1670     if (!nodepath) {
1671         return;
1672     }
1674     GList *nl = NULL;
1676     int n_added = 0;
1678     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1679        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1680         g_assert(t->selected);
1681         if (t->p.other && t->p.other->selected) {
1682             nl = g_list_prepend(nl, t);
1683         }
1684     }
1686     while (nl) {
1687        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1688        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1689        sp_nodepath_node_select(n, TRUE, FALSE);
1690        n_added ++;
1691        nl = g_list_remove(nl, t);
1692     }
1694     /** \todo fixme: adjust ? */
1695     sp_nodepath_update_handles(nodepath);
1697     if (n_added > 1) {
1698         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1699     } else if (n_added > 0) {
1700         sp_nodepath_update_repr(nodepath, _("Add node"));
1701     }
1703     sp_nodepath_update_statusbar(nodepath);
1706 /**
1707  * Select segment nearest to point
1708  */
1709 void
1710 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1712     if (!nodepath) {
1713         return;
1714     }
1716     sp_nodepath_ensure_livarot_path(nodepath);
1717     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1718     if (!maybe_position) {
1719         return;
1720     }
1721     Path::cut_position position = *maybe_position;
1723     //find segment to segment
1724     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1726     //fixme: this can return NULL, so check before proceeding.
1727     g_return_if_fail(e != NULL);
1729     gboolean force = FALSE;
1730     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1731         force = TRUE;
1732     }
1733     sp_nodepath_node_select(e, (gboolean) toggle, force);
1734     if (e->p.other)
1735         sp_nodepath_node_select(e->p.other, TRUE, force);
1737     sp_nodepath_update_handles(nodepath);
1739     sp_nodepath_update_statusbar(nodepath);
1742 /**
1743  * Add a node nearest to point
1744  */
1745 void
1746 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1748     if (!nodepath) {
1749         return;
1750     }
1752     sp_nodepath_ensure_livarot_path(nodepath);
1753     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1754     if (!maybe_position) {
1755         return;
1756     }
1757     Path::cut_position position = *maybe_position;
1759     //find segment to split
1760     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1762     //don't know why but t seems to flip for lines
1763     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1764         position.t = 1.0 - position.t;
1765     }
1766     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1767     sp_nodepath_node_select(n, FALSE, TRUE);
1769     /* fixme: adjust ? */
1770     sp_nodepath_update_handles(nodepath);
1772     sp_nodepath_update_repr(nodepath, _("Add node"));
1774     sp_nodepath_update_statusbar(nodepath);
1777 /*
1778  * Adjusts a segment so that t moves by a certain delta for dragging
1779  * converts lines to curves
1780  *
1781  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1782  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1783  */
1784 void
1785 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1787     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1789     //fixme: e and e->p can be NULL, so check for those before proceeding
1790     g_return_if_fail(e != NULL);
1791     g_return_if_fail(&e->p != NULL);
1793     /* feel good is an arbitrary parameter that distributes the delta between handles
1794      * if t of the drag point is less than 1/6 distance form the endpoint only
1795      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1796      */
1797     double feel_good;
1798     if (t <= 1.0 / 6.0)
1799         feel_good = 0;
1800     else if (t <= 0.5)
1801         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1802     else if (t <= 5.0 / 6.0)
1803         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1804     else
1805         feel_good = 1;
1807     //if we're dragging a line convert it to a curve
1808     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1809         sp_nodepath_set_line_type(e, NR_CURVETO);
1810     }
1812     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1813     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1814     e->p.other->n.pos += offsetcoord0;
1815     e->p.pos += offsetcoord1;
1817     // adjust handles of adjacent nodes where necessary
1818     sp_node_adjust_handle(e,1);
1819     sp_node_adjust_handle(e->p.other,-1);
1821     sp_nodepath_update_handles(e->subpath->nodepath);
1823     update_object(e->subpath->nodepath);
1825     sp_nodepath_update_statusbar(e->subpath->nodepath);
1829 /**
1830  * Call sp_nodepath_break() for all selected segments.
1831  */
1832 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1834     if (!nodepath) return;
1836     GList *temp = NULL;
1837     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1838        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1839        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1840         if (nn == NULL) continue; // no break, no new node
1841         temp = g_list_prepend(temp, nn);
1842     }
1844     if (temp) {
1845         sp_nodepath_deselect(nodepath);
1846     }
1847     for (GList *l = temp; l != NULL; l = l->next) {
1848         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1849     }
1851     sp_nodepath_update_handles(nodepath);
1853     sp_nodepath_update_repr(nodepath, _("Break path"));
1856 /**
1857  * Duplicate the selected node(s).
1858  */
1859 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1861     if (!nodepath) {
1862         return;
1863     }
1865     GList *temp = NULL;
1866     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1867        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1868        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1869         if (nn == NULL) continue; // could not duplicate
1870         temp = g_list_prepend(temp, nn);
1871     }
1873     if (temp) {
1874         sp_nodepath_deselect(nodepath);
1875     }
1876     for (GList *l = temp; l != NULL; l = l->next) {
1877         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1878     }
1880     sp_nodepath_update_handles(nodepath);
1882     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1885 /**
1886  *  Join two nodes by merging them into one.
1887  */
1888 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
1890     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1892     if (g_list_length(nodepath->selected) != 2) {
1893         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1894         return;
1895     }
1897    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1898    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1900     g_assert(a != b);
1901     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
1902         // someone tried to join an orphan node (i.e. a single-node subpath).
1903         // this is not worth an error message, just fail silently.
1904         return;
1905     }
1907     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1908         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1909         return;
1910     }
1912     /* a and b are endpoints */
1914     NR::Point c;
1915     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1916         c = a->pos;
1917     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1918         c = b->pos;
1919     } else {
1920         c = (a->pos + b->pos) / 2;
1921     }
1923     if (a->subpath == b->subpath) {
1924        Inkscape::NodePath::SubPath *sp = a->subpath;
1925         sp_nodepath_subpath_close(sp);
1926         sp_node_moveto (sp->first, c);
1928         sp_nodepath_update_handles(sp->nodepath);
1929         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1930         return;
1931     }
1933     /* a and b are separate subpaths */
1934    Inkscape::NodePath::SubPath *sa = a->subpath;
1935    Inkscape::NodePath::SubPath *sb = b->subpath;
1936     NR::Point p;
1937    Inkscape::NodePath::Node *n;
1938     NRPathcode code;
1939     if (a == sa->first) {
1940         p = sa->first->n.pos;
1941         code = (NRPathcode)sa->first->n.other->code;
1942        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1943         n = sa->last;
1944         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1945         n = n->p.other;
1946         while (n) {
1947             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1948             n = n->p.other;
1949             if (n == sa->first) n = NULL;
1950         }
1951         sp_nodepath_subpath_destroy(sa);
1952         sa = t;
1953     } else if (a == sa->last) {
1954         p = sa->last->p.pos;
1955         code = (NRPathcode)sa->last->code;
1956         sp_nodepath_node_destroy(sa->last);
1957     } else {
1958         code = NR_END;
1959         g_assert_not_reached();
1960     }
1962     if (b == sb->first) {
1963         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1964         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1965             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1966         }
1967     } else if (b == sb->last) {
1968         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1969         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1970             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1971         }
1972     } else {
1973         g_assert_not_reached();
1974     }
1975     /* and now destroy sb */
1977     sp_nodepath_subpath_destroy(sb);
1979     sp_nodepath_update_handles(sa->nodepath);
1981     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1983     sp_nodepath_update_statusbar(nodepath);
1986 /**
1987  *  Join two nodes by adding a segment between them.
1988  */
1989 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
1991     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1993     if (g_list_length(nodepath->selected) != 2) {
1994         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1995         return;
1996     }
1998    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1999    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2001     g_assert(a != b);
2002     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2003         // someone tried to join an orphan node (i.e. a single-node subpath).
2004         // this is not worth an error message, just fail silently.
2005         return;
2006     }
2008     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2009         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2010         return;
2011     }
2013     if (a->subpath == b->subpath) {
2014        Inkscape::NodePath::SubPath *sp = a->subpath;
2016         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2017         sp->closed = TRUE;
2019         sp->first->p.other = sp->last;
2020         sp->last->n.other  = sp->first;
2022         sp_node_handle_mirror_p_to_n(sp->last);
2023         sp_node_handle_mirror_n_to_p(sp->first);
2025         sp->first->code = sp->last->code;
2026         sp->first       = sp->last;
2028         sp_nodepath_update_handles(sp->nodepath);
2030         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2032         return;
2033     }
2035     /* a and b are separate subpaths */
2036    Inkscape::NodePath::SubPath *sa = a->subpath;
2037    Inkscape::NodePath::SubPath *sb = b->subpath;
2039    Inkscape::NodePath::Node *n;
2040     NR::Point p;
2041     NRPathcode code;
2042     if (a == sa->first) {
2043         code = (NRPathcode) sa->first->n.other->code;
2044        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2045         n = sa->last;
2046         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2047         for (n = n->p.other; n != NULL; n = n->p.other) {
2048             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2049         }
2050         sp_nodepath_subpath_destroy(sa);
2051         sa = t;
2052     } else if (a == sa->last) {
2053         code = (NRPathcode)sa->last->code;
2054     } else {
2055         code = NR_END;
2056         g_assert_not_reached();
2057     }
2059     if (b == sb->first) {
2060         n = sb->first;
2061         sp_node_handle_mirror_p_to_n(sa->last);
2062         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2063         sp_node_handle_mirror_n_to_p(sa->last);
2064         for (n = n->n.other; n != NULL; n = n->n.other) {
2065             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2066         }
2067     } else if (b == sb->last) {
2068         n = sb->last;
2069         sp_node_handle_mirror_p_to_n(sa->last);
2070         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2071         sp_node_handle_mirror_n_to_p(sa->last);
2072         for (n = n->p.other; n != NULL; n = n->p.other) {
2073             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2074         }
2075     } else {
2076         g_assert_not_reached();
2077     }
2078     /* and now destroy sb */
2080     sp_nodepath_subpath_destroy(sb);
2082     sp_nodepath_update_handles(sa->nodepath);
2084     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2087 /**
2088  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2089  */
2090 void sp_node_delete_preserve(GList *nodes_to_delete)
2092     GSList *nodepaths = NULL;
2094     while (nodes_to_delete) {
2095         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2096         Inkscape::NodePath::SubPath *sp = node->subpath;
2097         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2098         Inkscape::NodePath::Node *sample_cursor = NULL;
2099         Inkscape::NodePath::Node *sample_end = NULL;
2100         Inkscape::NodePath::Node *delete_cursor = node;
2101         bool just_delete = false;
2103         //find the start of this contiguous selection
2104         //move left to the first node that is not selected
2105         //or the start of the non-closed path
2106         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2107             delete_cursor = curr;
2108         }
2110         //just delete at the beginning of an open path
2111         if (!delete_cursor->p.other) {
2112             sample_cursor = delete_cursor;
2113             just_delete = true;
2114         } else {
2115             sample_cursor = delete_cursor->p.other;
2116         }
2118         //calculate points for each segment
2119         int rate = 5;
2120         float period = 1.0 / rate;
2121         std::vector<NR::Point> data;
2122         if (!just_delete) {
2123             data.push_back(sample_cursor->pos);
2124             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2125                 //just delete at the end of an open path
2126                 if (!sp->closed && curr == sp->last) {
2127                     just_delete = true;
2128                     break;
2129                 }
2131                 //sample points on the contiguous selected segment
2132                 NR::Point *bez;
2133                 bez = new NR::Point [4];
2134                 bez[0] = curr->pos;
2135                 bez[1] = curr->n.pos;
2136                 bez[2] = curr->n.other->p.pos;
2137                 bez[3] = curr->n.other->pos;
2138                 for (int i=1; i<rate; i++) {
2139                     gdouble t = i * period;
2140                     NR::Point p = bezier_pt(3, bez, t);
2141                     data.push_back(p);
2142                 }
2143                 data.push_back(curr->n.other->pos);
2145                 sample_end = curr->n.other;
2146                 //break if we've come full circle or hit the end of the selection
2147                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2148                     break;
2149                 }
2150             }
2151         }
2153         if (!just_delete) {
2154             //calculate the best fitting single segment and adjust the endpoints
2155             NR::Point *adata;
2156             adata = new NR::Point [data.size()];
2157             copy(data.begin(), data.end(), adata);
2159             NR::Point *bez;
2160             bez = new NR::Point [4];
2161             //would decreasing error create a better fitting approximation?
2162             gdouble error = 1.0;
2163             gint ret;
2164             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2166             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2167             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2168             //the resulting nodes behave as expected.
2169             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2170             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2172             //adjust endpoints
2173             sample_cursor->n.pos = bez[1];
2174             sample_end->p.pos = bez[2];
2175         }
2177         //destroy this contiguous selection
2178         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2179             Inkscape::NodePath::Node *temp = delete_cursor;
2180             if (delete_cursor->n.other == delete_cursor) {
2181                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2182                 delete_cursor = NULL;
2183             } else {
2184                 delete_cursor = delete_cursor->n.other;
2185             }
2186             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2187             sp_nodepath_node_destroy(temp);
2188         }
2190         sp_nodepath_update_handles(nodepath);
2192         if (!g_slist_find(nodepaths, nodepath))
2193             nodepaths = g_slist_prepend (nodepaths, nodepath);
2194     }
2196     for (GSList *i = nodepaths; i; i = i->next) {
2197         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2198         // different nodepaths will give us one undo event per nodepath
2199         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2201         // if the entire nodepath is removed, delete the selected object.
2202         if (nodepath->subpaths == NULL ||
2203             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2204             //at least 2
2205             sp_nodepath_get_node_count(nodepath) < 2) {
2206             SPDocument *document = sp_desktop_document (nodepath->desktop);
2207             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2208             //delete this nodepath's object, not the entire selection! (though at this time, this
2209             //does not matter)
2210             sp_selection_delete();
2211             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2212                               _("Delete nodes"));
2213         } else {
2214             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2215             sp_nodepath_update_statusbar(nodepath);
2216         }
2217     }
2219     g_slist_free (nodepaths);
2222 /**
2223  * Delete one or more selected nodes.
2224  */
2225 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2227     if (!nodepath) return;
2228     if (!nodepath->selected) return;
2230     /** \todo fixme: do it the right way */
2231     while (nodepath->selected) {
2232        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2233         sp_nodepath_node_destroy(node);
2234     }
2237     //clean up the nodepath (such as for trivial subpaths)
2238     sp_nodepath_cleanup(nodepath);
2240     sp_nodepath_update_handles(nodepath);
2242     // if the entire nodepath is removed, delete the selected object.
2243     if (nodepath->subpaths == NULL ||
2244         sp_nodepath_get_node_count(nodepath) < 2) {
2245         SPDocument *document = sp_desktop_document (nodepath->desktop);
2246         sp_selection_delete();
2247         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2248                           _("Delete nodes"));
2249         return;
2250     }
2252     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2254     sp_nodepath_update_statusbar(nodepath);
2257 /**
2258  * Delete one or more segments between two selected nodes.
2259  * This is the code for 'split'.
2260  */
2261 void
2262 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2264    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2265    Inkscape::NodePath::Node *curr, *next;     //Iterators
2267     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2269     if (g_list_length(nodepath->selected) != 2) {
2270         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2271                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2272         return;
2273     }
2275     //Selected nodes, not inclusive
2276    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2277    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2279     if ( ( a==b)                       ||  //same node
2280          (a->subpath  != b->subpath )  ||  //not the same path
2281          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2282          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2283     {
2284         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2285                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2286         return;
2287     }
2289     //###########################################
2290     //# BEGIN EDITS
2291     //###########################################
2292     //##################################
2293     //# CLOSED PATH
2294     //##################################
2295     if (a->subpath->closed) {
2298         gboolean reversed = FALSE;
2300         //Since we can go in a circle, we need to find the shorter distance.
2301         //  a->b or b->a
2302         start = end = NULL;
2303         int distance    = 0;
2304         int minDistance = 0;
2305         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2306             if (curr==b) {
2307                 //printf("a to b:%d\n", distance);
2308                 start = a;//go from a to b
2309                 end   = b;
2310                 minDistance = distance;
2311                 //printf("A to B :\n");
2312                 break;
2313             }
2314             distance++;
2315         }
2317         //try again, the other direction
2318         distance = 0;
2319         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2320             if (curr==a) {
2321                 //printf("b to a:%d\n", distance);
2322                 if (distance < minDistance) {
2323                     start    = b;  //we go from b to a
2324                     end      = a;
2325                     reversed = TRUE;
2326                     //printf("B to A\n");
2327                 }
2328                 break;
2329             }
2330             distance++;
2331         }
2334         //Copy everything from 'end' to 'start' to a new subpath
2335        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2336         for (curr=end ; curr ; curr=curr->n.other) {
2337             NRPathcode code = (NRPathcode) curr->code;
2338             if (curr == end)
2339                 code = NR_MOVETO;
2340             sp_nodepath_node_new(t, NULL,
2341                                  (Inkscape::NodePath::NodeType)curr->type, code,
2342                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2343             if (curr == start)
2344                 break;
2345         }
2346         sp_nodepath_subpath_destroy(a->subpath);
2349     }
2353     //##################################
2354     //# OPEN PATH
2355     //##################################
2356     else {
2358         //We need to get the direction of the list between A and B
2359         //Can we walk from a to b?
2360         start = end = NULL;
2361         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2362             if (curr==b) {
2363                 start = a;  //did it!  we go from a to b
2364                 end   = b;
2365                 //printf("A to B\n");
2366                 break;
2367             }
2368         }
2369         if (!start) {//didn't work?  let's try the other direction
2370             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2371                 if (curr==a) {
2372                     start = b;  //did it!  we go from b to a
2373                     end   = a;
2374                     //printf("B to A\n");
2375                     break;
2376                 }
2377             }
2378         }
2379         if (!start) {
2380             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2381                                                      _("Cannot find path between nodes."));
2382             return;
2383         }
2387         //Copy everything after 'end' to a new subpath
2388        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2389         for (curr=end ; curr ; curr=curr->n.other) {
2390             NRPathcode code = (NRPathcode) curr->code;
2391             if (curr == end)
2392                 code = NR_MOVETO;
2393             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2394                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2395         }
2397         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2398         for (curr = start->n.other ; curr  ; curr=next) {
2399             next = curr->n.other;
2400             sp_nodepath_node_destroy(curr);
2401         }
2403     }
2404     //###########################################
2405     //# END EDITS
2406     //###########################################
2408     //clean up the nodepath (such as for trivial subpaths)
2409     sp_nodepath_cleanup(nodepath);
2411     sp_nodepath_update_handles(nodepath);
2413     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2415     sp_nodepath_update_statusbar(nodepath);
2418 /**
2419  * Call sp_nodepath_set_line() for all selected segments.
2420  */
2421 void
2422 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2424     if (nodepath == NULL) return;
2426     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2427        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2428         g_assert(n->selected);
2429         if (n->p.other && n->p.other->selected) {
2430             sp_nodepath_set_line_type(n, code);
2431         }
2432     }
2434     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2437 /**
2438  * Call sp_nodepath_convert_node_type() for all selected nodes.
2439  */
2440 void
2441 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2443     if (nodepath == NULL) return;
2445     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2447     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2448         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2449     }
2451     sp_nodepath_update_repr(nodepath, _("Change node type"));
2454 /**
2455  * Change select status of node, update its own and neighbour handles.
2456  */
2457 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2459     node->selected = selected;
2461     if (selected) {
2462         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2463         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2464         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2465         sp_knot_update_ctrl(node->knot);
2466     } else {
2467         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2468         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2469         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2470         sp_knot_update_ctrl(node->knot);
2471     }
2473     sp_node_update_handles(node);
2474     if (node->n.other) sp_node_update_handles(node->n.other);
2475     if (node->p.other) sp_node_update_handles(node->p.other);
2478 /**
2479 \brief Select a node
2480 \param node     The node to select
2481 \param incremental   If true, add to selection, otherwise deselect others
2482 \param override   If true, always select this node, otherwise toggle selected status
2483 */
2484 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2486     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2488     if (incremental) {
2489         if (override) {
2490             if (!g_list_find(nodepath->selected, node)) {
2491                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2492             }
2493             sp_node_set_selected(node, TRUE);
2494         } else { // toggle
2495             if (node->selected) {
2496                 g_assert(g_list_find(nodepath->selected, node));
2497                 nodepath->selected = g_list_remove(nodepath->selected, node);
2498             } else {
2499                 g_assert(!g_list_find(nodepath->selected, node));
2500                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2501             }
2502             sp_node_set_selected(node, !node->selected);
2503         }
2504     } else {
2505         sp_nodepath_deselect(nodepath);
2506         nodepath->selected = g_list_prepend(nodepath->selected, node);
2507         sp_node_set_selected(node, TRUE);
2508     }
2510     sp_nodepath_update_statusbar(nodepath);
2514 /**
2515 \brief Deselect all nodes in the nodepath
2516 */
2517 void
2518 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2520     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2522     while (nodepath->selected) {
2523         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2524         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2525     }
2526     sp_nodepath_update_statusbar(nodepath);
2529 /**
2530 \brief Select or invert selection of all nodes in the nodepath
2531 */
2532 void
2533 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2535     if (!nodepath) return;
2537     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2538        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2539         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2540            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2541            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2542         }
2543     }
2546 /**
2547  * If nothing selected, does the same as sp_nodepath_select_all();
2548  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2549  * (i.e., similar to "select all in layer", with the "selected" subpaths
2550  * being treated as "layers" in the path).
2551  */
2552 void
2553 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2555     if (!nodepath) return;
2557     if (g_list_length (nodepath->selected) == 0) {
2558         sp_nodepath_select_all (nodepath, invert);
2559         return;
2560     }
2562     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2563     GSList *subpaths = NULL;
2565     for (GList *l = copy; l != NULL; l = l->next) {
2566         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2567         Inkscape::NodePath::SubPath *subpath = n->subpath;
2568         if (!g_slist_find (subpaths, subpath))
2569             subpaths = g_slist_prepend (subpaths, subpath);
2570     }
2572     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2573         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2574         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2575             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2576             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2577         }
2578     }
2580     g_slist_free (subpaths);
2581     g_list_free (copy);
2584 /**
2585  * \brief Select the node after the last selected; if none is selected,
2586  * select the first within path.
2587  */
2588 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2590     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2592    Inkscape::NodePath::Node *last = NULL;
2593     if (nodepath->selected) {
2594         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2595            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2596             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2597             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2598                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2599                 if (node->selected) {
2600                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2601                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2602                             if (spl->next) { // there's a next subpath
2603                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2604                                 last = subpath_next->first;
2605                             } else if (spl->prev) { // there's a previous subpath
2606                                 last = NULL; // to be set later to the first node of first subpath
2607                             } else {
2608                                 last = node->n.other;
2609                             }
2610                         } else {
2611                             last = node->n.other;
2612                         }
2613                     } else {
2614                         if (node->n.other) {
2615                             last = node->n.other;
2616                         } else {
2617                             if (spl->next) { // there's a next subpath
2618                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2619                                 last = subpath_next->first;
2620                             } else if (spl->prev) { // there's a previous subpath
2621                                 last = NULL; // to be set later to the first node of first subpath
2622                             } else {
2623                                 last = (Inkscape::NodePath::Node *) subpath->first;
2624                             }
2625                         }
2626                     }
2627                 }
2628             }
2629         }
2630         sp_nodepath_deselect(nodepath);
2631     }
2633     if (last) { // there's at least one more node after selected
2634         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2635     } else { // no more nodes, select the first one in first subpath
2636        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2637         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2638     }
2641 /**
2642  * \brief Select the node before the first selected; if none is selected,
2643  * select the last within path
2644  */
2645 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2647     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2649    Inkscape::NodePath::Node *last = NULL;
2650     if (nodepath->selected) {
2651         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2652            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2653             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2654                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2655                 if (node->selected) {
2656                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2657                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2658                             if (spl->prev) { // there's a prev subpath
2659                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2660                                 last = subpath_prev->last;
2661                             } else if (spl->next) { // there's a next subpath
2662                                 last = NULL; // to be set later to the last node of last subpath
2663                             } else {
2664                                 last = node->p.other;
2665                             }
2666                         } else {
2667                             last = node->p.other;
2668                         }
2669                     } else {
2670                         if (node->p.other) {
2671                             last = node->p.other;
2672                         } else {
2673                             if (spl->prev) { // there's a prev subpath
2674                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2675                                 last = subpath_prev->last;
2676                             } else if (spl->next) { // there's a next subpath
2677                                 last = NULL; // to be set later to the last node of last subpath
2678                             } else {
2679                                 last = (Inkscape::NodePath::Node *) subpath->last;
2680                             }
2681                         }
2682                     }
2683                 }
2684             }
2685         }
2686         sp_nodepath_deselect(nodepath);
2687     }
2689     if (last) { // there's at least one more node before selected
2690         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2691     } else { // no more nodes, select the last one in last subpath
2692         GList *spl = g_list_last(nodepath->subpaths);
2693        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2694         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2695     }
2698 /**
2699  * \brief Select all nodes that are within the rectangle.
2700  */
2701 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2703     if (!incremental) {
2704         sp_nodepath_deselect(nodepath);
2705     }
2707     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2708        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2709         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2710            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2712             if (b.contains(node->pos)) {
2713                 sp_nodepath_node_select(node, TRUE, TRUE);
2714             }
2715         }
2716     }
2720 void
2721 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2723     g_assert (n);
2724     g_assert (nodepath);
2725     g_assert (n->subpath->nodepath == nodepath);
2727     if (g_list_length (nodepath->selected) == 0) {
2728         if (grow > 0) {
2729             sp_nodepath_node_select(n, TRUE, TRUE);
2730         }
2731         return;
2732     }
2734     if (g_list_length (nodepath->selected) == 1) {
2735         if (grow < 0) {
2736             sp_nodepath_deselect (nodepath);
2737             return;
2738         }
2739     }
2741         double n_sel_range = 0, p_sel_range = 0;
2742             Inkscape::NodePath::Node *farthest_n_node = n;
2743             Inkscape::NodePath::Node *farthest_p_node = n;
2745         // Calculate ranges
2746         {
2747             double n_range = 0, p_range = 0;
2748             bool n_going = true, p_going = true;
2749             Inkscape::NodePath::Node *n_node = n;
2750             Inkscape::NodePath::Node *p_node = n;
2751             do {
2752                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2753                 if (n_node && n_going)
2754                     n_node = n_node->n.other;
2755                 if (n_node == NULL) {
2756                     n_going = false;
2757                 } else {
2758                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2759                     if (n_node->selected) {
2760                         n_sel_range = n_range;
2761                         farthest_n_node = n_node;
2762                     }
2763                     if (n_node == p_node) {
2764                         n_going = false;
2765                         p_going = false;
2766                     }
2767                 }
2768                 if (p_node && p_going)
2769                     p_node = p_node->p.other;
2770                 if (p_node == NULL) {
2771                     p_going = false;
2772                 } else {
2773                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2774                     if (p_node->selected) {
2775                         p_sel_range = p_range;
2776                         farthest_p_node = p_node;
2777                     }
2778                     if (p_node == n_node) {
2779                         n_going = false;
2780                         p_going = false;
2781                     }
2782                 }
2783             } while (n_going || p_going);
2784         }
2786     if (grow > 0) {
2787         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2788                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2789         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2790                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2791         }
2792     } else {
2793         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2794                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2795         } else if (farthest_p_node && farthest_p_node->selected) {
2796                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2797         }
2798     }
2801 void
2802 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2804     g_assert (n);
2805     g_assert (nodepath);
2806     g_assert (n->subpath->nodepath == nodepath);
2808     if (g_list_length (nodepath->selected) == 0) {
2809         if (grow > 0) {
2810             sp_nodepath_node_select(n, TRUE, TRUE);
2811         }
2812         return;
2813     }
2815     if (g_list_length (nodepath->selected) == 1) {
2816         if (grow < 0) {
2817             sp_nodepath_deselect (nodepath);
2818             return;
2819         }
2820     }
2822     Inkscape::NodePath::Node *farthest_selected = NULL;
2823     double farthest_dist = 0;
2825     Inkscape::NodePath::Node *closest_unselected = NULL;
2826     double closest_dist = NR_HUGE;
2828     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2829        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2830         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2831            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2832            if (node == n)
2833                continue;
2834            if (node->selected) {
2835                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2836                    farthest_dist = NR::L2(node->pos - n->pos);
2837                    farthest_selected = node;
2838                }
2839            } else {
2840                if (NR::L2(node->pos - n->pos) < closest_dist) {
2841                    closest_dist = NR::L2(node->pos - n->pos);
2842                    closest_unselected = node;
2843                }
2844            }
2845         }
2846     }
2848     if (grow > 0) {
2849         if (closest_unselected) {
2850             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2851         }
2852     } else {
2853         if (farthest_selected) {
2854             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2855         }
2856     }
2860 /**
2861 \brief  Saves all nodes' and handles' current positions in their origin members
2862 */
2863 void
2864 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2866     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2867        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2868         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2869            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2870            n->origin = n->pos;
2871            n->p.origin = n->p.pos;
2872            n->n.origin = n->n.pos;
2873         }
2874     }
2877 /**
2878 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2879 */
2880 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2882     if (!nodepath->selected) {
2883         return NULL;
2884     }
2886     GList *r = NULL;
2887     guint i = 0;
2888     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2889        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2890         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2891            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2892             i++;
2893             if (node->selected) {
2894                 r = g_list_append(r, GINT_TO_POINTER(i));
2895             }
2896         }
2897     }
2898     return r;
2901 /**
2902 \brief  Restores selection by selecting nodes whose positions are in the list
2903 */
2904 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2906     sp_nodepath_deselect(nodepath);
2908     guint i = 0;
2909     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2910        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2911         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2912            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2913             i++;
2914             if (g_list_find(r, GINT_TO_POINTER(i))) {
2915                 sp_nodepath_node_select(node, TRUE, TRUE);
2916             }
2917         }
2918     }
2922 /**
2923 \brief Adjusts handle according to node type and line code.
2924 */
2925 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2927     double len, otherlen, linelen;
2929     g_assert(node);
2931    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2932    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2934     /** \todo fixme: */
2935     if (me->other == NULL) return;
2936     if (other->other == NULL) return;
2938     /* I have line */
2940     NRPathcode mecode, ocode;
2941     if (which_adjust == 1) {
2942         mecode = (NRPathcode)me->other->code;
2943         ocode = (NRPathcode)node->code;
2944     } else {
2945         mecode = (NRPathcode)node->code;
2946         ocode = (NRPathcode)other->other->code;
2947     }
2949     if (mecode == NR_LINETO) return;
2951     /* I am curve */
2953     if (other->other == NULL) return;
2955     /* Other has line */
2957     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2959     NR::Point delta;
2960     if (ocode == NR_LINETO) {
2961         /* other is lineto, we are either smooth or symm */
2962        Inkscape::NodePath::Node *othernode = other->other;
2963         len = NR::L2(me->pos - node->pos);
2964         delta = node->pos - othernode->pos;
2965         linelen = NR::L2(delta);
2966         if (linelen < 1e-18)
2967             return;
2968         me->pos = node->pos + (len / linelen)*delta;
2969         return;
2970     }
2972     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2974         me->pos = 2 * node->pos - other->pos;
2975         return;
2976     }
2978     /* We are smooth */
2980     len = NR::L2(me->pos - node->pos);
2981     delta = other->pos - node->pos;
2982     otherlen = NR::L2(delta);
2983     if (otherlen < 1e-18) return;
2985     me->pos = node->pos - (len / otherlen) * delta;
2988 /**
2989  \brief Adjusts both handles according to node type and line code
2990  */
2991 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2993     g_assert(node);
2995     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2997     /* we are either smooth or symm */
2999     if (node->p.other == NULL) return;
3001     if (node->n.other == NULL) return;
3003     if (node->code == NR_LINETO) {
3004         if (node->n.other->code == NR_LINETO) return;
3005         sp_node_adjust_handle(node, 1);
3006         return;
3007     }
3009     if (node->n.other->code == NR_LINETO) {
3010         if (node->code == NR_LINETO) return;
3011         sp_node_adjust_handle(node, -1);
3012         return;
3013     }
3015     /* both are curves */
3016     NR::Point const delta( node->n.pos - node->p.pos );
3018     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3019         node->p.pos = node->pos - delta / 2;
3020         node->n.pos = node->pos + delta / 2;
3021         return;
3022     }
3024     /* We are smooth */
3025     double plen = NR::L2(node->p.pos - node->pos);
3026     if (plen < 1e-18) return;
3027     double nlen = NR::L2(node->n.pos - node->pos);
3028     if (nlen < 1e-18) return;
3029     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3030     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3033 /**
3034  * Node event callback.
3035  */
3036 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3038     gboolean ret = FALSE;
3039     switch (event->type) {
3040         case GDK_ENTER_NOTIFY:
3041             Inkscape::NodePath::Path::active_node = n;
3042             break;
3043         case GDK_LEAVE_NOTIFY:
3044             Inkscape::NodePath::Path::active_node = NULL;
3045             break;
3046         case GDK_SCROLL:
3047             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3048                 switch (event->scroll.direction) {
3049                     case GDK_SCROLL_UP:
3050                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3051                         break;
3052                     case GDK_SCROLL_DOWN:
3053                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3054                         break;
3055                     default:
3056                         break;
3057                 }
3058                 ret = TRUE;
3059             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3060                 switch (event->scroll.direction) {
3061                     case GDK_SCROLL_UP:
3062                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3063                         break;
3064                     case GDK_SCROLL_DOWN:
3065                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3066                         break;
3067                     default:
3068                         break;
3069                 }
3070                 ret = TRUE;
3071             }
3072             break;
3073         case GDK_KEY_PRESS:
3074             switch (get_group0_keyval (&event->key)) {
3075                 case GDK_space:
3076                     if (event->key.state & GDK_BUTTON1_MASK) {
3077                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3078                         stamp_repr(nodepath);
3079                         ret = TRUE;
3080                     }
3081                     break;
3082                 case GDK_Page_Up:
3083                     if (event->key.state & GDK_CONTROL_MASK) {
3084                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3085                     } else {
3086                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3087                     }
3088                     break;
3089                 case GDK_Page_Down:
3090                     if (event->key.state & GDK_CONTROL_MASK) {
3091                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3092                     } else {
3093                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3094                     }
3095                     break;
3096                 default:
3097                     break;
3098             }
3099             break;
3100         default:
3101             break;
3102     }
3104     return ret;
3107 /**
3108  * Handle keypress on node; directly called.
3109  */
3110 gboolean node_key(GdkEvent *event)
3112     Inkscape::NodePath::Path *np;
3114     // there is no way to verify nodes so set active_node to nil when deleting!!
3115     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3117     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3118         gint ret = FALSE;
3119         switch (get_group0_keyval (&event->key)) {
3120             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3121             case GDK_BackSpace:
3122                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3123                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3124                 sp_nodepath_update_repr(np, _("Delete node"));
3125                 Inkscape::NodePath::Path::active_node = NULL;
3126                 ret = TRUE;
3127                 break;
3128             case GDK_c:
3129                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3130                 ret = TRUE;
3131                 break;
3132             case GDK_s:
3133                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3134                 ret = TRUE;
3135                 break;
3136             case GDK_y:
3137                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3138                 ret = TRUE;
3139                 break;
3140             case GDK_b:
3141                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3142                 ret = TRUE;
3143                 break;
3144         }
3145         return ret;
3146     }
3147     return FALSE;
3150 /**
3151  * Mouseclick on node callback.
3152  */
3153 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3155    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3157     if (state & GDK_CONTROL_MASK) {
3158         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3160         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3161             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3162                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3163             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3164                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3165             } else {
3166                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3167             }
3168             sp_nodepath_update_repr(nodepath, _("Change node type"));
3169             sp_nodepath_update_statusbar(nodepath);
3171         } else { //ctrl+alt+click: delete node
3172             GList *node_to_delete = NULL;
3173             node_to_delete = g_list_append(node_to_delete, n);
3174             sp_node_delete_preserve(node_to_delete);
3175         }
3177     } else {
3178         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3179     }
3182 /**
3183  * Mouse grabbed node callback.
3184  */
3185 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3187    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3189     if (!n->selected) {
3190         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3191     }
3193     n->is_dragging = true;
3194     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3196     sp_nodepath_remember_origins (n->subpath->nodepath);
3199 /**
3200  * Mouse ungrabbed node callback.
3201  */
3202 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3204    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3206    n->dragging_out = NULL;
3207    n->is_dragging = false;
3208    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3210    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3213 /**
3214  * The point on a line, given by its angle, closest to the given point.
3215  * \param p  A point.
3216  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3217  * \param closest  Pointer to the point struct where the result is stored.
3218  * \todo FIXME: use dot product perhaps?
3219  */
3220 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3222     if (a == HUGE_VAL) { // vertical
3223         *closest = NR::Point(0, (*p)[NR::Y]);
3224     } else {
3225         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3226         (*closest)[NR::Y] = a * (*closest)[NR::X];
3227     }
3230 /**
3231  * Distance from the point to a line given by its angle.
3232  * \param p  A point.
3233  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3234  */
3235 static double point_line_distance(NR::Point *p, double a)
3237     NR::Point c;
3238     point_line_closest(p, a, &c);
3239     return sqrt(((*p)[NR::X] - c[NR::X])*((*p)[NR::X] - c[NR::X]) + ((*p)[NR::Y] - c[NR::Y])*((*p)[NR::Y] - c[NR::Y]));
3242 /**
3243  * Callback for node "request" signal.
3244  * \todo fixme: This goes to "moved" event? (lauris)
3245  */
3246 static gboolean
3247 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3249     double yn, xn, yp, xp;
3250     double an, ap, na, pa;
3251     double d_an, d_ap, d_na, d_pa;
3252     gboolean collinear = FALSE;
3253     NR::Point c;
3254     NR::Point pr;
3256    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3258    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3259     if ( (!n->subpath->nodepath->straight_path) &&
3260          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3261            || n->dragging_out ) )
3262     {
3263        NR::Point mouse = (*p);
3265        if (!n->dragging_out) {
3266            // This is the first drag-out event; find out which handle to drag out
3267            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3268            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3270            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3271                return FALSE;
3273            Inkscape::NodePath::NodeSide *opposite;
3274            if (appr_p > appr_n) { // closer to p
3275                n->dragging_out = &n->p;
3276                opposite = &n->n;
3277                n->code = NR_CURVETO;
3278            } else if (appr_p < appr_n) { // closer to n
3279                n->dragging_out = &n->n;
3280                opposite = &n->p;
3281                n->n.other->code = NR_CURVETO;
3282            } else { // p and n nodes are the same
3283                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3284                    n->dragging_out = &n->p;
3285                    opposite = &n->n;
3286                    n->code = NR_CURVETO;
3287                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3288                    n->dragging_out = &n->n;
3289                    opposite = &n->p;
3290                    n->n.other->code = NR_CURVETO;
3291                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3292                    double appr_other_n = (n->n.other ? NR::L2(n->n.other->n.pos - n->pos) - NR::L2(n->n.other->n.pos - (*p)) : -HUGE_VAL);
3293                    double appr_other_p = (n->n.other ? NR::L2(n->n.other->p.pos - n->pos) - NR::L2(n->n.other->p.pos - (*p)) : -HUGE_VAL);
3294                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3295                        n->dragging_out = &n->n;
3296                        opposite = &n->p;
3297                        n->n.other->code = NR_CURVETO;
3298                    } else { // closer to other's n handle
3299                        n->dragging_out = &n->p;
3300                        opposite = &n->n;
3301                        n->code = NR_CURVETO;
3302                    }
3303                }
3304            }
3306            // if there's another handle, make sure the one we drag out starts parallel to it
3307            if (opposite->pos != n->pos) {
3308                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3309            }
3311            // knots might not be created yet!
3312            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3313            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3314        }
3316        // pass this on to the handle-moved callback
3317        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3318        sp_node_update_handles(n);
3319        return TRUE;
3320    }
3322     if (state & GDK_CONTROL_MASK) { // constrained motion
3324         // calculate relative distances of handles
3325         // n handle:
3326         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3327         xn = n->n.pos[NR::X] - n->pos[NR::X];
3328         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3329         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3330             if (n->n.other) { // if there is the next point
3331                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3332                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3333                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3334             }
3335         }
3336         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3337         if (yn < 0) { xn = -xn; yn = -yn; }
3339         // p handle:
3340         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3341         xp = n->p.pos[NR::X] - n->pos[NR::X];
3342         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3343         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3344             if (n->p.other) {
3345                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3346                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3347                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3348             }
3349         }
3350         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3351         if (yp < 0) { xp = -xp; yp = -yp; }
3353         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3354             // sliding on handles, only if at least one of the handles is non-vertical
3355             // (otherwise it's the same as ctrl+drag anyway)
3357             // calculate angles of the handles
3358             if (xn == 0) {
3359                 if (yn == 0) { // no handle, consider it the continuation of the other one
3360                     an = 0;
3361                     collinear = TRUE;
3362                 }
3363                 else an = 0; // vertical; set the angle to horizontal
3364             } else an = yn/xn;
3366             if (xp == 0) {
3367                 if (yp == 0) { // no handle, consider it the continuation of the other one
3368                     ap = an;
3369                 }
3370                 else ap = 0; // vertical; set the angle to horizontal
3371             } else  ap = yp/xp;
3373             if (collinear) an = ap;
3375             // angles of the perpendiculars; HUGE_VAL means vertical
3376             if (an == 0) na = HUGE_VAL; else na = -1/an;
3377             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3379             // mouse point relative to the node's original pos
3380             pr = (*p) - n->origin;
3382             // distances to the four lines (two handles and two perpendiculars)
3383             d_an = point_line_distance(&pr, an);
3384             d_na = point_line_distance(&pr, na);
3385             d_ap = point_line_distance(&pr, ap);
3386             d_pa = point_line_distance(&pr, pa);
3388             // find out which line is the closest, save its closest point in c
3389             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3390                 point_line_closest(&pr, an, &c);
3391             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3392                 point_line_closest(&pr, ap, &c);
3393             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3394                 point_line_closest(&pr, na, &c);
3395             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3396                 point_line_closest(&pr, pa, &c);
3397             }
3399             // move the node to the closest point
3400             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3401                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3402                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3404         } else {  // constraining to hor/vert
3406             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3407                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3408             } else { // snap to vert
3409                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3410             }
3411         }
3412     } else { // move freely
3413         if (n->is_dragging) {
3414             if (state & GDK_MOD1_MASK) { // sculpt
3415                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3416             } else {
3417                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3418                                             (*p)[NR::X] - n->pos[NR::X],
3419                                             (*p)[NR::Y] - n->pos[NR::Y],
3420                                             (state & GDK_SHIFT_MASK) == 0);
3421             }
3422         }
3423     }
3425     n->subpath->nodepath->desktop->scroll_to_point(p);
3427     return TRUE;
3430 /**
3431  * Node handle clicked callback.
3432  */
3433 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3435    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3437     if (state & GDK_CONTROL_MASK) { // "delete" handle
3438         if (n->p.knot == knot) {
3439             n->p.pos = n->pos;
3440         } else if (n->n.knot == knot) {
3441             n->n.pos = n->pos;
3442         }
3443         sp_node_update_handles(n);
3444         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3445         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3446         sp_nodepath_update_statusbar(nodepath);
3448     } else { // just select or add to selection, depending in Shift
3449         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3450     }
3453 /**
3454  * Node handle grabbed callback.
3455  */
3456 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3458    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3460     if (!n->selected) {
3461         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3462     }
3464     // remember the origin point of the handle
3465     if (n->p.knot == knot) {
3466         n->p.origin_radial = n->p.pos - n->pos;
3467     } else if (n->n.knot == knot) {
3468         n->n.origin_radial = n->n.pos - n->pos;
3469     } else {
3470         g_assert_not_reached();
3471     }
3473     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3476 /**
3477  * Node handle ungrabbed callback.
3478  */
3479 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3481    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3483     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3484     if (n->p.knot == knot) {
3485         n->p.origin_radial.a = 0;
3486         sp_knot_set_position(knot, &n->p.pos, state);
3487     } else if (n->n.knot == knot) {
3488         n->n.origin_radial.a = 0;
3489         sp_knot_set_position(knot, &n->n.pos, state);
3490     } else {
3491         g_assert_not_reached();
3492     }
3494     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3497 /**
3498  * Node handle "request" signal callback.
3499  */
3500 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3502     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3504     Inkscape::NodePath::NodeSide *me, *opposite;
3505     gint which;
3506     if (n->p.knot == knot) {
3507         me = &n->p;
3508         opposite = &n->n;
3509         which = -1;
3510     } else if (n->n.knot == knot) {
3511         me = &n->n;
3512         opposite = &n->p;
3513         which = 1;
3514     } else {
3515         me = opposite = NULL;
3516         which = 0;
3517         g_assert_not_reached();
3518     }
3520     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3522     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3524     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3525         /* We are smooth node adjacent with line */
3526         NR::Point const delta = *p - n->pos;
3527         NR::Coord const len = NR::L2(delta);
3528         Inkscape::NodePath::Node *othernode = opposite->other;
3529         NR::Point const ndelta = n->pos - othernode->pos;
3530         NR::Coord const linelen = NR::L2(ndelta);
3531         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3532             NR::Coord const scal = dot(delta, ndelta) / linelen;
3533             (*p) = n->pos + (scal / linelen) * ndelta;
3534         }
3535         *p = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item).getPoint();
3536     } else {
3537         *p = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item).getPoint();
3538     }
3540     sp_node_adjust_handle(n, -which);
3542     return FALSE;
3545 /**
3546  * Node handle moved callback.
3547  */
3548 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3550    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3552    Inkscape::NodePath::NodeSide *me;
3553    Inkscape::NodePath::NodeSide *other;
3554     if (n->p.knot == knot) {
3555         me = &n->p;
3556         other = &n->n;
3557     } else if (n->n.knot == knot) {
3558         me = &n->n;
3559         other = &n->p;
3560     } else {
3561         me = NULL;
3562         other = NULL;
3563         g_assert_not_reached();
3564     }
3566     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3567     Radial rme(me->pos - n->pos);
3568     Radial rother(other->pos - n->pos);
3569     Radial rnew(*p - n->pos);
3571     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3572         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3573         /* 0 interpreted as "no snapping". */
3575         // The closest PI/snaps angle, starting from zero.
3576         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3577         if (me->origin_radial.a == HUGE_VAL) {
3578             // ortho doesn't exist: original handle was zero length.
3579             rnew.a = a_snapped;
3580         } else {
3581             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3582              * its opposite and perpendiculars). */
3583             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3585             // Snap to the closest.
3586             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3587                        ? a_snapped
3588                        : a_ortho );
3589         }
3590     }
3592     if (state & GDK_MOD1_MASK) {
3593         // lock handle length
3594         rnew.r = me->origin_radial.r;
3595     }
3597     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3598         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3599         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3600         rother.a += rnew.a - rme.a;
3601         other->pos = NR::Point(rother) + n->pos;
3602         if (other->knot) {
3603             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3604             sp_knot_moveto(other->knot, &other->pos);
3605         }
3606     }
3608     me->pos = NR::Point(rnew) + n->pos;
3609     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3611     // move knot, but without emitting the signal:
3612     // we cannot emit a "moved" signal because we're now processing it
3613     sp_knot_moveto(me->knot, &(me->pos));
3615     update_object(n->subpath->nodepath);
3617     /* status text */
3618     SPDesktop *desktop = n->subpath->nodepath->desktop;
3619     if (!desktop) return;
3620     SPEventContext *ec = desktop->event_context;
3621     if (!ec) return;
3622     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3623     if (!mc) return;
3625     double degrees = 180 / M_PI * rnew.a;
3626     if (degrees > 180) degrees -= 360;
3627     if (degrees < -180) degrees += 360;
3628     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3629         degrees = angle_to_compass (degrees);
3631     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3633     mc->setF(Inkscape::NORMAL_MESSAGE,
3634          _("<b>Node handle</b>: angle %0.2f&#176;, length %s; with <b>Ctrl</b> to snap angle; with <b>Alt</b> to lock length; with <b>Shift</b> to rotate both handles"), degrees, length->str);
3636     g_string_free(length, TRUE);
3639 /**
3640  * Node handle event callback.
3641  */
3642 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3644     gboolean ret = FALSE;
3645     switch (event->type) {
3646         case GDK_KEY_PRESS:
3647             switch (get_group0_keyval (&event->key)) {
3648                 case GDK_space:
3649                     if (event->key.state & GDK_BUTTON1_MASK) {
3650                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3651                         stamp_repr(nodepath);
3652                         ret = TRUE;
3653                     }
3654                     break;
3655                 default:
3656                     break;
3657             }
3658             break;
3659         case GDK_ENTER_NOTIFY:
3660             // we use an experimentally determined threshold that seems to work fine
3661             if (NR::L2(n->pos - knot->pos) < 0.75)
3662                 Inkscape::NodePath::Path::active_node = n;
3663             break;
3664         case GDK_LEAVE_NOTIFY:
3665             // we use an experimentally determined threshold that seems to work fine
3666             if (NR::L2(n->pos - knot->pos) < 0.75)
3667                 Inkscape::NodePath::Path::active_node = NULL;
3668             break;
3669         default:
3670             break;
3671     }
3673     return ret;
3676 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3677                                  Radial &rme, Radial &rother, gboolean const both)
3679     rme.a += angle;
3680     if ( both
3681          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3682          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3683     {
3684         rother.a += angle;
3685     }
3688 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3689                                         Radial &rme, Radial &rother, gboolean const both)
3691     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3693     gdouble r;
3694     if ( both
3695          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3696          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3697     {
3698         r = MAX(rme.r, rother.r);
3699     } else {
3700         r = rme.r;
3701     }
3703     gdouble const weird_angle = atan2(norm_angle, r);
3704 /* Bulia says norm_angle is just the visible distance that the
3705  * object's end must travel on the screen.  Left as 'angle' for want of
3706  * a better name.*/
3708     rme.a += weird_angle;
3709     if ( both
3710          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3711          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3712     {
3713         rother.a += weird_angle;
3714     }
3717 /**
3718  * Rotate one node.
3719  */
3720 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3722     Inkscape::NodePath::NodeSide *me, *other;
3723     bool both = false;
3725     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3726     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3728     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3729         me = &(n->p);
3730         other = &(n->n);
3731     } else if (!n->p.other) {
3732         me = &(n->n);
3733         other = &(n->p);
3734     } else {
3735         if (which > 0) { // right handle
3736             if (xn > xp) {
3737                 me = &(n->n);
3738                 other = &(n->p);
3739             } else {
3740                 me = &(n->p);
3741                 other = &(n->n);
3742             }
3743         } else if (which < 0){ // left handle
3744             if (xn <= xp) {
3745                 me = &(n->n);
3746                 other = &(n->p);
3747             } else {
3748                 me = &(n->p);
3749                 other = &(n->n);
3750             }
3751         } else { // both handles
3752             me = &(n->n);
3753             other = &(n->p);
3754             both = true;
3755         }
3756     }
3758     Radial rme(me->pos - n->pos);
3759     Radial rother(other->pos - n->pos);
3761     if (screen) {
3762         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3763     } else {
3764         node_rotate_one_internal (*n, angle, rme, rother, both);
3765     }
3767     me->pos = n->pos + NR::Point(rme);
3769     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3770         other->pos =  n->pos + NR::Point(rother);
3771     }
3773     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3774     // so here we just move all the knots without emitting move signals, for speed
3775     sp_node_update_handles(n, false);
3778 /**
3779  * Rotate selected nodes.
3780  */
3781 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3783     if (!nodepath || !nodepath->selected) return;
3785     if (g_list_length(nodepath->selected) == 1) {
3786        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3787         node_rotate_one (n, angle, which, screen);
3788     } else {
3789        // rotate as an object:
3791         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3792         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3793         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3794             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3795             box.expandTo (n->pos); // contain all selected nodes
3796         }
3798         gdouble rot;
3799         if (screen) {
3800             gdouble const zoom = nodepath->desktop->current_zoom();
3801             gdouble const zmove = angle / zoom;
3802             gdouble const r = NR::L2(box.max() - box.midpoint());
3803             rot = atan2(zmove, r);
3804         } else {
3805             rot = angle;
3806         }
3808         NR::Point rot_center;
3809         if (Inkscape::NodePath::Path::active_node == NULL)
3810             rot_center = box.midpoint();
3811         else
3812             rot_center = Inkscape::NodePath::Path::active_node->pos;
3814         NR::Matrix t =
3815             NR::Matrix (NR::translate(-rot_center)) *
3816             NR::Matrix (NR::rotate(rot)) *
3817             NR::Matrix (NR::translate(rot_center));
3819         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3820             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3821             n->pos *= t;
3822             n->n.pos *= t;
3823             n->p.pos *= t;
3824             sp_node_update_handles(n, false);
3825         }
3826     }
3828     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3831 /**
3832  * Scale one node.
3833  */
3834 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3836     bool both = false;
3837     Inkscape::NodePath::NodeSide *me, *other;
3839     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3840     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3842     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3843         me = &(n->p);
3844         other = &(n->n);
3845         n->code = NR_CURVETO;
3846     } else if (!n->p.other) {
3847         me = &(n->n);
3848         other = &(n->p);
3849         if (n->n.other)
3850             n->n.other->code = NR_CURVETO;
3851     } else {
3852         if (which > 0) { // right handle
3853             if (xn > xp) {
3854                 me = &(n->n);
3855                 other = &(n->p);
3856                 if (n->n.other)
3857                     n->n.other->code = NR_CURVETO;
3858             } else {
3859                 me = &(n->p);
3860                 other = &(n->n);
3861                 n->code = NR_CURVETO;
3862             }
3863         } else if (which < 0){ // left handle
3864             if (xn <= xp) {
3865                 me = &(n->n);
3866                 other = &(n->p);
3867                 if (n->n.other)
3868                     n->n.other->code = NR_CURVETO;
3869             } else {
3870                 me = &(n->p);
3871                 other = &(n->n);
3872                 n->code = NR_CURVETO;
3873             }
3874         } else { // both handles
3875             me = &(n->n);
3876             other = &(n->p);
3877             both = true;
3878             n->code = NR_CURVETO;
3879             if (n->n.other)
3880                 n->n.other->code = NR_CURVETO;
3881         }
3882     }
3884     Radial rme(me->pos - n->pos);
3885     Radial rother(other->pos - n->pos);
3887     rme.r += grow;
3888     if (rme.r < 0) rme.r = 0;
3889     if (rme.a == HUGE_VAL) {
3890         if (me->other) { // if direction is unknown, initialize it towards the next node
3891             Radial rme_next(me->other->pos - n->pos);
3892             rme.a = rme_next.a;
3893         } else { // if there's no next, initialize to 0
3894             rme.a = 0;
3895         }
3896     }
3897     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3898         rother.r += grow;
3899         if (rother.r < 0) rother.r = 0;
3900         if (rother.a == HUGE_VAL) {
3901             rother.a = rme.a + M_PI;
3902         }
3903     }
3905     me->pos = n->pos + NR::Point(rme);
3907     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3908         other->pos = n->pos + NR::Point(rother);
3909     }
3911     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3912     // so here we just move all the knots without emitting move signals, for speed
3913     sp_node_update_handles(n, false);
3916 /**
3917  * Scale selected nodes.
3918  */
3919 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3921     if (!nodepath || !nodepath->selected) return;
3923     if (g_list_length(nodepath->selected) == 1) {
3924         // scale handles of the single selected node
3925         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3926         node_scale_one (n, grow, which);
3927     } else {
3928         // scale nodes as an "object":
3930         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3931         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3932         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3933             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3934             box.expandTo (n->pos); // contain all selected nodes
3935         }
3937         double scale = (box.maxExtent() + grow)/box.maxExtent();
3939         NR::Point scale_center;
3940         if (Inkscape::NodePath::Path::active_node == NULL)
3941             scale_center = box.midpoint();
3942         else
3943             scale_center = Inkscape::NodePath::Path::active_node->pos;
3945         NR::Matrix t =
3946             NR::Matrix (NR::translate(-scale_center)) *
3947             NR::Matrix (NR::scale(scale, scale)) *
3948             NR::Matrix (NR::translate(scale_center));
3950         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3951             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3952             n->pos *= t;
3953             n->n.pos *= t;
3954             n->p.pos *= t;
3955             sp_node_update_handles(n, false);
3956         }
3957     }
3959     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3962 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3964     if (!nodepath) return;
3965     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3968 /**
3969  * Flip selected nodes horizontally/vertically.
3970  */
3971 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
3973     if (!nodepath || !nodepath->selected) return;
3975     if (g_list_length(nodepath->selected) == 1 && !center) {
3976         // flip handles of the single selected node
3977         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3978         double temp = n->p.pos[axis];
3979         n->p.pos[axis] = n->n.pos[axis];
3980         n->n.pos[axis] = temp;
3981         sp_node_update_handles(n, false);
3982     } else {
3983         // scale nodes as an "object":
3985         NR::Rect box = sp_node_selected_bbox (nodepath);
3986         if (!center) {
3987             center = box.midpoint();
3988         }
3989         NR::Matrix t =
3990             NR::Matrix (NR::translate(- *center)) *
3991             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3992             NR::Matrix (NR::translate(*center));
3994         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3995             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3996             n->pos *= t;
3997             n->n.pos *= t;
3998             n->p.pos *= t;
3999             sp_node_update_handles(n, false);
4000         }
4001     }
4003     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4006 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4008     g_assert (nodepath->selected);
4010     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4011     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4012     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4013         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4014         box.expandTo (n->pos); // contain all selected nodes
4015     }
4016     return box;
4019 //-----------------------------------------------
4020 /**
4021  * Return new subpath under given nodepath.
4022  */
4023 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4025     g_assert(nodepath);
4026     g_assert(nodepath->desktop);
4028    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4030     s->nodepath = nodepath;
4031     s->closed = FALSE;
4032     s->nodes = NULL;
4033     s->first = NULL;
4034     s->last = NULL;
4036     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4037     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4038     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4040     return s;
4043 /**
4044  * Destroy nodes in subpath, then subpath itself.
4045  */
4046 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4048     g_assert(subpath);
4049     g_assert(subpath->nodepath);
4050     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4052     while (subpath->nodes) {
4053         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4054     }
4056     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4058     g_free(subpath);
4061 /**
4062  * Link head to tail in subpath.
4063  */
4064 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4066     g_assert(!sp->closed);
4067     g_assert(sp->last != sp->first);
4068     g_assert(sp->first->code == NR_MOVETO);
4070     sp->closed = TRUE;
4072     //Link the head to the tail
4073     sp->first->p.other = sp->last;
4074     sp->last->n.other  = sp->first;
4075     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4076     sp->first          = sp->last;
4078     //Remove the extra end node
4079     sp_nodepath_node_destroy(sp->last->n.other);
4082 /**
4083  * Open closed (loopy) subpath at node.
4084  */
4085 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4087     g_assert(sp->closed);
4088     g_assert(n->subpath == sp);
4089     g_assert(sp->first == sp->last);
4091     /* We create new startpoint, current node will become last one */
4093    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4094                                                 &n->pos, &n->pos, &n->n.pos);
4097     sp->closed        = FALSE;
4099     //Unlink to make a head and tail
4100     sp->first         = new_path;
4101     sp->last          = n;
4102     n->n.other        = NULL;
4103     new_path->p.other = NULL;
4106 /**
4107  * Return new node in subpath with given properties.
4108  * \param pos Position of node.
4109  * \param ppos Handle position in previous direction
4110  * \param npos Handle position in previous direction
4111  */
4112 Inkscape::NodePath::Node *
4113 sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, NR::Point *ppos, NR::Point *pos, NR::Point *npos)
4115     g_assert(sp);
4116     g_assert(sp->nodepath);
4117     g_assert(sp->nodepath->desktop);
4119     if (nodechunk == NULL)
4120         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4122     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4124     n->subpath  = sp;
4126     if (type != Inkscape::NodePath::NODE_NONE) {
4127         // use the type from sodipodi:nodetypes
4128         n->type = type;
4129     } else {
4130         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4131             // points are (almost) collinear
4132             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4133                 // endnode, or a node with a retracted handle
4134                 n->type = Inkscape::NodePath::NODE_CUSP;
4135             } else {
4136                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4137             }
4138         } else {
4139             n->type = Inkscape::NodePath::NODE_CUSP;
4140         }
4141     }
4143     n->code     = code;
4144     n->selected = FALSE;
4145     n->pos      = *pos;
4146     n->p.pos    = *ppos;
4147     n->n.pos    = *npos;
4149     n->dragging_out = NULL;
4151     Inkscape::NodePath::Node *prev;
4152     if (next) {
4153         //g_assert(g_list_find(sp->nodes, next));
4154         prev = next->p.other;
4155     } else {
4156         prev = sp->last;
4157     }
4159     if (prev)
4160         prev->n.other = n;
4161     else
4162         sp->first = n;
4164     if (next)
4165         next->p.other = n;
4166     else
4167         sp->last = n;
4169     n->p.other = prev;
4170     n->n.other = next;
4172     n->knot = sp_knot_new(sp->nodepath->desktop, _("<b>Node</b>: drag to edit the path; with <b>Ctrl</b> to snap to horizontal/vertical; with <b>Ctrl+Alt</b> to snap to handles' directions"));
4173     sp_knot_set_position(n->knot, pos, 0);
4175     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4176     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4177     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4178     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4179     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4180     sp_knot_update_ctrl(n->knot);
4182     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4183     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4184     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4185     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4186     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4187     sp_knot_show(n->knot);
4189     // We only create handle knots and lines on demand
4190     n->p.knot = NULL;
4191     n->p.line = NULL;
4192     n->n.knot = NULL;
4193     n->n.line = NULL;
4195     sp->nodes = g_list_prepend(sp->nodes, n);
4197     return n;
4200 /**
4201  * Destroy node and its knots, link neighbors in subpath.
4202  */
4203 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4205     g_assert(node);
4206     g_assert(node->subpath);
4207     g_assert(SP_IS_KNOT(node->knot));
4209    Inkscape::NodePath::SubPath *sp = node->subpath;
4211     if (node->selected) { // first, deselect
4212         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4213         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4214     }
4216     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4218     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4219     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4220     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4221     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4222     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4223     g_object_unref(G_OBJECT(node->knot));
4225     if (node->p.knot) {
4226         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4227         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4228         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4229         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4230         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4231         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4232         g_object_unref(G_OBJECT(node->p.knot));
4233         node->p.knot = NULL;
4234     }
4236     if (node->n.knot) {
4237         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4238         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4239         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4240         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4241         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4242         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4243         g_object_unref(G_OBJECT(node->n.knot));
4244         node->n.knot = NULL;
4245     }
4247     if (node->p.line)
4248         gtk_object_destroy(GTK_OBJECT(node->p.line));
4249     if (node->n.line)
4250         gtk_object_destroy(GTK_OBJECT(node->n.line));
4252     if (sp->nodes) { // there are others nodes on the subpath
4253         if (sp->closed) {
4254             if (sp->first == node) {
4255                 g_assert(sp->last == node);
4256                 sp->first = node->n.other;
4257                 sp->last = sp->first;
4258             }
4259             node->p.other->n.other = node->n.other;
4260             node->n.other->p.other = node->p.other;
4261         } else {
4262             if (sp->first == node) {
4263                 sp->first = node->n.other;
4264                 sp->first->code = NR_MOVETO;
4265             }
4266             if (sp->last == node) sp->last = node->p.other;
4267             if (node->p.other) node->p.other->n.other = node->n.other;
4268             if (node->n.other) node->n.other->p.other = node->p.other;
4269         }
4270     } else { // this was the last node on subpath
4271         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4272     }
4274     g_mem_chunk_free(nodechunk, node);
4277 /**
4278  * Returns one of the node's two sides.
4279  * \param which Indicates which side.
4280  * \return Pointer to previous node side if which==-1, next if which==1.
4281  */
4282 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4284     g_assert(node);
4286     switch (which) {
4287         case -1:
4288             return &node->p;
4289         case 1:
4290             return &node->n;
4291         default:
4292             break;
4293     }
4295     g_assert_not_reached();
4297     return NULL;
4300 /**
4301  * Return the other side of the node, given one of its sides.
4302  */
4303 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4305     g_assert(node);
4307     if (me == &node->p) return &node->n;
4308     if (me == &node->n) return &node->p;
4310     g_assert_not_reached();
4312     return NULL;
4315 /**
4316  * Return NRPathcode on the given side of the node.
4317  */
4318 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4320     g_assert(node);
4322     if (me == &node->p) {
4323         if (node->p.other) return (NRPathcode)node->code;
4324         return NR_MOVETO;
4325     }
4327     if (me == &node->n) {
4328         if (node->n.other) return (NRPathcode)node->n.other->code;
4329         return NR_MOVETO;
4330     }
4332     g_assert_not_reached();
4334     return NR_END;
4337 /**
4338  * Return node with the given index
4339  */
4340 Inkscape::NodePath::Node *
4341 sp_nodepath_get_node_by_index(int index)
4343     Inkscape::NodePath::Node *e = NULL;
4345     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4346     if (!nodepath) {
4347         return e;
4348     }
4350     //find segment
4351     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4353         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4354         int n = g_list_length(sp->nodes);
4355         if (sp->closed) {
4356             n++;
4357         }
4359         //if the piece belongs to this subpath grab it
4360         //otherwise move onto the next subpath
4361         if (index < n) {
4362             e = sp->first;
4363             for (int i = 0; i < index; ++i) {
4364                 e = e->n.other;
4365             }
4366             break;
4367         } else {
4368             if (sp->closed) {
4369                 index -= (n+1);
4370             } else {
4371                 index -= n;
4372             }
4373         }
4374     }
4376     return e;
4379 /**
4380  * Returns plain text meaning of node type.
4381  */
4382 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4384     unsigned retracted = 0;
4385     bool endnode = false;
4387     for (int which = -1; which <= 1; which += 2) {
4388         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4389         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4390             retracted ++;
4391         if (!side->other)
4392             endnode = true;
4393     }
4395     if (retracted == 0) {
4396         if (endnode) {
4397                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4398                 return _("end node");
4399         } else {
4400             switch (node->type) {
4401                 case Inkscape::NodePath::NODE_CUSP:
4402                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4403                     return _("cusp");
4404                 case Inkscape::NodePath::NODE_SMOOTH:
4405                     // TRANSLATORS: "smooth" is an adjective here
4406                     return _("smooth");
4407                 case Inkscape::NodePath::NODE_SYMM:
4408                     return _("symmetric");
4409             }
4410         }
4411     } else if (retracted == 1) {
4412         if (endnode) {
4413             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4414             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4415         } else {
4416             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4417         }
4418     } else {
4419         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4420     }
4422     return NULL;
4425 /**
4426  * Handles content of statusbar as long as node tool is active.
4427  */
4428 void
4429 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4431     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>Alt+drag</b> nodes to sculpt; <b>arrow</b> keys to move nodes, <b>&lt; &gt;</b> to scale, <b>[ ]</b> to rotate");
4432     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4434     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4435     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4436     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4437     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4439     SPDesktop *desktop = NULL;
4440     if (nodepath) {
4441         desktop = nodepath->desktop;
4442     } else {
4443         desktop = SP_ACTIVE_DESKTOP;
4444     }
4446     SPEventContext *ec = desktop->event_context;
4447     if (!ec) return;
4448     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4449     if (!mc) return;
4451     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4453     if (selected_nodes == 0) {
4454         Inkscape::Selection *sel = desktop->selection;
4455         if (!sel || sel->isEmpty()) {
4456             mc->setF(Inkscape::NORMAL_MESSAGE,
4457                      _("Select a single object to edit its nodes or handles."));
4458         } else {
4459             if (nodepath) {
4460             mc->setF(Inkscape::NORMAL_MESSAGE,
4461                      ngettext("<b>0</b> out of <b>%i</b> node selected. <b>Click</b>, <b>Shift+click</b>, or <b>drag around</b> nodes to select.",
4462                               "<b>0</b> out of <b>%i</b> nodes selected. <b>Click</b>, <b>Shift+click</b>, or <b>drag around</b> nodes to select.",
4463                               total_nodes),
4464                      total_nodes);
4465             } else {
4466                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4467                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4468                 } else {
4469                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4470                 }
4471             }
4472         }
4473     } else if (nodepath && selected_nodes == 1) {
4474         mc->setF(Inkscape::NORMAL_MESSAGE,
4475                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4476                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4477                           total_nodes),
4478                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4479     } else {
4480         if (selected_subpaths > 1) {
4481             mc->setF(Inkscape::NORMAL_MESSAGE,
4482                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4483                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4484                               total_nodes),
4485                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4486         } else {
4487             mc->setF(Inkscape::NORMAL_MESSAGE,
4488                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4489                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4490                               total_nodes),
4491                      selected_nodes, total_nodes, when_selected);
4492         }
4493     }
4496 /*
4497  * returns a *copy* of the curve of that object.
4498  */
4499 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4500     if (!object)
4501         return NULL;
4503     SPCurve *curve = NULL;
4504     if (SP_IS_PATH(object)) {
4505         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4506         curve = sp_curve_copy(curve_new);
4507     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4508         const gchar *svgd = object->repr->attribute(key);
4509         if (svgd) {
4510             NArtBpath *bpath = sp_svg_read_path(svgd);
4511             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4512             if (curve_new) {
4513                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4514             } else {
4515                 g_free(bpath);
4516             }
4517         }
4518     }
4520     return curve;
4523 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4524     if (!np || !np->object || !curve)
4525         return;
4527     if (SP_IS_PATH(np->object)) {
4528         if (SP_SHAPE(np->object)->path_effect_href) {
4529             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4530         } else {
4531             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4532         }
4533     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4534         // FIXME: this writing to string and then reading from string is bound to be slow.
4535         // create a method to convert from curve directly to 2geom...
4536         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4537         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4538         g_free(svgpath);
4540         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4541     }
4544 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4545     np->show_helperpath = show;
4547     if (show) {
4548         SPCurve *helper_curve = sp_curve_copy(np->curve);
4549         sp_curve_transform(helper_curve, np->i2d );
4550         if (!np->helper_path) {
4551             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4552             sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4553             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4554             sp_canvas_item_show(np->helper_path);
4555         } else {
4556             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4557         }
4558         sp_curve_unref(helper_curve);
4559     } else {
4560         if (np->helper_path) {
4561             GtkObject *temp = np->helper_path;
4562             np->helper_path = NULL;
4563             gtk_object_destroy(temp);
4564         }
4565     }
4568 /* this function does not work yet */
4569 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4570     np->straight_path = true;
4571     np->show_handles = false;
4572     g_message("add code to make the path straight.");
4573     // do sp_nodepath_convert_node_type on all nodes?
4574     // search for this text !!!   "Make selected segments lines"
4578 /*
4579   Local Variables:
4580   mode:c++
4581   c-file-style:"stroustrup"
4582   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4583   indent-tabs-mode:nil
4584   fill-column:99
4585   End:
4586 */
4587 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :