Code

c62fb2c0f6859929d25f36d8231c0f43ee444a7c
[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 "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include <2geom/pathvector.h>
25 #include <2geom/sbasis-to-bezier.h>
26 #include <2geom/bezier-curve.h>
27 #include <2geom/hvlinesegment.h>
28 #include "helper/units.h"
29 #include "knot.h"
30 #include "inkscape.h"
31 #include "document.h"
32 #include "sp-namedview.h"
33 #include "desktop.h"
34 #include "desktop-handles.h"
35 #include "snap.h"
36 #include "message-stack.h"
37 #include "message-context.h"
38 #include "node-context.h"
39 #include "shape-editor.h"
40 #include "selection-chemistry.h"
41 #include "selection.h"
42 #include "xml/repr.h"
43 #include "prefs-utils.h"
44 #include "sp-metrics.h"
45 #include "sp-path.h"
46 #include "libnr/nr-matrix-ops.h"
47 #include "splivarot.h"
48 #include "svg/svg.h"
49 #include "verbs.h"
50 #include "display/bezier-utils.h"
51 #include <vector>
52 #include <algorithm>
53 #include <cstring>
54 #include <string>
55 #include "live_effects/lpeobject.h"
56 #include "live_effects/effect.h"
57 #include "live_effects/parameter/parameter.h"
58 #include "live_effects/parameter/path.h"
59 #include "util/mathfns.h"
60 #include "display/snap-indicator.h"
61 #include "snapped-point.h"
63 class NR::Matrix;
65 /// \todo
66 /// evil evil evil. FIXME: conflict of two different Path classes!
67 /// There is a conflict in the namespace between two classes named Path.
68 /// #include "sp-flowtext.h"
69 /// #include "sp-flowregion.h"
71 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
72 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
73 GType sp_flowregion_get_type (void);
74 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
75 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
76 GType sp_flowtext_get_type (void);
77 // end evil workaround
79 #include "helper/stlport.h"
82 /// \todo fixme: Implement these via preferences */
84 #define NODE_FILL          0xbfbfbf00
85 #define NODE_STROKE        0x000000ff
86 #define NODE_FILL_HI       0xff000000
87 #define NODE_STROKE_HI     0x000000ff
88 #define NODE_FILL_SEL      0x0000ffff
89 #define NODE_STROKE_SEL    0x000000ff
90 #define NODE_FILL_SEL_HI   0xff000000
91 #define NODE_STROKE_SEL_HI 0x000000ff
92 #define KNOT_FILL          0xffffffff
93 #define KNOT_STROKE        0x000000ff
94 #define KNOT_FILL_HI       0xff000000
95 #define KNOT_STROKE_HI     0x000000ff
97 static GMemChunk *nodechunk = NULL;
99 /* Creation from object */
101 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
102 static void add_curve_to_subpath( Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c,
103                                   Inkscape::NodePath::NodeType const *t, guint & i, NR::Point & ppos, NRPathcode & pcode  );
104 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
106 /* Object updating */
108 static void stamp_repr(Inkscape::NodePath::Path *np);
109 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
110 static gchar *create_typestr(Inkscape::NodePath::Path *np);
112 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
114 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
116 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
118 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
120 /* Adjust handle placement, if the node or the other handle is moved */
121 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
122 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
124 /* Node event callbacks */
125 static void node_clicked(SPKnot *knot, guint state, gpointer data);
126 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
127 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
128 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 /* Handle event callbacks */
131 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
132 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
134 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
135 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
138 /* Constructors and destructors */
140 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
141 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
142 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
143 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
144 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
145                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
146 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
148 /* Helpers */
150 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
151 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
152 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
154 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
155 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
157 // active_node indicates mouseover node
158 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
160 static void sp_nodepath_draw_helper_curve(Inkscape::NodePath::Path *np, SPDesktop *desktop) {
161     // Draw helper curve
162     if (np->show_helperpath) {
163         SPCurve *helper_curve = np->curve->copy();
164         helper_curve->transform(to_2geom(np->i2d));
165         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
166         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);
167         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
168         sp_canvas_item_move_to_z(np->helper_path, 0);
169         sp_canvas_item_show(np->helper_path);
170         helper_curve->unref();
171     }
174 /**
175  * \brief Creates new nodepath from item
176  */
177 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
179     Inkscape::XML::Node *repr = object->repr;
181     /** \todo
182      * FIXME: remove this. We don't want to edit paths inside flowtext.
183      * Instead we will build our flowtext with cloned paths, so that the
184      * real paths are outside the flowtext and thus editable as usual.
185      */
186     if (SP_IS_FLOWTEXT(object)) {
187         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
188             if SP_IS_FLOWREGION(child) {
189                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
190                 if (grandchild && SP_IS_PATH(grandchild)) {
191                     object = SP_ITEM(grandchild);
192                     break;
193                 }
194             }
195         }
196     }
198     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
200     if (curve == NULL)
201         return NULL;
203     if (curve->get_segment_count() < 1) {
204         curve->unref();
205         return NULL; // prevent crash for one-node paths
206     }
208     //Create new nodepath
209     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
210     if (!np) {
211         curve->unref();
212         return NULL;
213     }
215     // Set defaults
216     np->desktop     = desktop;
217     np->object      = object;
218     np->subpaths    = NULL;
219     np->selected    = NULL;
220     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
221     np->livarot_path = NULL;
222     np->local_change = 0;
223     np->show_handles = show_handles;
224     np->helper_path = NULL;
225     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
226     np->helperpath_width = 1.0;
227     np->curve = curve->copy();
228     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
229     if (SP_IS_LPE_ITEM(object)) {
230         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
231         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
232             np->show_helperpath = true;
233         }            
234     }
235     np->straight_path = false;
236     if (IS_LIVEPATHEFFECT(object) && item) {
237         np->item = item;
238     } else {
239         np->item = SP_ITEM(object);
240     }
242     // we need to update item's transform from the repr here,
243     // because they may be out of sync when we respond
244     // to a change in repr by regenerating nodepath     --bb
245     sp_object_read_attr(SP_OBJECT(np->item), "transform");
247     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
248     np->d2i  = np->i2d.inverse();
250     np->repr = repr;
251     if (repr_key_in) { // apparantly the object is an LPEObject
252         np->repr_key = g_strdup(repr_key_in);
253         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
254         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
255         if (lpeparam) {
256             lpeparam->param_setup_nodepath(np);
257         }
258     } else {
259         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
260         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
261             np->repr_key = g_strdup("inkscape:original-d");
263             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
264             if (lpe) {
265                 lpe->setup_nodepath(np);
266             }
267         } else {
268             np->repr_key = g_strdup("d");
269         }
270     }
272     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
273      * So for example a closed rectangle has a nodetypestring of length 5.
274      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
275     Geom::PathVector const &pathv = curve->get_pathvector();
276     guint length = curve->get_segment_count();
277     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
278         length += pit->empty() ? 0 : 1;
279     }
281     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
282     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
284     // create the subpath(s) from the bpath
285     subpaths_from_pathvector(np, pathv, typestr);
287     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
288     np->subpaths = g_list_reverse(np->subpaths);
290     delete[] typestr;
291     curve->unref();
293     // create the livarot representation from the same item
294     sp_nodepath_ensure_livarot_path(np);
296     sp_nodepath_draw_helper_curve(np, desktop);
298     return np;
301 /**
302  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
303  */
304 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
306     if (!np)  //soft fail, like delete
307         return;
309     while (np->subpaths) {
310         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
311     }
313     //Inform the ShapeEditor that made me, if any, that I am gone.
314     if (np->shape_editor)
315         np->shape_editor->nodepath_destroyed();
317     g_assert(!np->selected);
319     if (np->livarot_path) {
320         delete np->livarot_path;
321         np->livarot_path = NULL;
322     }
324     if (np->helper_path) {
325         GtkObject *temp = np->helper_path;
326         np->helper_path = NULL;
327         gtk_object_destroy(temp);
328     }
329     if (np->curve) {
330         np->curve->unref();
331         np->curve = NULL;
332     }
334     if (np->repr_key) {
335         g_free(np->repr_key);
336         np->repr_key = NULL;
337     }
338     if (np->repr_nodetypes_key) {
339         g_free(np->repr_nodetypes_key);
340         np->repr_nodetypes_key = NULL;
341     }
343     np->desktop = NULL;
345     g_free(np);
349 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
351     if (np && np->livarot_path == NULL) {
352         SPCurve *curve = create_curve(np);
353         np->livarot_path = new Path;
354         np->livarot_path->LoadPathVector(curve->get_pathvector());
356         if (np->livarot_path)
357             np->livarot_path->ConvertWithBackData(0.01);
359         curve->unref();
360     }
364 /**
365  *  Return the node count of a given NodeSubPath.
366  */
367 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
369     if (!subpath)
370         return 0;
371     gint nodeCount = g_list_length(subpath->nodes);
372     return nodeCount;
375 /**
376  *  Return the node count of a given NodePath.
377  */
378 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
380     if (!np)
381         return 0;
382     gint nodeCount = 0;
383     for (GList *item = np->subpaths ; item ; item=item->next) {
384        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
385         nodeCount += g_list_length(subpath->nodes);
386     }
387     return nodeCount;
390 /**
391  *  Return the subpath count of a given NodePath.
392  */
393 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
395     if (!np)
396         return 0;
397     return g_list_length (np->subpaths);
400 /**
401  *  Return the selected node count of a given NodePath.
402  */
403 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
405     if (!np)
406         return 0;
407     return g_list_length (np->selected);
410 /**
411  *  Return the number of subpaths where nodes are selected in a given NodePath.
412  */
413 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
415     if (!np)
416         return 0;
417     if (!np->selected)
418         return 0;
419     if (!np->selected->next)
420         return 1;
421     gint count = 0;
422     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
423         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
424         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
425             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
426             if (node->selected) {
427                 count ++;
428                 break;
429             }
430         }
431     }
432     return count;
435 /**
436  * Clean up a nodepath after editing.
437  *
438  * Currently we are deleting trivial subpaths.
439  */
440 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
442     GList *badSubPaths = NULL;
444     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
445     for (GList *l = nodepath->subpaths; l ; l=l->next) {
446        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
447        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
448             badSubPaths = g_list_append(badSubPaths, sp);
449     }
451     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
452     //also removes the subpath from nodepath->subpaths
453     for (GList *l = badSubPaths; l ; l=l->next) {
454        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
455         sp_nodepath_subpath_destroy(sp);
456     }
458     g_list_free(badSubPaths);
461 /**
462  * Create new nodepaths from pathvector, make it subpaths of np.
463  * \param t The node type array.
464  */
465 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
467     guint i = 0;  // index into node type array
468     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
469         if (pit->empty())
470             continue;  // don't add single knot paths
472         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
474         NR::Point ppos = from_2geom(pit->initialPoint()) * np->i2d;
475         NRPathcode pcode = NR_MOVETO;
477         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
478             add_curve_to_subpath(np, sp, *cit, t, i, ppos, pcode);
479         }
481         if (pit->closed()) {
482             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
483             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
484              * If the length is zero, don't add it to the nodepath. */
485             Geom::Curve const &closing_seg = pit->back_closed();
486             if ( ! closing_seg.isDegenerate() ) {
487                 NR::Point pos = from_2geom(closing_seg.finalPoint()) * np->i2d;
488                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
489             }
491             sp_nodepath_subpath_close(sp);
492         }
493     }
495 // should add initial point of curve with type of previous curve:
496 static void add_curve_to_subpath(Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c, Inkscape::NodePath::NodeType const *t, guint & i,
497                                  NR::Point & ppos, NRPathcode & pcode)
499     if( dynamic_cast<Geom::LineSegment const*>(&c) ||
500         dynamic_cast<Geom::HLineSegment const*>(&c) ||
501         dynamic_cast<Geom::VLineSegment const*>(&c) )
502     {
503         NR::Point pos = from_2geom(c.initialPoint()) * np->i2d;
504         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
505         ppos = from_2geom(c.finalPoint());
506         pcode = NR_LINETO;
507     }
508     else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) {
509         std::vector<Geom::Point> points = cubic_bezier->points();
510         NR::Point pos = from_2geom(points[0]) * np->i2d;
511         NR::Point npos = from_2geom(points[1]) * np->i2d;
512         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
513         ppos = from_2geom(points[2]) * np->i2d;
514         pcode = NR_CURVETO;
515     }
516     else {
517         //this case handles sbasis as well as all other curve types
518         Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1);
520         for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
521             add_curve_to_subpath(np, sp, *iter, t, i, ppos, pcode);
522         }
523     }
527 /**
528  * Convert from sodipodi:nodetypes to new style type array.
529  */
530 static
531 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
533     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
535     guint pos = 0;
537     if (types) {
538         for (guint i = 0; types[i] && ( i < length ); i++) {
539             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
540             if (types[i] != '\0') {
541                 switch (types[i]) {
542                     case 's':
543                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
544                         break;
545                     case 'z':
546                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
547                         break;
548                     case 'c':
549                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
550                         break;
551                     default:
552                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
553                         break;
554                 }
555             }
556         }
557     }
559     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
561     return typestr;
564 /**
565  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
566  * updated but repr is not (for speed). Used during curve and node drag.
567  */
568 static void update_object(Inkscape::NodePath::Path *np)
570     g_assert(np);
572     np->curve->unref();
573     np->curve = create_curve(np);
575     sp_nodepath_set_curve(np, np->curve);
577     if (np->show_helperpath) {
578         SPCurve * helper_curve = np->curve->copy();
579         helper_curve->transform(to_2geom(np->i2d));
580         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
581         helper_curve->unref();
582     }
584     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
585     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
586     np->shape_editor->update_knotholder();
589 /**
590  * Update XML path node with data from path object.
591  */
592 static void update_repr_internal(Inkscape::NodePath::Path *np)
594     g_assert(np);
596     Inkscape::XML::Node *repr = np->object->repr;
598     np->curve->unref();
599     np->curve = create_curve(np);
601     gchar *typestr = create_typestr(np);
602     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
604     // determine if path has an effect applied and write to correct "d" attribute.
605     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
606         np->local_change++;
607         repr->setAttribute(np->repr_key, svgpath);
608     }
610     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
611         np->local_change++;
612         repr->setAttribute(np->repr_nodetypes_key, typestr);
613     }
615     g_free(svgpath);
616     g_free(typestr);
618     if (np->show_helperpath) {
619         SPCurve * helper_curve = np->curve->copy();
620         helper_curve->transform(to_2geom(np->i2d));
621         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
622         helper_curve->unref();
623     }
624  }
626 /**
627  * Update XML path node with data from path object, commit changes forever.
628  */
629 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
631     //fixme: np can be NULL, so check before proceeding
632     g_return_if_fail(np != NULL);
634     if (np->livarot_path) {
635         delete np->livarot_path;
636         np->livarot_path = NULL;
637     }
639     update_repr_internal(np);
640     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
642     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
643                      annotation);
646 /**
647  * Update XML path node with data from path object, commit changes with undo.
648  */
649 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
651     if (np->livarot_path) {
652         delete np->livarot_path;
653         np->livarot_path = NULL;
654     }
656     update_repr_internal(np);
657     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
658                            annotation);
661 /**
662  * Make duplicate of path, replace corresponding XML node in tree, commit.
663  */
664 static void stamp_repr(Inkscape::NodePath::Path *np)
666     g_assert(np);
668     Inkscape::XML::Node *old_repr = np->object->repr;
669     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
671     // remember the position of the item
672     gint pos = old_repr->position();
673     // remember parent
674     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
676     SPCurve *curve = create_curve(np);
677     gchar *typestr = create_typestr(np);
679     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
681     new_repr->setAttribute(np->repr_key, svgpath);
682     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
684     // add the new repr to the parent
685     parent->appendChild(new_repr);
686     // move to the saved position
687     new_repr->setPosition(pos > 0 ? pos : 0);
689     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
690                      _("Stamp"));
692     Inkscape::GC::release(new_repr);
693     g_free(svgpath);
694     g_free(typestr);
695     curve->unref();
698 /**
699  * Create curve from path.
700  */
701 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
703     SPCurve *curve = new SPCurve();
705     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
706        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
707         curve->moveto(sp->first->pos * np->d2i);
708        Inkscape::NodePath::Node *n = sp->first->n.other;
709         while (n) {
710             NR::Point const end_pt = n->pos * np->d2i;
711             switch (n->code) {
712                 case NR_LINETO:
713                     curve->lineto(end_pt);
714                     break;
715                 case NR_CURVETO:
716                     curve->curveto(n->p.other->n.pos * np->d2i,
717                                      n->p.pos * np->d2i,
718                                      end_pt);
719                     break;
720                 default:
721                     g_assert_not_reached();
722                     break;
723             }
724             if (n != sp->last) {
725                 n = n->n.other;
726             } else {
727                 n = NULL;
728             }
729         }
730         if (sp->closed) {
731             curve->closepath();
732         }
733     }
735     return curve;
738 /**
739  * Convert path type string to sodipodi:nodetypes style.
740  */
741 static gchar *create_typestr(Inkscape::NodePath::Path *np)
743     gchar *typestr = g_new(gchar, 32);
744     gint len = 32;
745     gint pos = 0;
747     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
748        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
750         if (pos >= len) {
751             typestr = g_renew(gchar, typestr, len + 32);
752             len += 32;
753         }
755         typestr[pos++] = 'c';
757        Inkscape::NodePath::Node *n;
758         n = sp->first->n.other;
759         while (n) {
760             gchar code;
762             switch (n->type) {
763                 case Inkscape::NodePath::NODE_CUSP:
764                     code = 'c';
765                     break;
766                 case Inkscape::NodePath::NODE_SMOOTH:
767                     code = 's';
768                     break;
769                 case Inkscape::NodePath::NODE_SYMM:
770                     code = 'z';
771                     break;
772                 default:
773                     g_assert_not_reached();
774                     code = '\0';
775                     break;
776             }
778             if (pos >= len) {
779                 typestr = g_renew(gchar, typestr, len + 32);
780                 len += 32;
781             }
783             typestr[pos++] = code;
785             if (n != sp->last) {
786                 n = n->n.other;
787             } else {
788                 n = NULL;
789             }
790         }
791     }
793     if (pos >= len) {
794         typestr = g_renew(gchar, typestr, len + 1);
795         len += 1;
796     }
798     typestr[pos++] = '\0';
800     return typestr;
803 /**
804  * Returns current path in context. // later eliminate this function at all!
805  */
806 static Inkscape::NodePath::Path *sp_nodepath_current()
808     if (!SP_ACTIVE_DESKTOP) {
809         return NULL;
810     }
812     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
814     if (!SP_IS_NODE_CONTEXT(event_context)) {
815         return NULL;
816     }
818     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
823 /**
824  \brief Fills node and handle positions for three nodes, splitting line
825   marked by end at distance t.
826  */
827 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
829     g_assert(new_path != NULL);
830     g_assert(end      != NULL);
832     g_assert(end->p.other == new_path);
833    Inkscape::NodePath::Node *start = new_path->p.other;
834     g_assert(start);
836     if (end->code == NR_LINETO) {
837         new_path->type =Inkscape::NodePath::NODE_CUSP;
838         new_path->code = NR_LINETO;
839         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
840     } else {
841         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
842         new_path->code = NR_CURVETO;
843         gdouble s      = 1 - t;
844         for (int dim = 0; dim < 2; dim++) {
845             NR::Coord const f000 = start->pos[dim];
846             NR::Coord const f001 = start->n.pos[dim];
847             NR::Coord const f011 = end->p.pos[dim];
848             NR::Coord const f111 = end->pos[dim];
849             NR::Coord const f00t = s * f000 + t * f001;
850             NR::Coord const f01t = s * f001 + t * f011;
851             NR::Coord const f11t = s * f011 + t * f111;
852             NR::Coord const f0tt = s * f00t + t * f01t;
853             NR::Coord const f1tt = s * f01t + t * f11t;
854             NR::Coord const fttt = s * f0tt + t * f1tt;
855             start->n.pos[dim]    = f00t;
856             new_path->p.pos[dim] = f0tt;
857             new_path->pos[dim]   = fttt;
858             new_path->n.pos[dim] = f1tt;
859             end->p.pos[dim]      = f11t;
860         }
861     }
864 /**
865  * Adds new node on direct line between two nodes, activates handles of all
866  * three nodes.
867  */
868 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
870     g_assert(end);
871     g_assert(end->subpath);
872     g_assert(g_list_find(end->subpath->nodes, end));
874    Inkscape::NodePath::Node *start = end->p.other;
875     g_assert( start->n.other == end );
876    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
877                                                end,
878                                                (NRPathcode)end->code == NR_LINETO?
879                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
880                                                (NRPathcode)end->code,
881                                                &start->pos, &start->pos, &start->n.pos);
882     sp_nodepath_line_midpoint(newnode, end, t);
884     sp_node_adjust_handles(start);
885     sp_node_update_handles(start);
886     sp_node_update_handles(newnode);
887     sp_node_adjust_handles(end);
888     sp_node_update_handles(end);
890     return newnode;
893 /**
894 \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
895 */
896 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
898     g_assert(node);
899     g_assert(node->subpath);
900     g_assert(g_list_find(node->subpath->nodes, node));
902    Inkscape::NodePath::SubPath *sp = node->subpath;
903     Inkscape::NodePath::Path *np    = sp->nodepath;
905     if (sp->closed) {
906         sp_nodepath_subpath_open(sp, node);
907         return sp->first;
908     } else {
909         // no break for end nodes
910         if (node == sp->first) return NULL;
911         if (node == sp->last ) return NULL;
913         // create a new subpath
914        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
916         // duplicate the break node as start of the new subpath
917         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
919         // attach rest of curve to new node
920         g_assert(node->n.other);
921         newnode->n.other = node->n.other; node->n.other = NULL;
922         newnode->n.other->p.other = newnode;
923         newsubpath->last = sp->last;
924         sp->last = node;
925         node = newnode;
926         while (node->n.other) {
927             node = node->n.other;
928             node->subpath = newsubpath;
929             sp->nodes = g_list_remove(sp->nodes, node);
930             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
931         }
934         return newnode;
935     }
938 /**
939  * Duplicate node and connect to neighbours.
940  */
941 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
943     g_assert(node);
944     g_assert(node->subpath);
945     g_assert(g_list_find(node->subpath->nodes, node));
947    Inkscape::NodePath::SubPath *sp = node->subpath;
949     NRPathcode code = (NRPathcode) node->code;
950     if (code == NR_MOVETO) { // if node is the endnode,
951         node->code = NR_LINETO; // new one is inserted before it, so change that to line
952     }
954     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
956     if (!node->n.other || !node->p.other) // if node is an endnode, select it
957         return node;
958     else
959         return newnode; // otherwise select the newly created node
962 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
964     node->p.pos = (node->pos + (node->pos - node->n.pos));
967 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
969     node->n.pos = (node->pos + (node->pos - node->p.pos));
972 /**
973  * Change line type at node, with side effects on neighbours.
974  */
975 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
977     g_assert(end);
978     g_assert(end->subpath);
979     g_assert(end->p.other);
981     if (end->code == static_cast< guint > ( code ) )
982         return;
984    Inkscape::NodePath::Node *start = end->p.other;
986     end->code = code;
988     if (code == NR_LINETO) {
989         if (start->code == NR_LINETO) {
990             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
991         }
992         if (end->n.other) {
993             if (end->n.other->code == NR_LINETO) {
994                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
995             }
996         }
997     } else {
998         NR::Point delta = end->pos - start->pos;
999         start->n.pos = start->pos + delta / 3;
1000         end->p.pos = end->pos - delta / 3;
1001         sp_node_adjust_handle(start, 1);
1002         sp_node_adjust_handle(end, -1);
1003     }
1005     sp_node_update_handles(start);
1006     sp_node_update_handles(end);
1009 /**
1010  * Change node type, and its handles accordingly.
1011  */
1012 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1014     g_assert(node);
1015     g_assert(node->subpath);
1017     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1018         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1019             type =Inkscape::NodePath::NODE_CUSP;
1020         }
1021     }
1023     node->type = type;
1025     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1026         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1027         node->knot->setSize (node->selected? 11 : 9);
1028         sp_knot_update_ctrl(node->knot);
1029     } else {
1030         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1031         node->knot->setSize (node->selected? 9 : 7);
1032         sp_knot_update_ctrl(node->knot);
1033     }
1035     // if one of handles is mouseovered, preserve its position
1036     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1037         sp_node_adjust_handle(node, 1);
1038     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1039         sp_node_adjust_handle(node, -1);
1040     } else {
1041         sp_node_adjust_handles(node);
1042     }
1044     sp_node_update_handles(node);
1046     sp_nodepath_update_statusbar(node->subpath->nodepath);
1048     return node;
1051 bool
1052 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1054         Inkscape::NodePath::Node *othernode = side->other;
1055         if (!othernode)
1056             return false;
1057         NRPathcode const code = sp_node_path_code_from_side(node, side);
1058         if (code == NR_LINETO)
1059             return true;
1060         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1061         if (&node->p == side) {
1062             other_to_me = &othernode->n;
1063         } else if (&node->n == side) {
1064             other_to_me = &othernode->p;
1065         } 
1066         if (!other_to_me)
1067             return false;
1068         bool is_line = 
1069              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1070               NR::L2(node->pos - side->pos) < 1e-6);
1071         return is_line;
1074 /**
1075  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1076  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1077  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1078  * If already cusp and set to cusp, retracts handles.
1079 */
1080 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1082     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1084 /* 
1085   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1086  
1087         if (two_handles) {
1088             // do nothing, adjust_handles called via set_node_type will line them up
1089         } else if (one_handle) {
1090             if (opposite_to_handle_is_line) {
1091                 if (lined_up) {
1092                     // already half-smooth; pull opposite handle too making it fully smooth
1093                 } else {
1094                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1095                 }
1096             } else {
1097                 // pull opposite handle in line with the existing one
1098             }
1099         } else if (no_handles) {
1100             if (both_segments_are_lines OR both_segments_are_curves) {
1101                 //pull both handles
1102             } else {
1103                 // pull the handle opposite to line segment, making node half-smooth
1104             }
1105         }
1106 */
1107         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1108         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1109         bool p_is_line = sp_node_side_is_line(node, &node->p);
1110         bool n_is_line = sp_node_side_is_line(node, &node->n);
1112         if (p_has_handle && n_has_handle) {
1113             // do nothing, adjust_handles will line them up
1114         } else if (p_has_handle || n_has_handle) {
1115             if (p_has_handle && n_is_line) {
1116                 Radial line (node->n.other->pos - node->pos);
1117                 Radial handle (node->pos - node->p.pos);
1118                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1119                     // already half-smooth; pull opposite handle too making it fully smooth
1120                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1121                 } else {
1122                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1123                 }
1124             } else if (n_has_handle && p_is_line) {
1125                 Radial line (node->p.other->pos - node->pos);
1126                 Radial handle (node->pos - node->n.pos);
1127                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1128                     // already half-smooth; pull opposite handle too making it fully smooth
1129                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1130                 } else {
1131                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1132                 }
1133             } else if (p_has_handle && node->n.other) {
1134                 // pull n handle
1135                 node->n.other->code = NR_CURVETO;
1136                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1137                     NR::L2(node->p.pos - node->pos) :
1138                     NR::L2(node->n.other->pos - node->pos) / 3;
1139                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1140             } else if (n_has_handle && node->p.other) {
1141                 // pull p handle
1142                 node->code = NR_CURVETO;
1143                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1144                     NR::L2(node->n.pos - node->pos) :
1145                     NR::L2(node->p.other->pos - node->pos) / 3;
1146                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1147             }
1148         } else if (!p_has_handle && !n_has_handle) {
1149             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1150                 // no handles, but both segments are either lnes or curves:
1151                 //pull both handles
1153                 // convert both to curves:
1154                 node->code = NR_CURVETO;
1155                 node->n.other->code = NR_CURVETO;
1157                 NR::Point leg_prev = node->pos - node->p.other->pos;
1158                 NR::Point leg_next = node->pos - node->n.other->pos;
1160                 double norm_leg_prev = L2(leg_prev);
1161                 double norm_leg_next = L2(leg_next);
1163                 NR::Point delta;
1164                 if (norm_leg_next > 0.0) {
1165                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1166                     (&delta)->normalize();
1167                 }
1169                 if (type == Inkscape::NodePath::NODE_SYMM) {
1170                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1171                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1172                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1173                 } else {
1174                     // length of handle is proportional to distance to adjacent node
1175                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1176                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1177                 }
1179             } else {
1180                 // pull the handle opposite to line segment, making it half-smooth
1181                 if (p_is_line && node->n.other) {
1182                     if (type != Inkscape::NodePath::NODE_SYMM) {
1183                         // pull n handle
1184                         node->n.other->code = NR_CURVETO;
1185                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1186                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1187                     }
1188                 } else if (n_is_line && node->p.other) {
1189                     if (type != Inkscape::NodePath::NODE_SYMM) {
1190                         // pull p handle
1191                         node->code = NR_CURVETO;
1192                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1193                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1194                     }
1195                 }
1196             }
1197         }
1198     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1199         // cusping a cusp: retract nodes
1200         node->p.pos = node->pos;
1201         node->n.pos = node->pos;
1202     }
1204     sp_nodepath_set_node_type (node, type);
1207 /**
1208  * Move node to point, and adjust its and neighbouring handles.
1209  */
1210 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1212     NR::Point delta = p - node->pos;
1213     node->pos = p;
1215     node->p.pos += delta;
1216     node->n.pos += delta;
1218     Inkscape::NodePath::Node *node_p = NULL;
1219     Inkscape::NodePath::Node *node_n = NULL;
1221     if (node->p.other) {
1222         if (node->code == NR_LINETO) {
1223             sp_node_adjust_handle(node, 1);
1224             sp_node_adjust_handle(node->p.other, -1);
1225             node_p = node->p.other;
1226         }
1227     }
1228     if (node->n.other) {
1229         if (node->n.other->code == NR_LINETO) {
1230             sp_node_adjust_handle(node, -1);
1231             sp_node_adjust_handle(node->n.other, 1);
1232             node_n = node->n.other;
1233         }
1234     }
1236     // this function is only called from batch movers that will update display at the end
1237     // themselves, so here we just move all the knots without emitting move signals, for speed
1238     sp_node_update_handles(node, false);
1239     if (node_n) {
1240         sp_node_update_handles(node_n, false);
1241     }
1242     if (node_p) {
1243         sp_node_update_handles(node_p, false);
1244     }
1247 /**
1248  * Call sp_node_moveto() for node selection and handle possible snapping.
1249  */
1250 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1251                                             bool const snap, bool constrained = false, 
1252                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1254     NR::Coord best = NR_HUGE;
1255     NR::Point delta(dx, dy);
1256     NR::Point best_pt = delta;
1257     Inkscape::SnappedPoint best_abs;
1258     
1259     if (snap) {    
1260         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1261          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1262          * must provide that information. */
1263           
1264         // Build a list of the unselected nodes to which the snapper should snap 
1265         std::vector<NR::Point> unselected_nodes;
1266         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1267             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1268             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1269                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1270                 if (!node->selected) {
1271                     unselected_nodes.push_back(node->pos);
1272                 }    
1273             }
1274         }        
1275         
1276         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1277         
1278         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1279             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1280             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1281             Inkscape::SnappedPoint s;
1282             if (constrained) {
1283                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1284                 dedicated_constraint.setPoint(n->pos);
1285                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1286             } else {
1287                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1288             }            
1289             if (s.getSnapped() && (s.getDistance() < best)) {
1290                 best = s.getDistance();
1291                 best_abs = s;
1292                 best_pt = s.getPoint() - n->pos;
1293             }
1294         }
1295                         
1296         if (best_abs.getSnapped()) {
1297             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1298         } else {
1299             nodepath->desktop->snapindicator->remove_snappoint();    
1300         }
1301     }
1303     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1304         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1305         sp_node_moveto(n, n->pos + best_pt);
1306     }
1308     // do not update repr here so that node dragging is acceptably fast
1309     update_object(nodepath);
1312 /**
1313 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1314 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1315 near x = 0.
1316  */
1317 double
1318 sculpt_profile (double x, double alpha, guint profile)
1320     if (x >= 1)
1321         return 0;
1322     if (x <= 0)
1323         return 1;
1325     switch (profile) {
1326         case SCULPT_PROFILE_LINEAR:
1327         return 1 - x;
1328         case SCULPT_PROFILE_BELL:
1329         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1330         case SCULPT_PROFILE_ELLIPTIC:
1331         return sqrt(1 - x*x);
1332     }
1334     return 1;
1337 double
1338 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1340     // extremely primitive for now, don't have time to look for the real one
1341     double lower = NR::L2(b - a);
1342     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1343     return (lower + upper)/2;
1346 void
1347 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1349     n->pos = n->origin + delta;
1350     n->n.pos = n->n.origin + delta_n;
1351     n->p.pos = n->p.origin + delta_p;
1352     sp_node_adjust_handles(n);
1353     sp_node_update_handles(n, false);
1356 /**
1357  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1358  * on how far they are from the dragged node n.
1359  */
1360 static void
1361 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1363     g_assert (n);
1364     g_assert (nodepath);
1365     g_assert (n->subpath->nodepath == nodepath);
1367     double pressure = n->knot->pressure;
1368     if (pressure == 0)
1369         pressure = 0.5; // default
1370     pressure = CLAMP (pressure, 0.2, 0.8);
1372     // map pressure to alpha = 1/5 ... 5
1373     double alpha = 1 - 2 * fabs(pressure - 0.5);
1374     if (pressure > 0.5)
1375         alpha = 1/alpha;
1377     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1379     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1380         // Only one subpath has selected nodes:
1381         // use linear mode, where the distance from n to node being dragged is calculated along the path
1383         double n_sel_range = 0, p_sel_range = 0;
1384         guint n_nodes = 0, p_nodes = 0;
1385         guint n_sel_nodes = 0, p_sel_nodes = 0;
1387         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1388         {
1389             double n_range = 0, p_range = 0;
1390             bool n_going = true, p_going = true;
1391             Inkscape::NodePath::Node *n_node = n;
1392             Inkscape::NodePath::Node *p_node = n;
1393             do {
1394                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1395                 if (n_node && n_going)
1396                     n_node = n_node->n.other;
1397                 if (n_node == NULL) {
1398                     n_going = false;
1399                 } else {
1400                     n_nodes ++;
1401                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1402                     if (n_node->selected) {
1403                         n_sel_nodes ++;
1404                         n_sel_range = n_range;
1405                     }
1406                     if (n_node == p_node) {
1407                         n_going = false;
1408                         p_going = false;
1409                     }
1410                 }
1411                 if (p_node && p_going)
1412                     p_node = p_node->p.other;
1413                 if (p_node == NULL) {
1414                     p_going = false;
1415                 } else {
1416                     p_nodes ++;
1417                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1418                     if (p_node->selected) {
1419                         p_sel_nodes ++;
1420                         p_sel_range = p_range;
1421                     }
1422                     if (p_node == n_node) {
1423                         n_going = false;
1424                         p_going = false;
1425                     }
1426                 }
1427             } while (n_going || p_going);
1428         }
1430         // Second pass: actually move nodes in this subpath
1431         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1432         {
1433             double n_range = 0, p_range = 0;
1434             bool n_going = true, p_going = true;
1435             Inkscape::NodePath::Node *n_node = n;
1436             Inkscape::NodePath::Node *p_node = n;
1437             do {
1438                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1439                 if (n_node && n_going)
1440                     n_node = n_node->n.other;
1441                 if (n_node == NULL) {
1442                     n_going = false;
1443                 } else {
1444                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1445                     if (n_node->selected) {
1446                         sp_nodepath_move_node_and_handles (n_node,
1447                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1448                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1449                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1450                     }
1451                     if (n_node == p_node) {
1452                         n_going = false;
1453                         p_going = false;
1454                     }
1455                 }
1456                 if (p_node && p_going)
1457                     p_node = p_node->p.other;
1458                 if (p_node == NULL) {
1459                     p_going = false;
1460                 } else {
1461                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1462                     if (p_node->selected) {
1463                         sp_nodepath_move_node_and_handles (p_node,
1464                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1465                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1466                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1467                     }
1468                     if (p_node == n_node) {
1469                         n_going = false;
1470                         p_going = false;
1471                     }
1472                 }
1473             } while (n_going || p_going);
1474         }
1476     } else {
1477         // Multiple subpaths have selected nodes:
1478         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1479         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1480         // fix the pear-like shape when sculpting e.g. a ring
1482         // First pass: calculate range
1483         gdouble direct_range = 0;
1484         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1485             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1486             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1487                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1488                 if (node->selected) {
1489                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1490                 }
1491             }
1492         }
1494         // Second pass: actually move nodes
1495         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1496             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1497             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1498                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1499                 if (node->selected) {
1500                     if (direct_range > 1e-6) {
1501                         sp_nodepath_move_node_and_handles (node,
1502                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1503                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1504                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1505                     } else {
1506                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1507                     }
1509                 }
1510             }
1511         }
1512     }
1514     // do not update repr here so that node dragging is acceptably fast
1515     update_object(nodepath);
1519 /**
1520  * Move node selection to point, adjust its and neighbouring handles,
1521  * handle possible snapping, and commit the change with possible undo.
1522  */
1523 void
1524 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1526     if (!nodepath) return;
1528     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1530     if (dx == 0) {
1531         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1532     } else if (dy == 0) {
1533         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1534     } else {
1535         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1536     }
1539 /**
1540  * Move node selection off screen and commit the change.
1541  */
1542 void
1543 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1545     // borrowed from sp_selection_move_screen in selection-chemistry.c
1546     // we find out the current zoom factor and divide deltas by it
1547     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1549     gdouble zoom = desktop->current_zoom();
1550     gdouble zdx = dx / zoom;
1551     gdouble zdy = dy / zoom;
1553     if (!nodepath) return;
1555     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1557     if (dx == 0) {
1558         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1559     } else if (dy == 0) {
1560         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1561     } else {
1562         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1563     }
1566 /**
1567  * Move selected nodes to the absolute position given
1568  */
1569 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1571     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1572         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1573         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1574         sp_node_moveto(n, npos);
1575     }
1577     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1580 /**
1581  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1582  */
1583 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1585     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1586     g_return_val_if_fail(nodepath->selected, no_coord);
1588     // determine coordinate of first selected node
1589     GList *nsel = nodepath->selected;
1590     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1591     NR::Coord coord = n->pos[axis];
1592     bool coincide = true;
1594     // compare it to the coordinates of all the other selected nodes
1595     for (GList *l = nsel->next; l != NULL; l = l->next) {
1596         n = (Inkscape::NodePath::Node *) l->data;
1597         if (n->pos[axis] != coord) {
1598             coincide = false;
1599         }
1600     }
1601     if (coincide) {
1602         return coord;
1603     } else {
1604         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1605         // currently we return the coordinate of the bounding box midpoint because I don't know how
1606         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1607         return bbox.midpoint()[axis];
1608     }
1611 /** If they don't yet exist, creates knot and line for the given side of the node */
1612 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1614     if (!side->knot) {
1615         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"));
1617         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1618         side->knot->setSize (7);
1619         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1620         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1621         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1622         sp_knot_update_ctrl(side->knot);
1624         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1625         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1626         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1627         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1628         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1629         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1630     }
1632     if (!side->line) {
1633         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1634                                         SP_TYPE_CTRLLINE, NULL);
1635     }
1638 /**
1639  * Ensure the given handle of the node is visible/invisible, update its screen position
1640  */
1641 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1643     g_assert(node != NULL);
1645    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1646     NRPathcode code = sp_node_path_code_from_side(node, side);
1648     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1650     if (show_handle) {
1651         if (!side->knot) { // No handle knot at all
1652             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1653             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1654             side->knot->pos = side->pos;
1655             if (side->knot->item)
1656                 SP_CTRL(side->knot->item)->moveto(side->pos);
1657             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1658             sp_knot_show(side->knot);
1659         } else {
1660             if (side->knot->pos != side->pos) { // only if it's really moved
1661                 if (fire_move_signals) {
1662                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1663                 } else {
1664                     sp_knot_moveto(side->knot, &side->pos);
1665                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1666                 }
1667             }
1668             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1669                 sp_knot_show(side->knot);
1670             }
1671         }
1672         sp_canvas_item_show(side->line);
1673     } else {
1674         if (side->knot) {
1675             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1676                 sp_knot_hide(side->knot);
1677             }
1678         }
1679         if (side->line) {
1680             sp_canvas_item_hide(side->line);
1681         }
1682     }
1685 /**
1686  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1687  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1688  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1689  * updated; otherwise, just move the knots silently (used in batch moves).
1690  */
1691 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1693     g_assert(node != NULL);
1695     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1696         sp_knot_show(node->knot);
1697     }
1699     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1700         if (fire_move_signals)
1701             sp_knot_set_position(node->knot, &node->pos, 0);
1702         else
1703             sp_knot_moveto(node->knot, &node->pos);
1704     }
1706     gboolean show_handles = node->selected;
1707     if (node->p.other != NULL) {
1708         if (node->p.other->selected) show_handles = TRUE;
1709     }
1710     if (node->n.other != NULL) {
1711         if (node->n.other->selected) show_handles = TRUE;
1712     }
1714     if (node->subpath->nodepath->show_handles == false)
1715         show_handles = FALSE;
1717     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1718     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1721 /**
1722  * Call sp_node_update_handles() for all nodes on subpath.
1723  */
1724 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1726     g_assert(subpath != NULL);
1728     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1729         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1730     }
1733 /**
1734  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1735  */
1736 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1738     g_assert(nodepath != NULL);
1740     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1741         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1742     }
1745 void
1746 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1748     if (nodepath == NULL) return;
1750     nodepath->show_handles = show;
1751     sp_nodepath_update_handles(nodepath);
1754 /**
1755  * Adds all selected nodes in nodepath to list.
1756  */
1757 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1759     StlConv<Node *>::list(l, selected);
1760 /// \todo this adds a copying, rework when the selection becomes a stl list
1763 /**
1764  * Align selected nodes on the specified axis.
1765  */
1766 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1768     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1769         return;
1770     }
1772     if ( !nodepath->selected->next ) { // only one node selected
1773         return;
1774     }
1775    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1776     NR::Point dest(pNode->pos);
1777     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1778         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1779         if (pNode) {
1780             dest[axis] = pNode->pos[axis];
1781             sp_node_moveto(pNode, dest);
1782         }
1783     }
1785     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1788 /// Helper struct.
1789 struct NodeSort
1791    Inkscape::NodePath::Node *_node;
1792     NR::Coord _coord;
1793     /// \todo use vectorof pointers instead of calling copy ctor
1794     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1795         _node(node), _coord(node->pos[axis])
1796     {}
1798 };
1800 static bool operator<(NodeSort const &a, NodeSort const &b)
1802     return (a._coord < b._coord);
1805 /**
1806  * Distribute selected nodes on the specified axis.
1807  */
1808 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1810     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1811         return;
1812     }
1814     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1815         return;
1816     }
1818    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1819     std::vector<NodeSort> sorted;
1820     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1821         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1822         if (pNode) {
1823             NodeSort n(pNode, axis);
1824             sorted.push_back(n);
1825             //dest[axis] = pNode->pos[axis];
1826             //sp_node_moveto(pNode, dest);
1827         }
1828     }
1829     std::sort(sorted.begin(), sorted.end());
1830     unsigned int len = sorted.size();
1831     //overall bboxes span
1832     float dist = (sorted.back()._coord -
1833                   sorted.front()._coord);
1834     //new distance between each bbox
1835     float step = (dist) / (len - 1);
1836     float pos = sorted.front()._coord;
1837     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1838           it < sorted.end();
1839           it ++ )
1840     {
1841         NR::Point dest((*it)._node->pos);
1842         dest[axis] = pos;
1843         sp_node_moveto((*it)._node, dest);
1844         pos += step;
1845     }
1847     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1851 /**
1852  * Call sp_nodepath_line_add_node() for all selected segments.
1853  */
1854 void
1855 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1857     if (!nodepath) {
1858         return;
1859     }
1861     GList *nl = NULL;
1863     int n_added = 0;
1865     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1866        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1867         g_assert(t->selected);
1868         if (t->p.other && t->p.other->selected) {
1869             nl = g_list_prepend(nl, t);
1870         }
1871     }
1873     while (nl) {
1874        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1875        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1876        sp_nodepath_node_select(n, TRUE, FALSE);
1877        n_added ++;
1878        nl = g_list_remove(nl, t);
1879     }
1881     /** \todo fixme: adjust ? */
1882     sp_nodepath_update_handles(nodepath);
1884     if (n_added > 1) {
1885         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1886     } else if (n_added > 0) {
1887         sp_nodepath_update_repr(nodepath, _("Add node"));
1888     }
1890     sp_nodepath_update_statusbar(nodepath);
1893 /**
1894  * Select segment nearest to point
1895  */
1896 void
1897 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1899     if (!nodepath) {
1900         return;
1901     }
1903     sp_nodepath_ensure_livarot_path(nodepath);
1904     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1905     if (!maybe_position) {
1906         return;
1907     }
1908     Path::cut_position position = *maybe_position;
1910     //find segment to segment
1911     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1913     //fixme: this can return NULL, so check before proceeding.
1914     g_return_if_fail(e != NULL);
1916     gboolean force = FALSE;
1917     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1918         force = TRUE;
1919     }
1920     sp_nodepath_node_select(e, (gboolean) toggle, force);
1921     if (e->p.other)
1922         sp_nodepath_node_select(e->p.other, TRUE, force);
1924     sp_nodepath_update_handles(nodepath);
1926     sp_nodepath_update_statusbar(nodepath);
1929 /**
1930  * Add a node nearest to point
1931  */
1932 void
1933 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1935     if (!nodepath) {
1936         return;
1937     }
1939     sp_nodepath_ensure_livarot_path(nodepath);
1940     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1941     if (!maybe_position) {
1942         return;
1943     }
1944     Path::cut_position position = *maybe_position;
1946     //find segment to split
1947     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1949     //don't know why but t seems to flip for lines
1950     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1951         position.t = 1.0 - position.t;
1952     }
1953     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1954     sp_nodepath_node_select(n, FALSE, TRUE);
1956     /* fixme: adjust ? */
1957     sp_nodepath_update_handles(nodepath);
1959     sp_nodepath_update_repr(nodepath, _("Add node"));
1961     sp_nodepath_update_statusbar(nodepath);
1964 /*
1965  * Adjusts a segment so that t moves by a certain delta for dragging
1966  * converts lines to curves
1967  *
1968  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1969  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1970  */
1971 void
1972 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1974     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1976     //fixme: e and e->p can be NULL, so check for those before proceeding
1977     g_return_if_fail(e != NULL);
1978     g_return_if_fail(&e->p != NULL);
1980     /* feel good is an arbitrary parameter that distributes the delta between handles
1981      * if t of the drag point is less than 1/6 distance form the endpoint only
1982      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1983      */
1984     double feel_good;
1985     if (t <= 1.0 / 6.0)
1986         feel_good = 0;
1987     else if (t <= 0.5)
1988         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1989     else if (t <= 5.0 / 6.0)
1990         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1991     else
1992         feel_good = 1;
1994     //if we're dragging a line convert it to a curve
1995     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1996         sp_nodepath_set_line_type(e, NR_CURVETO);
1997     }
1999     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2000     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2001     e->p.other->n.pos += offsetcoord0;
2002     e->p.pos += offsetcoord1;
2004     // adjust handles of adjacent nodes where necessary
2005     sp_node_adjust_handle(e,1);
2006     sp_node_adjust_handle(e->p.other,-1);
2008     sp_nodepath_update_handles(e->subpath->nodepath);
2010     update_object(e->subpath->nodepath);
2012     sp_nodepath_update_statusbar(e->subpath->nodepath);
2016 /**
2017  * Call sp_nodepath_break() for all selected segments.
2018  */
2019 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2021     if (!nodepath) return;
2023     GList *tempin = g_list_copy(nodepath->selected);
2024     GList *temp = NULL;
2025     for (GList *l = tempin; l != NULL; l = l->next) {
2026        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2027        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2028         if (nn == NULL) continue; // no break, no new node
2029         temp = g_list_prepend(temp, nn);
2030     }
2031     g_list_free(tempin);
2033     if (temp) {
2034         sp_nodepath_deselect(nodepath);
2035     }
2036     for (GList *l = temp; l != NULL; l = l->next) {
2037         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2038     }
2040     sp_nodepath_update_handles(nodepath);
2042     sp_nodepath_update_repr(nodepath, _("Break path"));
2045 /**
2046  * Duplicate the selected node(s).
2047  */
2048 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2050     if (!nodepath) {
2051         return;
2052     }
2054     GList *temp = NULL;
2055     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2056        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2057        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2058         if (nn == NULL) continue; // could not duplicate
2059         temp = g_list_prepend(temp, nn);
2060     }
2062     if (temp) {
2063         sp_nodepath_deselect(nodepath);
2064     }
2065     for (GList *l = temp; l != NULL; l = l->next) {
2066         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2067     }
2069     sp_nodepath_update_handles(nodepath);
2071     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2074 /**
2075  *  Internal function to join two nodes by merging them into one.
2076  */
2077 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2079     /* a and b are endpoints */
2081     // if one of the two nodes is mouseovered, fix its position
2082     NR::Point c;
2083     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2084         c = a->pos;
2085     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2086         c = b->pos;
2087     } else {
2088         // otherwise, move joined node to the midpoint
2089         c = (a->pos + b->pos) / 2;
2090     }
2092     if (a->subpath == b->subpath) {
2093        Inkscape::NodePath::SubPath *sp = a->subpath;
2094         sp_nodepath_subpath_close(sp);
2095         sp_node_moveto (sp->first, c);
2097         sp_nodepath_update_handles(sp->nodepath);
2098         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2099         return;
2100     }
2102     /* a and b are separate subpaths */
2103     Inkscape::NodePath::SubPath *sa = a->subpath;
2104     Inkscape::NodePath::SubPath *sb = b->subpath;
2105     NR::Point p;
2106     Inkscape::NodePath::Node *n;
2107     NRPathcode code;
2108     if (a == sa->first) {
2109         // we will now reverse sa, so that a is its last node, not first, and drop that node
2110         p = sa->first->n.pos;
2111         code = (NRPathcode)sa->first->n.other->code;
2112         // create new subpath
2113        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2114        // create a first moveto node on it
2115         n = sa->last;
2116         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2117         n = n->p.other;
2118         if (n == sa->first) n = NULL;
2119         while (n) {
2120             // copy the rest of the nodes from sa to t, going backwards
2121             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2122             n = n->p.other;
2123             if (n == sa->first) n = NULL;
2124         }
2125         // replace sa with t
2126         sp_nodepath_subpath_destroy(sa);
2127         sa = t;
2128     } else if (a == sa->last) {
2129         // a is already last, just drop it
2130         p = sa->last->p.pos;
2131         code = (NRPathcode)sa->last->code;
2132         sp_nodepath_node_destroy(sa->last);
2133     } else {
2134         code = NR_END;
2135         g_assert_not_reached();
2136     }
2138     if (b == sb->first) {
2139         // copy all nodes from b to a, forward 
2140         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2141         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2142             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2143         }
2144     } else if (b == sb->last) {
2145         // copy all nodes from b to a, backward 
2146         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2147         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2148             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2149         }
2150     } else {
2151         g_assert_not_reached();
2152     }
2153     /* and now destroy sb */
2155     sp_nodepath_subpath_destroy(sb);
2157     sp_nodepath_update_handles(sa->nodepath);
2159     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2161     sp_nodepath_update_statusbar(nodepath);
2164 /**
2165  *  Internal function to join two nodes by adding a segment between them.
2166  */
2167 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2169     if (a->subpath == b->subpath) {
2170        Inkscape::NodePath::SubPath *sp = a->subpath;
2172         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2173         sp->closed = TRUE;
2175         sp->first->p.other = sp->last;
2176         sp->last->n.other  = sp->first;
2178         sp_node_handle_mirror_p_to_n(sp->last);
2179         sp_node_handle_mirror_n_to_p(sp->first);
2181         sp->first->code = sp->last->code;
2182         sp->first       = sp->last;
2184         sp_nodepath_update_handles(sp->nodepath);
2186         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2188         return;
2189     }
2191     /* a and b are separate subpaths */
2192     Inkscape::NodePath::SubPath *sa = a->subpath;
2193     Inkscape::NodePath::SubPath *sb = b->subpath;
2195     Inkscape::NodePath::Node *n;
2196     NR::Point p;
2197     NRPathcode code;
2198     if (a == sa->first) {
2199         code = (NRPathcode) sa->first->n.other->code;
2200        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2201         n = sa->last;
2202         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2203         for (n = n->p.other; n != NULL; n = n->p.other) {
2204             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2205         }
2206         sp_nodepath_subpath_destroy(sa);
2207         sa = t;
2208     } else if (a == sa->last) {
2209         code = (NRPathcode)sa->last->code;
2210     } else {
2211         code = NR_END;
2212         g_assert_not_reached();
2213     }
2215     if (b == sb->first) {
2216         n = sb->first;
2217         sp_node_handle_mirror_p_to_n(sa->last);
2218         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2219         sp_node_handle_mirror_n_to_p(sa->last);
2220         for (n = n->n.other; n != NULL; n = n->n.other) {
2221             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2222         }
2223     } else if (b == sb->last) {
2224         n = sb->last;
2225         sp_node_handle_mirror_p_to_n(sa->last);
2226         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2227         sp_node_handle_mirror_n_to_p(sa->last);
2228         for (n = n->p.other; n != NULL; n = n->p.other) {
2229             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2230         }
2231     } else {
2232         g_assert_not_reached();
2233     }
2234     /* and now destroy sb */
2236     sp_nodepath_subpath_destroy(sb);
2238     sp_nodepath_update_handles(sa->nodepath);
2240     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2243 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2245 /**
2246  * Internal function to handle joining two nodes.
2247  */
2248 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2250     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2252     if (g_list_length(nodepath->selected) != 2) {
2253         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2254         return;
2255     }
2257     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2258     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2260     g_assert(a != b);
2261     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2262         // someone tried to join an orphan node (i.e. a single-node subpath).
2263         // this is not worth an error message, just fail silently.
2264         return;
2265     }
2267     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2268         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2269         return;
2270     }
2272     switch(mode) {
2273         case NODE_JOIN_ENDPOINTS:
2274             do_node_selected_join(nodepath, a, b);
2275             break;
2276         case NODE_JOIN_SEGMENT:
2277             do_node_selected_join_segment(nodepath, a, b);
2278             break;
2279     }
2282 /**
2283  *  Join two nodes by merging them into one.
2284  */
2285 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2287     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2290 /**
2291  *  Join two nodes by adding a segment between them.
2292  */
2293 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2295     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2298 /**
2299  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2300  */
2301 void sp_node_delete_preserve(GList *nodes_to_delete)
2303     GSList *nodepaths = NULL;
2305     while (nodes_to_delete) {
2306         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2307         Inkscape::NodePath::SubPath *sp = node->subpath;
2308         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2309         Inkscape::NodePath::Node *sample_cursor = NULL;
2310         Inkscape::NodePath::Node *sample_end = NULL;
2311         Inkscape::NodePath::Node *delete_cursor = node;
2312         bool just_delete = false;
2314         //find the start of this contiguous selection
2315         //move left to the first node that is not selected
2316         //or the start of the non-closed path
2317         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2318             delete_cursor = curr;
2319         }
2321         //just delete at the beginning of an open path
2322         if (!delete_cursor->p.other) {
2323             sample_cursor = delete_cursor;
2324             just_delete = true;
2325         } else {
2326             sample_cursor = delete_cursor->p.other;
2327         }
2329         //calculate points for each segment
2330         int rate = 5;
2331         float period = 1.0 / rate;
2332         std::vector<NR::Point> data;
2333         if (!just_delete) {
2334             data.push_back(sample_cursor->pos);
2335             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2336                 //just delete at the end of an open path
2337                 if (!sp->closed && curr == sp->last) {
2338                     just_delete = true;
2339                     break;
2340                 }
2342                 //sample points on the contiguous selected segment
2343                 NR::Point *bez;
2344                 bez = new NR::Point [4];
2345                 bez[0] = curr->pos;
2346                 bez[1] = curr->n.pos;
2347                 bez[2] = curr->n.other->p.pos;
2348                 bez[3] = curr->n.other->pos;
2349                 for (int i=1; i<rate; i++) {
2350                     gdouble t = i * period;
2351                     NR::Point p = bezier_pt(3, bez, t);
2352                     data.push_back(p);
2353                 }
2354                 data.push_back(curr->n.other->pos);
2356                 sample_end = curr->n.other;
2357                 //break if we've come full circle or hit the end of the selection
2358                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2359                     break;
2360                 }
2361             }
2362         }
2364         if (!just_delete) {
2365             //calculate the best fitting single segment and adjust the endpoints
2366             NR::Point *adata;
2367             adata = new NR::Point [data.size()];
2368             copy(data.begin(), data.end(), adata);
2370             NR::Point *bez;
2371             bez = new NR::Point [4];
2372             //would decreasing error create a better fitting approximation?
2373             gdouble error = 1.0;
2374             gint ret;
2375             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2377             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2378             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2379             //the resulting nodes behave as expected.
2380             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2381                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2382             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2383                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2385             //adjust endpoints
2386             sample_cursor->n.pos = bez[1];
2387             sample_end->p.pos = bez[2];
2388         }
2390         //destroy this contiguous selection
2391         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2392             Inkscape::NodePath::Node *temp = delete_cursor;
2393             if (delete_cursor->n.other == delete_cursor) {
2394                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2395                 delete_cursor = NULL;
2396             } else {
2397                 delete_cursor = delete_cursor->n.other;
2398             }
2399             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2400             sp_nodepath_node_destroy(temp);
2401         }
2403         sp_nodepath_update_handles(nodepath);
2405         if (!g_slist_find(nodepaths, nodepath))
2406             nodepaths = g_slist_prepend (nodepaths, nodepath);
2407     }
2409     for (GSList *i = nodepaths; i; i = i->next) {
2410         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2411         // different nodepaths will give us one undo event per nodepath
2412         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2414         // if the entire nodepath is removed, delete the selected object.
2415         if (nodepath->subpaths == NULL ||
2416             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2417             //at least 2
2418             sp_nodepath_get_node_count(nodepath) < 2) {
2419             SPDocument *document = sp_desktop_document (nodepath->desktop);
2420             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2421             //delete this nodepath's object, not the entire selection! (though at this time, this
2422             //does not matter)
2423             sp_selection_delete();
2424             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2425                               _("Delete nodes"));
2426         } else {
2427             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2428             sp_nodepath_update_statusbar(nodepath);
2429         }
2430     }
2432     g_slist_free (nodepaths);
2435 /**
2436  * Delete one or more selected nodes.
2437  */
2438 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2440     if (!nodepath) return;
2441     if (!nodepath->selected) return;
2443     /** \todo fixme: do it the right way */
2444     while (nodepath->selected) {
2445        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2446         sp_nodepath_node_destroy(node);
2447     }
2450     //clean up the nodepath (such as for trivial subpaths)
2451     sp_nodepath_cleanup(nodepath);
2453     sp_nodepath_update_handles(nodepath);
2455     // if the entire nodepath is removed, delete the selected object.
2456     if (nodepath->subpaths == NULL ||
2457         sp_nodepath_get_node_count(nodepath) < 2) {
2458         SPDocument *document = sp_desktop_document (nodepath->desktop);
2459         sp_selection_delete();
2460         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2461                           _("Delete nodes"));
2462         return;
2463     }
2465     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2467     sp_nodepath_update_statusbar(nodepath);
2470 /**
2471  * Delete one or more segments between two selected nodes.
2472  * This is the code for 'split'.
2473  */
2474 void
2475 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2477    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2478    Inkscape::NodePath::Node *curr, *next;     //Iterators
2480     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2482     if (g_list_length(nodepath->selected) != 2) {
2483         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2484                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2485         return;
2486     }
2488     //Selected nodes, not inclusive
2489    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2490    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2492     if ( ( a==b)                       ||  //same node
2493          (a->subpath  != b->subpath )  ||  //not the same path
2494          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2495          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2496     {
2497         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2498                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2499         return;
2500     }
2502     //###########################################
2503     //# BEGIN EDITS
2504     //###########################################
2505     //##################################
2506     //# CLOSED PATH
2507     //##################################
2508     if (a->subpath->closed) {
2511         gboolean reversed = FALSE;
2513         //Since we can go in a circle, we need to find the shorter distance.
2514         //  a->b or b->a
2515         start = end = NULL;
2516         int distance    = 0;
2517         int minDistance = 0;
2518         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2519             if (curr==b) {
2520                 //printf("a to b:%d\n", distance);
2521                 start = a;//go from a to b
2522                 end   = b;
2523                 minDistance = distance;
2524                 //printf("A to B :\n");
2525                 break;
2526             }
2527             distance++;
2528         }
2530         //try again, the other direction
2531         distance = 0;
2532         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2533             if (curr==a) {
2534                 //printf("b to a:%d\n", distance);
2535                 if (distance < minDistance) {
2536                     start    = b;  //we go from b to a
2537                     end      = a;
2538                     reversed = TRUE;
2539                     //printf("B to A\n");
2540                 }
2541                 break;
2542             }
2543             distance++;
2544         }
2547         //Copy everything from 'end' to 'start' to a new subpath
2548        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2549         for (curr=end ; curr ; curr=curr->n.other) {
2550             NRPathcode code = (NRPathcode) curr->code;
2551             if (curr == end)
2552                 code = NR_MOVETO;
2553             sp_nodepath_node_new(t, NULL,
2554                                  (Inkscape::NodePath::NodeType)curr->type, code,
2555                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2556             if (curr == start)
2557                 break;
2558         }
2559         sp_nodepath_subpath_destroy(a->subpath);
2562     }
2566     //##################################
2567     //# OPEN PATH
2568     //##################################
2569     else {
2571         //We need to get the direction of the list between A and B
2572         //Can we walk from a to b?
2573         start = end = NULL;
2574         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2575             if (curr==b) {
2576                 start = a;  //did it!  we go from a to b
2577                 end   = b;
2578                 //printf("A to B\n");
2579                 break;
2580             }
2581         }
2582         if (!start) {//didn't work?  let's try the other direction
2583             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2584                 if (curr==a) {
2585                     start = b;  //did it!  we go from b to a
2586                     end   = a;
2587                     //printf("B to A\n");
2588                     break;
2589                 }
2590             }
2591         }
2592         if (!start) {
2593             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2594                                                      _("Cannot find path between nodes."));
2595             return;
2596         }
2600         //Copy everything after 'end' to a new subpath
2601        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2602         for (curr=end ; curr ; curr=curr->n.other) {
2603             NRPathcode code = (NRPathcode) curr->code;
2604             if (curr == end)
2605                 code = NR_MOVETO;
2606             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2607                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2608         }
2610         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2611         for (curr = start->n.other ; curr  ; curr=next) {
2612             next = curr->n.other;
2613             sp_nodepath_node_destroy(curr);
2614         }
2616     }
2617     //###########################################
2618     //# END EDITS
2619     //###########################################
2621     //clean up the nodepath (such as for trivial subpaths)
2622     sp_nodepath_cleanup(nodepath);
2624     sp_nodepath_update_handles(nodepath);
2626     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2628     sp_nodepath_update_statusbar(nodepath);
2631 /**
2632  * Call sp_nodepath_set_line() for all selected segments.
2633  */
2634 void
2635 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2637     if (nodepath == NULL) return;
2639     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2640        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2641         g_assert(n->selected);
2642         if (n->p.other && n->p.other->selected) {
2643             sp_nodepath_set_line_type(n, code);
2644         }
2645     }
2647     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2650 /**
2651  * Call sp_nodepath_convert_node_type() for all selected nodes.
2652  */
2653 void
2654 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2656     if (nodepath == NULL) return;
2658     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2660     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2661         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2662     }
2664     sp_nodepath_update_repr(nodepath, _("Change node type"));
2667 /**
2668  * Change select status of node, update its own and neighbour handles.
2669  */
2670 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2672     node->selected = selected;
2674     if (selected) {
2675         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2676         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2677         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2678         sp_knot_update_ctrl(node->knot);
2679     } else {
2680         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2681         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2682         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2683         sp_knot_update_ctrl(node->knot);
2684     }
2686     sp_node_update_handles(node);
2687     if (node->n.other) sp_node_update_handles(node->n.other);
2688     if (node->p.other) sp_node_update_handles(node->p.other);
2691 /**
2692 \brief Select a node
2693 \param node     The node to select
2694 \param incremental   If true, add to selection, otherwise deselect others
2695 \param override   If true, always select this node, otherwise toggle selected status
2696 */
2697 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2699     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2701     if (incremental) {
2702         if (override) {
2703             if (!g_list_find(nodepath->selected, node)) {
2704                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2705             }
2706             sp_node_set_selected(node, TRUE);
2707         } else { // toggle
2708             if (node->selected) {
2709                 g_assert(g_list_find(nodepath->selected, node));
2710                 nodepath->selected = g_list_remove(nodepath->selected, node);
2711             } else {
2712                 g_assert(!g_list_find(nodepath->selected, node));
2713                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2714             }
2715             sp_node_set_selected(node, !node->selected);
2716         }
2717     } else {
2718         sp_nodepath_deselect(nodepath);
2719         nodepath->selected = g_list_prepend(nodepath->selected, node);
2720         sp_node_set_selected(node, TRUE);
2721     }
2723     sp_nodepath_update_statusbar(nodepath);
2727 /**
2728 \brief Deselect all nodes in the nodepath
2729 */
2730 void
2731 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2733     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2735     while (nodepath->selected) {
2736         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2737         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2738     }
2739     sp_nodepath_update_statusbar(nodepath);
2742 /**
2743 \brief Select or invert selection of all nodes in the nodepath
2744 */
2745 void
2746 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2748     if (!nodepath) return;
2750     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2751        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2752         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2753            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2754            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2755         }
2756     }
2759 /**
2760  * If nothing selected, does the same as sp_nodepath_select_all();
2761  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2762  * (i.e., similar to "select all in layer", with the "selected" subpaths
2763  * being treated as "layers" in the path).
2764  */
2765 void
2766 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2768     if (!nodepath) return;
2770     if (g_list_length (nodepath->selected) == 0) {
2771         sp_nodepath_select_all (nodepath, invert);
2772         return;
2773     }
2775     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2776     GSList *subpaths = NULL;
2778     for (GList *l = copy; l != NULL; l = l->next) {
2779         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2780         Inkscape::NodePath::SubPath *subpath = n->subpath;
2781         if (!g_slist_find (subpaths, subpath))
2782             subpaths = g_slist_prepend (subpaths, subpath);
2783     }
2785     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2786         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2787         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2788             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2789             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2790         }
2791     }
2793     g_slist_free (subpaths);
2794     g_list_free (copy);
2797 /**
2798  * \brief Select the node after the last selected; if none is selected,
2799  * select the first within path.
2800  */
2801 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2803     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2805    Inkscape::NodePath::Node *last = NULL;
2806     if (nodepath->selected) {
2807         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2808            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2809             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2810             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2811                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2812                 if (node->selected) {
2813                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2814                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2815                             if (spl->next) { // there's a next subpath
2816                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2817                                 last = subpath_next->first;
2818                             } else if (spl->prev) { // there's a previous subpath
2819                                 last = NULL; // to be set later to the first node of first subpath
2820                             } else {
2821                                 last = node->n.other;
2822                             }
2823                         } else {
2824                             last = node->n.other;
2825                         }
2826                     } else {
2827                         if (node->n.other) {
2828                             last = node->n.other;
2829                         } else {
2830                             if (spl->next) { // there's a next subpath
2831                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2832                                 last = subpath_next->first;
2833                             } else if (spl->prev) { // there's a previous subpath
2834                                 last = NULL; // to be set later to the first node of first subpath
2835                             } else {
2836                                 last = (Inkscape::NodePath::Node *) subpath->first;
2837                             }
2838                         }
2839                     }
2840                 }
2841             }
2842         }
2843         sp_nodepath_deselect(nodepath);
2844     }
2846     if (last) { // there's at least one more node after selected
2847         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2848     } else { // no more nodes, select the first one in first subpath
2849        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2850         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2851     }
2854 /**
2855  * \brief Select the node before the first selected; if none is selected,
2856  * select the last within path
2857  */
2858 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2860     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2862    Inkscape::NodePath::Node *last = NULL;
2863     if (nodepath->selected) {
2864         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2865            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2866             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2867                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2868                 if (node->selected) {
2869                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2870                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2871                             if (spl->prev) { // there's a prev subpath
2872                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2873                                 last = subpath_prev->last;
2874                             } else if (spl->next) { // there's a next subpath
2875                                 last = NULL; // to be set later to the last node of last subpath
2876                             } else {
2877                                 last = node->p.other;
2878                             }
2879                         } else {
2880                             last = node->p.other;
2881                         }
2882                     } else {
2883                         if (node->p.other) {
2884                             last = node->p.other;
2885                         } else {
2886                             if (spl->prev) { // there's a prev subpath
2887                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2888                                 last = subpath_prev->last;
2889                             } else if (spl->next) { // there's a next subpath
2890                                 last = NULL; // to be set later to the last node of last subpath
2891                             } else {
2892                                 last = (Inkscape::NodePath::Node *) subpath->last;
2893                             }
2894                         }
2895                     }
2896                 }
2897             }
2898         }
2899         sp_nodepath_deselect(nodepath);
2900     }
2902     if (last) { // there's at least one more node before selected
2903         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2904     } else { // no more nodes, select the last one in last subpath
2905         GList *spl = g_list_last(nodepath->subpaths);
2906        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2907         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2908     }
2911 /**
2912  * \brief Select all nodes that are within the rectangle.
2913  */
2914 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2916     if (!incremental) {
2917         sp_nodepath_deselect(nodepath);
2918     }
2920     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2921        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2922         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2923            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2925             if (b.contains(node->pos)) {
2926                 sp_nodepath_node_select(node, TRUE, TRUE);
2927             }
2928         }
2929     }
2933 void
2934 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2936     g_assert (n);
2937     g_assert (nodepath);
2938     g_assert (n->subpath->nodepath == nodepath);
2940     if (g_list_length (nodepath->selected) == 0) {
2941         if (grow > 0) {
2942             sp_nodepath_node_select(n, TRUE, TRUE);
2943         }
2944         return;
2945     }
2947     if (g_list_length (nodepath->selected) == 1) {
2948         if (grow < 0) {
2949             sp_nodepath_deselect (nodepath);
2950             return;
2951         }
2952     }
2954         double n_sel_range = 0, p_sel_range = 0;
2955             Inkscape::NodePath::Node *farthest_n_node = n;
2956             Inkscape::NodePath::Node *farthest_p_node = n;
2958         // Calculate ranges
2959         {
2960             double n_range = 0, p_range = 0;
2961             bool n_going = true, p_going = true;
2962             Inkscape::NodePath::Node *n_node = n;
2963             Inkscape::NodePath::Node *p_node = n;
2964             do {
2965                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2966                 if (n_node && n_going)
2967                     n_node = n_node->n.other;
2968                 if (n_node == NULL) {
2969                     n_going = false;
2970                 } else {
2971                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2972                     if (n_node->selected) {
2973                         n_sel_range = n_range;
2974                         farthest_n_node = n_node;
2975                     }
2976                     if (n_node == p_node) {
2977                         n_going = false;
2978                         p_going = false;
2979                     }
2980                 }
2981                 if (p_node && p_going)
2982                     p_node = p_node->p.other;
2983                 if (p_node == NULL) {
2984                     p_going = false;
2985                 } else {
2986                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2987                     if (p_node->selected) {
2988                         p_sel_range = p_range;
2989                         farthest_p_node = p_node;
2990                     }
2991                     if (p_node == n_node) {
2992                         n_going = false;
2993                         p_going = false;
2994                     }
2995                 }
2996             } while (n_going || p_going);
2997         }
2999     if (grow > 0) {
3000         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3001                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3002         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3003                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3004         }
3005     } else {
3006         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3007                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3008         } else if (farthest_p_node && farthest_p_node->selected) {
3009                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3010         }
3011     }
3014 void
3015 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3017     g_assert (n);
3018     g_assert (nodepath);
3019     g_assert (n->subpath->nodepath == nodepath);
3021     if (g_list_length (nodepath->selected) == 0) {
3022         if (grow > 0) {
3023             sp_nodepath_node_select(n, TRUE, TRUE);
3024         }
3025         return;
3026     }
3028     if (g_list_length (nodepath->selected) == 1) {
3029         if (grow < 0) {
3030             sp_nodepath_deselect (nodepath);
3031             return;
3032         }
3033     }
3035     Inkscape::NodePath::Node *farthest_selected = NULL;
3036     double farthest_dist = 0;
3038     Inkscape::NodePath::Node *closest_unselected = NULL;
3039     double closest_dist = NR_HUGE;
3041     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3042        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3043         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3044            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3045            if (node == n)
3046                continue;
3047            if (node->selected) {
3048                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3049                    farthest_dist = NR::L2(node->pos - n->pos);
3050                    farthest_selected = node;
3051                }
3052            } else {
3053                if (NR::L2(node->pos - n->pos) < closest_dist) {
3054                    closest_dist = NR::L2(node->pos - n->pos);
3055                    closest_unselected = node;
3056                }
3057            }
3058         }
3059     }
3061     if (grow > 0) {
3062         if (closest_unselected) {
3063             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3064         }
3065     } else {
3066         if (farthest_selected) {
3067             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3068         }
3069     }
3073 /**
3074 \brief  Saves all nodes' and handles' current positions in their origin members
3075 */
3076 void
3077 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3079     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3080        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3081         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3082            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3083            n->origin = n->pos;
3084            n->p.origin = n->p.pos;
3085            n->n.origin = n->n.pos;
3086         }
3087     }
3090 /**
3091 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3092 */
3093 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3095     if (!nodepath->selected) {
3096         return NULL;
3097     }
3099     GList *r = NULL;
3100     guint i = 0;
3101     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3102        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3103         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3104            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3105             i++;
3106             if (node->selected) {
3107                 r = g_list_append(r, GINT_TO_POINTER(i));
3108             }
3109         }
3110     }
3111     return r;
3114 /**
3115 \brief  Restores selection by selecting nodes whose positions are in the list
3116 */
3117 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3119     sp_nodepath_deselect(nodepath);
3121     guint i = 0;
3122     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3123        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3124         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3125            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3126             i++;
3127             if (g_list_find(r, GINT_TO_POINTER(i))) {
3128                 sp_nodepath_node_select(node, TRUE, TRUE);
3129             }
3130         }
3131     }
3135 /**
3136 \brief Adjusts handle according to node type and line code.
3137 */
3138 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3140     g_assert(node);
3142    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3143    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3145    // nothing to do if we are an end node
3146     if (me->other == NULL) return;
3147     if (other->other == NULL) return;
3149     // nothing to do if we are a cusp node
3150     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3152     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3153     NRPathcode mecode;
3154     if (which_adjust == 1) {
3155         mecode = (NRPathcode)me->other->code;
3156     } else {
3157         mecode = (NRPathcode)node->code;
3158     }
3159     if (mecode == NR_LINETO) return;
3161     if (sp_node_side_is_line(node, other)) {
3162         // other is a line, and we are either smooth or symm
3163        Inkscape::NodePath::Node *othernode = other->other;
3164         double len = NR::L2(me->pos - node->pos);
3165         NR::Point delta = node->pos - othernode->pos;
3166         double linelen = NR::L2(delta);
3167         if (linelen < 1e-18)
3168             return;
3169         me->pos = node->pos + (len / linelen)*delta;
3170         return;
3171     }
3173     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3174         // symmetrize 
3175         me->pos = 2 * node->pos - other->pos;
3176         return;
3177     } else {
3178         // smoothify
3179         double len = NR::L2(me->pos - node->pos);
3180         NR::Point delta = other->pos - node->pos;
3181         double otherlen = NR::L2(delta);
3182         if (otherlen < 1e-18) return;
3183         me->pos = node->pos - (len / otherlen) * delta;
3184     }
3187 /**
3188  \brief Adjusts both handles according to node type and line code
3189  */
3190 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3192     g_assert(node);
3194     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3196     /* we are either smooth or symm */
3198     if (node->p.other == NULL) return;
3199     if (node->n.other == NULL) return;
3201     if (sp_node_side_is_line(node, &node->p)) {
3202         sp_node_adjust_handle(node, 1);
3203         return;
3204     }
3206     if (sp_node_side_is_line(node, &node->n)) {
3207         sp_node_adjust_handle(node, -1);
3208         return;
3209     }
3211     /* both are curves */
3212     NR::Point const delta( node->n.pos - node->p.pos );
3214     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3215         node->p.pos = node->pos - delta / 2;
3216         node->n.pos = node->pos + delta / 2;
3217         return;
3218     }
3220     /* We are smooth */
3221     double plen = NR::L2(node->p.pos - node->pos);
3222     if (plen < 1e-18) return;
3223     double nlen = NR::L2(node->n.pos - node->pos);
3224     if (nlen < 1e-18) return;
3225     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3226     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3229 /**
3230  * Node event callback.
3231  */
3232 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3234     gboolean ret = FALSE;
3235     switch (event->type) {
3236         case GDK_ENTER_NOTIFY:
3237             Inkscape::NodePath::Path::active_node = n;
3238             break;
3239         case GDK_LEAVE_NOTIFY:
3240             Inkscape::NodePath::Path::active_node = NULL;
3241             break;
3242         case GDK_SCROLL:
3243             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3244                 switch (event->scroll.direction) {
3245                     case GDK_SCROLL_UP:
3246                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3247                         break;
3248                     case GDK_SCROLL_DOWN:
3249                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3250                         break;
3251                     default:
3252                         break;
3253                 }
3254                 ret = TRUE;
3255             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3256                 switch (event->scroll.direction) {
3257                     case GDK_SCROLL_UP:
3258                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3259                         break;
3260                     case GDK_SCROLL_DOWN:
3261                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3262                         break;
3263                     default:
3264                         break;
3265                 }
3266                 ret = TRUE;
3267             }
3268             break;
3269         case GDK_KEY_PRESS:
3270             switch (get_group0_keyval (&event->key)) {
3271                 case GDK_space:
3272                     if (event->key.state & GDK_BUTTON1_MASK) {
3273                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3274                         stamp_repr(nodepath);
3275                         ret = TRUE;
3276                     }
3277                     break;
3278                 case GDK_Page_Up:
3279                     if (event->key.state & GDK_CONTROL_MASK) {
3280                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3281                     } else {
3282                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3283                     }
3284                     break;
3285                 case GDK_Page_Down:
3286                     if (event->key.state & GDK_CONTROL_MASK) {
3287                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3288                     } else {
3289                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3290                     }
3291                     break;
3292                 default:
3293                     break;
3294             }
3295             break;
3296         default:
3297             break;
3298     }
3300     return ret;
3303 /**
3304  * Handle keypress on node; directly called.
3305  */
3306 gboolean node_key(GdkEvent *event)
3308     Inkscape::NodePath::Path *np;
3310     // there is no way to verify nodes so set active_node to nil when deleting!!
3311     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3313     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3314         gint ret = FALSE;
3315         switch (get_group0_keyval (&event->key)) {
3316             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3317             case GDK_BackSpace:
3318                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3319                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3320                 sp_nodepath_update_repr(np, _("Delete node"));
3321                 Inkscape::NodePath::Path::active_node = NULL;
3322                 ret = TRUE;
3323                 break;
3324             case GDK_c:
3325                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3326                 ret = TRUE;
3327                 break;
3328             case GDK_s:
3329                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3330                 ret = TRUE;
3331                 break;
3332             case GDK_y:
3333                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3334                 ret = TRUE;
3335                 break;
3336             case GDK_b:
3337                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3338                 ret = TRUE;
3339                 break;
3340         }
3341         return ret;
3342     }
3343     return FALSE;
3346 /**
3347  * Mouseclick on node callback.
3348  */
3349 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3351    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3353     if (state & GDK_CONTROL_MASK) {
3354         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3356         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3357             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3358                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3359             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3360                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3361             } else {
3362                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3363             }
3364             sp_nodepath_update_repr(nodepath, _("Change node type"));
3365             sp_nodepath_update_statusbar(nodepath);
3367         } else { //ctrl+alt+click: delete node
3368             GList *node_to_delete = NULL;
3369             node_to_delete = g_list_append(node_to_delete, n);
3370             sp_node_delete_preserve(node_to_delete);
3371         }
3373     } else {
3374         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3375     }
3378 /**
3379  * Mouse grabbed node callback.
3380  */
3381 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3383    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3385     if (!n->selected) {
3386         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3387     }
3389     n->is_dragging = true;
3390     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3392     sp_nodepath_remember_origins (n->subpath->nodepath);
3395 /**
3396  * Mouse ungrabbed node callback.
3397  */
3398 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3400    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3402    n->dragging_out = NULL;
3403    n->is_dragging = false;
3404    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3406    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3409 /**
3410  * The point on a line, given by its angle, closest to the given point.
3411  * \param p  A point.
3412  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3413  * \param closest  Pointer to the point struct where the result is stored.
3414  * \todo FIXME: use dot product perhaps?
3415  */
3416 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3418     if (a == HUGE_VAL) { // vertical
3419         *closest = NR::Point(0, (*p)[NR::Y]);
3420     } else {
3421         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3422         (*closest)[NR::Y] = a * (*closest)[NR::X];
3423     }
3426 /**
3427  * Distance from the point to a line given by its angle.
3428  * \param p  A point.
3429  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3430  */
3431 static double point_line_distance(NR::Point *p, double a)
3433     NR::Point c;
3434     point_line_closest(p, a, &c);
3435     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]));
3438 /**
3439  * Callback for node "request" signal.
3440  * \todo fixme: This goes to "moved" event? (lauris)
3441  */
3442 static gboolean
3443 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3445     double yn, xn, yp, xp;
3446     double an, ap, na, pa;
3447     double d_an, d_ap, d_na, d_pa;
3448     gboolean collinear = FALSE;
3449     NR::Point c;
3450     NR::Point pr;
3452     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3454     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3456     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3457     if ( (!n->subpath->nodepath->straight_path) &&
3458          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3459            || n->dragging_out ) )
3460     {
3461        NR::Point mouse = (*p);
3463        if (!n->dragging_out) {
3464            // This is the first drag-out event; find out which handle to drag out
3465            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3466            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3468            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3469                return FALSE;
3471            Inkscape::NodePath::NodeSide *opposite;
3472            if (appr_p > appr_n) { // closer to p
3473                n->dragging_out = &n->p;
3474                opposite = &n->n;
3475                n->code = NR_CURVETO;
3476            } else if (appr_p < appr_n) { // closer to n
3477                n->dragging_out = &n->n;
3478                opposite = &n->p;
3479                n->n.other->code = NR_CURVETO;
3480            } else { // p and n nodes are the same
3481                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3482                    n->dragging_out = &n->p;
3483                    opposite = &n->n;
3484                    n->code = NR_CURVETO;
3485                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3486                    n->dragging_out = &n->n;
3487                    opposite = &n->p;
3488                    n->n.other->code = NR_CURVETO;
3489                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3490                    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);
3491                    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);
3492                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3493                        n->dragging_out = &n->n;
3494                        opposite = &n->p;
3495                        n->n.other->code = NR_CURVETO;
3496                    } else { // closer to other's n handle
3497                        n->dragging_out = &n->p;
3498                        opposite = &n->n;
3499                        n->code = NR_CURVETO;
3500                    }
3501                }
3502            }
3504            // if there's another handle, make sure the one we drag out starts parallel to it
3505            if (opposite->pos != n->pos) {
3506                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3507            }
3509            // knots might not be created yet!
3510            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3511            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3512        }
3514        // pass this on to the handle-moved callback
3515        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3516        sp_node_update_handles(n);
3517        return TRUE;
3518    }
3520     if (state & GDK_CONTROL_MASK) { // constrained motion
3522         // calculate relative distances of handles
3523         // n handle:
3524         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3525         xn = n->n.pos[NR::X] - n->pos[NR::X];
3526         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3527         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3528             if (n->n.other) { // if there is the next point
3529                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3530                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3531                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3532             }
3533         }
3534         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3535         if (yn < 0) { xn = -xn; yn = -yn; }
3537         // p handle:
3538         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3539         xp = n->p.pos[NR::X] - n->pos[NR::X];
3540         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3541         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3542             if (n->p.other) {
3543                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3544                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3545                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3546             }
3547         }
3548         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3549         if (yp < 0) { xp = -xp; yp = -yp; }
3551         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3552             // sliding on handles, only if at least one of the handles is non-vertical
3553             // (otherwise it's the same as ctrl+drag anyway)
3555             // calculate angles of the handles
3556             if (xn == 0) {
3557                 if (yn == 0) { // no handle, consider it the continuation of the other one
3558                     an = 0;
3559                     collinear = TRUE;
3560                 }
3561                 else an = 0; // vertical; set the angle to horizontal
3562             } else an = yn/xn;
3564             if (xp == 0) {
3565                 if (yp == 0) { // no handle, consider it the continuation of the other one
3566                     ap = an;
3567                 }
3568                 else ap = 0; // vertical; set the angle to horizontal
3569             } else  ap = yp/xp;
3571             if (collinear) an = ap;
3573             // angles of the perpendiculars; HUGE_VAL means vertical
3574             if (an == 0) na = HUGE_VAL; else na = -1/an;
3575             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3577             // mouse point relative to the node's original pos
3578             pr = (*p) - n->origin;
3580             // distances to the four lines (two handles and two perpendiculars)
3581             d_an = point_line_distance(&pr, an);
3582             d_na = point_line_distance(&pr, na);
3583             d_ap = point_line_distance(&pr, ap);
3584             d_pa = point_line_distance(&pr, pa);
3586             // find out which line is the closest, save its closest point in c
3587             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3588                 point_line_closest(&pr, an, &c);
3589             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3590                 point_line_closest(&pr, ap, &c);
3591             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3592                 point_line_closest(&pr, na, &c);
3593             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3594                 point_line_closest(&pr, pa, &c);
3595             }
3597             // move the node to the closest point
3598             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3599                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3600                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3601                                             true);
3603         } else {  // constraining to hor/vert
3605             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3606                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3607                                                 (*p)[NR::X] - n->pos[NR::X], 
3608                                                 n->origin[NR::Y] - n->pos[NR::Y],
3609                                                 true, 
3610                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3611             } else { // snap to vert
3612                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3613                                                 n->origin[NR::X] - n->pos[NR::X],
3614                                                 (*p)[NR::Y] - n->pos[NR::Y],
3615                                                 true,
3616                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3617             }
3618         }
3619     } else { // move freely
3620         if (n->is_dragging) {
3621             if (state & GDK_MOD1_MASK) { // sculpt
3622                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3623             } else {
3624                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3625                                             (*p)[NR::X] - n->pos[NR::X],
3626                                             (*p)[NR::Y] - n->pos[NR::Y],
3627                                             (state & GDK_SHIFT_MASK) == 0);
3628             }
3629         }
3630     }
3632     n->subpath->nodepath->desktop->scroll_to_point(p);
3634     return TRUE;
3637 /**
3638  * Node handle clicked callback.
3639  */
3640 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3642    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3644     if (state & GDK_CONTROL_MASK) { // "delete" handle
3645         if (n->p.knot == knot) {
3646             n->p.pos = n->pos;
3647         } else if (n->n.knot == knot) {
3648             n->n.pos = n->pos;
3649         }
3650         sp_node_update_handles(n);
3651         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3652         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3653         sp_nodepath_update_statusbar(nodepath);
3655     } else { // just select or add to selection, depending in Shift
3656         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3657     }
3660 /**
3661  * Node handle grabbed callback.
3662  */
3663 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3665    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3667     if (!n->selected) {
3668         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3669     }
3671     // remember the origin point of the handle
3672     if (n->p.knot == knot) {
3673         n->p.origin_radial = n->p.pos - n->pos;
3674     } else if (n->n.knot == knot) {
3675         n->n.origin_radial = n->n.pos - n->pos;
3676     } else {
3677         g_assert_not_reached();
3678     }
3680     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3683 /**
3684  * Node handle ungrabbed callback.
3685  */
3686 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3688    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3690     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3691     if (n->p.knot == knot) {
3692         n->p.origin_radial.a = 0;
3693         sp_knot_set_position(knot, &n->p.pos, state);
3694     } else if (n->n.knot == knot) {
3695         n->n.origin_radial.a = 0;
3696         sp_knot_set_position(knot, &n->n.pos, state);
3697     } else {
3698         g_assert_not_reached();
3699     }
3701     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3704 /**
3705  * Node handle "request" signal callback.
3706  */
3707 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3709     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3711     Inkscape::NodePath::NodeSide *me, *opposite;
3712     gint which;
3713     if (n->p.knot == knot) {
3714         me = &n->p;
3715         opposite = &n->n;
3716         which = -1;
3717     } else if (n->n.knot == knot) {
3718         me = &n->n;
3719         opposite = &n->p;
3720         which = 1;
3721     } else {
3722         me = opposite = NULL;
3723         which = 0;
3724         g_assert_not_reached();
3725     }
3727     SPDesktop *desktop = n->subpath->nodepath->desktop;
3728     SnapManager &m = desktop->namedview->snap_manager;
3729     m.setup(desktop, n->subpath->nodepath->item);
3730     Inkscape::SnappedPoint s;
3731     
3732     if ((state & GDK_SHIFT_MASK) != 0) {
3733         // We will not try to snap when the shift-key is pressed
3734         // so remove the old snap indicator and don't wait for it to time-out  
3735         desktop->snapindicator->remove_snappoint();     
3736     }
3738     Inkscape::NodePath::Node *othernode = opposite->other;
3739     if (othernode) {
3740         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3741             /* We are smooth node adjacent with line */
3742             NR::Point const delta = *p - n->pos;
3743             NR::Coord const len = NR::L2(delta);
3744             Inkscape::NodePath::Node *othernode = opposite->other;
3745             NR::Point const ndelta = n->pos - othernode->pos;
3746             NR::Coord const linelen = NR::L2(ndelta);
3747             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3748                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3749                 (*p) = n->pos + (scal / linelen) * ndelta;
3750             }
3751             if ((state & GDK_SHIFT_MASK) == 0) {
3752                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3753             }
3754         } else {
3755                 if ((state & GDK_SHIFT_MASK) == 0) {
3756                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3757                 }
3758         }
3759     } else {
3760         if ((state & GDK_SHIFT_MASK) == 0) {
3761                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3762         }
3763     }
3764     
3765     s.getPoint(*p);
3766     
3767     sp_node_adjust_handle(n, -which);
3769     return FALSE;
3772 /**
3773  * Node handle moved callback.
3774  */
3775 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3777    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3779    Inkscape::NodePath::NodeSide *me;
3780    Inkscape::NodePath::NodeSide *other;
3781     if (n->p.knot == knot) {
3782         me = &n->p;
3783         other = &n->n;
3784     } else if (n->n.knot == knot) {
3785         me = &n->n;
3786         other = &n->p;
3787     } else {
3788         me = NULL;
3789         other = NULL;
3790         g_assert_not_reached();
3791     }
3793     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3794     Radial rme(me->pos - n->pos);
3795     Radial rother(other->pos - n->pos);
3796     Radial rnew(*p - n->pos);
3798     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3799         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3800         /* 0 interpreted as "no snapping". */
3802         // 1. Snap to the closest PI/snaps angle, starting from zero.
3803         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3805         // 2. Snap to the original angle, its opposite and perpendiculars
3806         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3807             /* The closest PI/2 angle, starting from original angle */
3808             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3810             // Snap to the closest.
3811             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3812                        ? a_snapped
3813                        : a_ortho );
3814         }
3816         // 3. Snap to the angle of the opposite line, if any
3817         Inkscape::NodePath::Node *othernode = other->other;
3818         if (othernode) {
3819             NR::Point other_to_snap(0,0);
3820             if (sp_node_side_is_line(n, other)) {
3821                 other_to_snap = othernode->pos - n->pos;
3822             } else {
3823                 other_to_snap = other->pos - n->pos;
3824             }
3825             if (NR::L2(other_to_snap) > 1e-3) {
3826                 Radial rother_to_snap(other_to_snap);
3827                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3828                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3830                 // Snap to the closest.
3831                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3832                        ? a_snapped
3833                        : a_oppo );
3834             }
3835         }
3837         rnew.a = a_snapped;
3838     }
3840     if (state & GDK_MOD1_MASK) {
3841         // lock handle length
3842         rnew.r = me->origin_radial.r;
3843     }
3845     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3846         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3847         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3848         rother.a += rnew.a - rme.a;
3849         other->pos = NR::Point(rother) + n->pos;
3850         if (other->knot) {
3851             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3852             sp_knot_moveto(other->knot, &other->pos);
3853         }
3854     }
3856     me->pos = NR::Point(rnew) + n->pos;
3857     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3859     // move knot, but without emitting the signal:
3860     // we cannot emit a "moved" signal because we're now processing it
3861     sp_knot_moveto(me->knot, &(me->pos));
3863     update_object(n->subpath->nodepath);
3865     /* status text */
3866     SPDesktop *desktop = n->subpath->nodepath->desktop;
3867     if (!desktop) return;
3868     SPEventContext *ec = desktop->event_context;
3869     if (!ec) return;
3870     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3871     if (!mc) return;
3873     double degrees = 180 / M_PI * rnew.a;
3874     if (degrees > 180) degrees -= 360;
3875     if (degrees < -180) degrees += 360;
3876     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3877         degrees = angle_to_compass (degrees);
3879     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3881     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3882          _("<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);
3884     g_string_free(length, TRUE);
3887 /**
3888  * Node handle event callback.
3889  */
3890 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3892     gboolean ret = FALSE;
3893     switch (event->type) {
3894         case GDK_KEY_PRESS:
3895             switch (get_group0_keyval (&event->key)) {
3896                 case GDK_space:
3897                     if (event->key.state & GDK_BUTTON1_MASK) {
3898                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3899                         stamp_repr(nodepath);
3900                         ret = TRUE;
3901                     }
3902                     break;
3903                 default:
3904                     break;
3905             }
3906             break;
3907         case GDK_ENTER_NOTIFY:
3908             // we use an experimentally determined threshold that seems to work fine
3909             if (NR::L2(n->pos - knot->pos) < 0.75)
3910                 Inkscape::NodePath::Path::active_node = n;
3911             break;
3912         case GDK_LEAVE_NOTIFY:
3913             // we use an experimentally determined threshold that seems to work fine
3914             if (NR::L2(n->pos - knot->pos) < 0.75)
3915                 Inkscape::NodePath::Path::active_node = NULL;
3916             break;
3917         default:
3918             break;
3919     }
3921     return ret;
3924 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3925                                  Radial &rme, Radial &rother, gboolean const both)
3927     rme.a += angle;
3928     if ( both
3929          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3930          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3931     {
3932         rother.a += angle;
3933     }
3936 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3937                                         Radial &rme, Radial &rother, gboolean const both)
3939     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3941     gdouble r;
3942     if ( both
3943          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3944          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3945     {
3946         r = MAX(rme.r, rother.r);
3947     } else {
3948         r = rme.r;
3949     }
3951     gdouble const weird_angle = atan2(norm_angle, r);
3952 /* Bulia says norm_angle is just the visible distance that the
3953  * object's end must travel on the screen.  Left as 'angle' for want of
3954  * a better name.*/
3956     rme.a += weird_angle;
3957     if ( both
3958          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3959          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3960     {
3961         rother.a += weird_angle;
3962     }
3965 /**
3966  * Rotate one node.
3967  */
3968 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3970     Inkscape::NodePath::NodeSide *me, *other;
3971     bool both = false;
3973     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3974     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3976     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3977         me = &(n->p);
3978         other = &(n->n);
3979     } else if (!n->p.other) {
3980         me = &(n->n);
3981         other = &(n->p);
3982     } else {
3983         if (which > 0) { // right handle
3984             if (xn > xp) {
3985                 me = &(n->n);
3986                 other = &(n->p);
3987             } else {
3988                 me = &(n->p);
3989                 other = &(n->n);
3990             }
3991         } else if (which < 0){ // left handle
3992             if (xn <= xp) {
3993                 me = &(n->n);
3994                 other = &(n->p);
3995             } else {
3996                 me = &(n->p);
3997                 other = &(n->n);
3998             }
3999         } else { // both handles
4000             me = &(n->n);
4001             other = &(n->p);
4002             both = true;
4003         }
4004     }
4006     Radial rme(me->pos - n->pos);
4007     Radial rother(other->pos - n->pos);
4009     if (screen) {
4010         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4011     } else {
4012         node_rotate_one_internal (*n, angle, rme, rother, both);
4013     }
4015     me->pos = n->pos + NR::Point(rme);
4017     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4018         other->pos =  n->pos + NR::Point(rother);
4019     }
4021     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4022     // so here we just move all the knots without emitting move signals, for speed
4023     sp_node_update_handles(n, false);
4026 /**
4027  * Rotate selected nodes.
4028  */
4029 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4031     if (!nodepath || !nodepath->selected) return;
4033     if (g_list_length(nodepath->selected) == 1) {
4034        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4035         node_rotate_one (n, angle, which, screen);
4036     } else {
4037        // rotate as an object:
4039         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4040         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4041         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4042             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4043             box.expandTo (n->pos); // contain all selected nodes
4044         }
4046         gdouble rot;
4047         if (screen) {
4048             gdouble const zoom = nodepath->desktop->current_zoom();
4049             gdouble const zmove = angle / zoom;
4050             gdouble const r = NR::L2(box.max() - box.midpoint());
4051             rot = atan2(zmove, r);
4052         } else {
4053             rot = angle;
4054         }
4056         NR::Point rot_center;
4057         if (Inkscape::NodePath::Path::active_node == NULL)
4058             rot_center = box.midpoint();
4059         else
4060             rot_center = Inkscape::NodePath::Path::active_node->pos;
4062         NR::Matrix t =
4063             NR::Matrix (NR::translate(-rot_center)) *
4064             NR::Matrix (NR::rotate(rot)) *
4065             NR::Matrix (NR::translate(rot_center));
4067         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4068             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4069             n->pos *= t;
4070             n->n.pos *= t;
4071             n->p.pos *= t;
4072             sp_node_update_handles(n, false);
4073         }
4074     }
4076     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4079 /**
4080  * Scale one node.
4081  */
4082 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4084     bool both = false;
4085     Inkscape::NodePath::NodeSide *me, *other;
4087     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4088     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4090     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4091         me = &(n->p);
4092         other = &(n->n);
4093         n->code = NR_CURVETO;
4094     } else if (!n->p.other) {
4095         me = &(n->n);
4096         other = &(n->p);
4097         if (n->n.other)
4098             n->n.other->code = NR_CURVETO;
4099     } else {
4100         if (which > 0) { // right handle
4101             if (xn > xp) {
4102                 me = &(n->n);
4103                 other = &(n->p);
4104                 if (n->n.other)
4105                     n->n.other->code = NR_CURVETO;
4106             } else {
4107                 me = &(n->p);
4108                 other = &(n->n);
4109                 n->code = NR_CURVETO;
4110             }
4111         } else if (which < 0){ // left handle
4112             if (xn <= xp) {
4113                 me = &(n->n);
4114                 other = &(n->p);
4115                 if (n->n.other)
4116                     n->n.other->code = NR_CURVETO;
4117             } else {
4118                 me = &(n->p);
4119                 other = &(n->n);
4120                 n->code = NR_CURVETO;
4121             }
4122         } else { // both handles
4123             me = &(n->n);
4124             other = &(n->p);
4125             both = true;
4126             n->code = NR_CURVETO;
4127             if (n->n.other)
4128                 n->n.other->code = NR_CURVETO;
4129         }
4130     }
4132     Radial rme(me->pos - n->pos);
4133     Radial rother(other->pos - n->pos);
4135     rme.r += grow;
4136     if (rme.r < 0) rme.r = 0;
4137     if (rme.a == HUGE_VAL) {
4138         if (me->other) { // if direction is unknown, initialize it towards the next node
4139             Radial rme_next(me->other->pos - n->pos);
4140             rme.a = rme_next.a;
4141         } else { // if there's no next, initialize to 0
4142             rme.a = 0;
4143         }
4144     }
4145     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4146         rother.r += grow;
4147         if (rother.r < 0) rother.r = 0;
4148         if (rother.a == HUGE_VAL) {
4149             rother.a = rme.a + M_PI;
4150         }
4151     }
4153     me->pos = n->pos + NR::Point(rme);
4155     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4156         other->pos = n->pos + NR::Point(rother);
4157     }
4159     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4160     // so here we just move all the knots without emitting move signals, for speed
4161     sp_node_update_handles(n, false);
4164 /**
4165  * Scale selected nodes.
4166  */
4167 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4169     if (!nodepath || !nodepath->selected) return;
4171     if (g_list_length(nodepath->selected) == 1) {
4172         // scale handles of the single selected node
4173         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4174         node_scale_one (n, grow, which);
4175     } else {
4176         // scale nodes as an "object":
4178         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4179         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4180         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4181             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4182             box.expandTo (n->pos); // contain all selected nodes
4183         }
4185         double scale = (box.maxExtent() + grow)/box.maxExtent();
4187         NR::Point scale_center;
4188         if (Inkscape::NodePath::Path::active_node == NULL)
4189             scale_center = box.midpoint();
4190         else
4191             scale_center = Inkscape::NodePath::Path::active_node->pos;
4193         NR::Matrix t =
4194             NR::Matrix (NR::translate(-scale_center)) *
4195             NR::Matrix (NR::scale(scale, scale)) *
4196             NR::Matrix (NR::translate(scale_center));
4198         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4199             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4200             n->pos *= t;
4201             n->n.pos *= t;
4202             n->p.pos *= t;
4203             sp_node_update_handles(n, false);
4204         }
4205     }
4207     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4210 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4212     if (!nodepath) return;
4213     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4216 /**
4217  * Flip selected nodes horizontally/vertically.
4218  */
4219 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4221     if (!nodepath || !nodepath->selected) return;
4223     if (g_list_length(nodepath->selected) == 1 && !center) {
4224         // flip handles of the single selected node
4225         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4226         double temp = n->p.pos[axis];
4227         n->p.pos[axis] = n->n.pos[axis];
4228         n->n.pos[axis] = temp;
4229         sp_node_update_handles(n, false);
4230     } else {
4231         // scale nodes as an "object":
4233         NR::Rect box = sp_node_selected_bbox (nodepath);
4234         if (!center) {
4235             center = box.midpoint();
4236         }
4237         NR::Matrix t =
4238             NR::Matrix (NR::translate(- *center)) *
4239             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4240             NR::Matrix (NR::translate(*center));
4242         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4243             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4244             n->pos *= t;
4245             n->n.pos *= t;
4246             n->p.pos *= t;
4247             sp_node_update_handles(n, false);
4248         }
4249     }
4251     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4254 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4256     g_assert (nodepath->selected);
4258     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4259     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4260     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4261         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4262         box.expandTo (n->pos); // contain all selected nodes
4263     }
4264     return box;
4267 //-----------------------------------------------
4268 /**
4269  * Return new subpath under given nodepath.
4270  */
4271 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4273     g_assert(nodepath);
4274     g_assert(nodepath->desktop);
4276    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4278     s->nodepath = nodepath;
4279     s->closed = FALSE;
4280     s->nodes = NULL;
4281     s->first = NULL;
4282     s->last = NULL;
4284     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4285     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4286     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4288     return s;
4291 /**
4292  * Destroy nodes in subpath, then subpath itself.
4293  */
4294 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4296     g_assert(subpath);
4297     g_assert(subpath->nodepath);
4298     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4300     while (subpath->nodes) {
4301         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4302     }
4304     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4306     g_free(subpath);
4309 /**
4310  * Link head to tail in subpath.
4311  */
4312 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4314     g_assert(!sp->closed);
4315     g_assert(sp->last != sp->first);
4316     g_assert(sp->first->code == NR_MOVETO);
4318     sp->closed = TRUE;
4320     //Link the head to the tail
4321     sp->first->p.other = sp->last;
4322     sp->last->n.other  = sp->first;
4323     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4324     sp->first          = sp->last;
4326     //Remove the extra end node
4327     sp_nodepath_node_destroy(sp->last->n.other);
4330 /**
4331  * Open closed (loopy) subpath at node.
4332  */
4333 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4335     g_assert(sp->closed);
4336     g_assert(n->subpath == sp);
4337     g_assert(sp->first == sp->last);
4339     /* We create new startpoint, current node will become last one */
4341    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4342                                                 &n->pos, &n->pos, &n->n.pos);
4345     sp->closed        = FALSE;
4347     //Unlink to make a head and tail
4348     sp->first         = new_path;
4349     sp->last          = n;
4350     n->n.other        = NULL;
4351     new_path->p.other = NULL;
4354 /**
4355  * Return new node in subpath with given properties.
4356  * \param pos Position of node.
4357  * \param ppos Handle position in previous direction
4358  * \param npos Handle position in previous direction
4359  */
4360 Inkscape::NodePath::Node *
4361 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)
4363     g_assert(sp);
4364     g_assert(sp->nodepath);
4365     g_assert(sp->nodepath->desktop);
4367     if (nodechunk == NULL)
4368         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4370     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4372     n->subpath  = sp;
4374     if (type != Inkscape::NodePath::NODE_NONE) {
4375         // use the type from sodipodi:nodetypes
4376         n->type = type;
4377     } else {
4378         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4379             // points are (almost) collinear
4380             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4381                 // endnode, or a node with a retracted handle
4382                 n->type = Inkscape::NodePath::NODE_CUSP;
4383             } else {
4384                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4385             }
4386         } else {
4387             n->type = Inkscape::NodePath::NODE_CUSP;
4388         }
4389     }
4391     n->code     = code;
4392     n->selected = FALSE;
4393     n->pos      = *pos;
4394     n->p.pos    = *ppos;
4395     n->n.pos    = *npos;
4397     n->dragging_out = NULL;
4399     Inkscape::NodePath::Node *prev;
4400     if (next) {
4401         //g_assert(g_list_find(sp->nodes, next));
4402         prev = next->p.other;
4403     } else {
4404         prev = sp->last;
4405     }
4407     if (prev)
4408         prev->n.other = n;
4409     else
4410         sp->first = n;
4412     if (next)
4413         next->p.other = n;
4414     else
4415         sp->last = n;
4417     n->p.other = prev;
4418     n->n.other = next;
4420     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"));
4421     sp_knot_set_position(n->knot, pos, 0);
4423     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4424     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4425     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4426     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4427     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4428     sp_knot_update_ctrl(n->knot);
4430     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4431     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4432     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4433     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4434     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4435     sp_knot_show(n->knot);
4437     // We only create handle knots and lines on demand
4438     n->p.knot = NULL;
4439     n->p.line = NULL;
4440     n->n.knot = NULL;
4441     n->n.line = NULL;
4443     sp->nodes = g_list_prepend(sp->nodes, n);
4445     return n;
4448 /**
4449  * Destroy node and its knots, link neighbors in subpath.
4450  */
4451 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4453     g_assert(node);
4454     g_assert(node->subpath);
4455     g_assert(SP_IS_KNOT(node->knot));
4457    Inkscape::NodePath::SubPath *sp = node->subpath;
4459     if (node->selected) { // first, deselect
4460         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4461         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4462     }
4464     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4466     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4467     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4468     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4469     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4470     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4471     g_object_unref(G_OBJECT(node->knot));
4473     if (node->p.knot) {
4474         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4475         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4476         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4477         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4478         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4479         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4480         g_object_unref(G_OBJECT(node->p.knot));
4481         node->p.knot = NULL;
4482     }
4484     if (node->n.knot) {
4485         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4486         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4487         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4488         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4489         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4490         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4491         g_object_unref(G_OBJECT(node->n.knot));
4492         node->n.knot = NULL;
4493     }
4495     if (node->p.line)
4496         gtk_object_destroy(GTK_OBJECT(node->p.line));
4497     if (node->n.line)
4498         gtk_object_destroy(GTK_OBJECT(node->n.line));
4500     if (sp->nodes) { // there are others nodes on the subpath
4501         if (sp->closed) {
4502             if (sp->first == node) {
4503                 g_assert(sp->last == node);
4504                 sp->first = node->n.other;
4505                 sp->last = sp->first;
4506             }
4507             node->p.other->n.other = node->n.other;
4508             node->n.other->p.other = node->p.other;
4509         } else {
4510             if (sp->first == node) {
4511                 sp->first = node->n.other;
4512                 sp->first->code = NR_MOVETO;
4513             }
4514             if (sp->last == node) sp->last = node->p.other;
4515             if (node->p.other) node->p.other->n.other = node->n.other;
4516             if (node->n.other) node->n.other->p.other = node->p.other;
4517         }
4518     } else { // this was the last node on subpath
4519         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4520     }
4522     g_mem_chunk_free(nodechunk, node);
4525 /**
4526  * Returns one of the node's two sides.
4527  * \param which Indicates which side.
4528  * \return Pointer to previous node side if which==-1, next if which==1.
4529  */
4530 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4532     g_assert(node);
4534     switch (which) {
4535         case -1:
4536             return &node->p;
4537         case 1:
4538             return &node->n;
4539         default:
4540             break;
4541     }
4543     g_assert_not_reached();
4545     return NULL;
4548 /**
4549  * Return the other side of the node, given one of its sides.
4550  */
4551 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4553     g_assert(node);
4555     if (me == &node->p) return &node->n;
4556     if (me == &node->n) return &node->p;
4558     g_assert_not_reached();
4560     return NULL;
4563 /**
4564  * Return NRPathcode on the given side of the node.
4565  */
4566 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4568     g_assert(node);
4570     if (me == &node->p) {
4571         if (node->p.other) return (NRPathcode)node->code;
4572         return NR_MOVETO;
4573     }
4575     if (me == &node->n) {
4576         if (node->n.other) return (NRPathcode)node->n.other->code;
4577         return NR_MOVETO;
4578     }
4580     g_assert_not_reached();
4582     return NR_END;
4585 /**
4586  * Return node with the given index
4587  */
4588 Inkscape::NodePath::Node *
4589 sp_nodepath_get_node_by_index(int index)
4591     Inkscape::NodePath::Node *e = NULL;
4593     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4594     if (!nodepath) {
4595         return e;
4596     }
4598     //find segment
4599     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4601         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4602         int n = g_list_length(sp->nodes);
4603         if (sp->closed) {
4604             n++;
4605         }
4607         //if the piece belongs to this subpath grab it
4608         //otherwise move onto the next subpath
4609         if (index < n) {
4610             e = sp->first;
4611             for (int i = 0; i < index; ++i) {
4612                 e = e->n.other;
4613             }
4614             break;
4615         } else {
4616             if (sp->closed) {
4617                 index -= (n+1);
4618             } else {
4619                 index -= n;
4620             }
4621         }
4622     }
4624     return e;
4627 /**
4628  * Returns plain text meaning of node type.
4629  */
4630 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4632     unsigned retracted = 0;
4633     bool endnode = false;
4635     for (int which = -1; which <= 1; which += 2) {
4636         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4637         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4638             retracted ++;
4639         if (!side->other)
4640             endnode = true;
4641     }
4643     if (retracted == 0) {
4644         if (endnode) {
4645                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4646                 return _("end node");
4647         } else {
4648             switch (node->type) {
4649                 case Inkscape::NodePath::NODE_CUSP:
4650                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4651                     return _("cusp");
4652                 case Inkscape::NodePath::NODE_SMOOTH:
4653                     // TRANSLATORS: "smooth" is an adjective here
4654                     return _("smooth");
4655                 case Inkscape::NodePath::NODE_SYMM:
4656                     return _("symmetric");
4657             }
4658         }
4659     } else if (retracted == 1) {
4660         if (endnode) {
4661             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4662             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4663         } else {
4664             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4665         }
4666     } else {
4667         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4668     }
4670     return NULL;
4673 /**
4674  * Handles content of statusbar as long as node tool is active.
4675  */
4676 void
4677 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4679     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");
4680     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4682     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4683     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4684     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4685     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4687     SPDesktop *desktop = NULL;
4688     if (nodepath) {
4689         desktop = nodepath->desktop;
4690     } else {
4691         desktop = SP_ACTIVE_DESKTOP;
4692     }
4694     SPEventContext *ec = desktop->event_context;
4695     if (!ec) return;
4696     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4697     if (!mc) return;
4699     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4701     if (selected_nodes == 0) {
4702         Inkscape::Selection *sel = desktop->selection;
4703         if (!sel || sel->isEmpty()) {
4704             mc->setF(Inkscape::NORMAL_MESSAGE,
4705                      _("Select a single object to edit its nodes or handles."));
4706         } else {
4707             if (nodepath) {
4708             mc->setF(Inkscape::NORMAL_MESSAGE,
4709                      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.",
4710                               "<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.",
4711                               total_nodes),
4712                      total_nodes);
4713             } else {
4714                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4715                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4716                 } else {
4717                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4718                 }
4719             }
4720         }
4721     } else if (nodepath && selected_nodes == 1) {
4722         mc->setF(Inkscape::NORMAL_MESSAGE,
4723                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4724                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4725                           total_nodes),
4726                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4727     } else {
4728         if (selected_subpaths > 1) {
4729             mc->setF(Inkscape::NORMAL_MESSAGE,
4730                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4731                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4732                               total_nodes),
4733                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4734         } else {
4735             mc->setF(Inkscape::NORMAL_MESSAGE,
4736                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4737                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4738                               total_nodes),
4739                      selected_nodes, total_nodes, when_selected);
4740         }
4741     }
4744 /*
4745  * returns a *copy* of the curve of that object.
4746  */
4747 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4748     if (!object)
4749         return NULL;
4751     SPCurve *curve = NULL;
4752     if (SP_IS_PATH(object)) {
4753         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4754         curve = curve_new->copy();
4755     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4756         const gchar *svgd = object->repr->attribute(key);
4757         if (svgd) {
4758             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4759             SPCurve *curve_new = new SPCurve(pv);
4760             if (curve_new) {
4761                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4762             }
4763         }
4764     }
4766     return curve;
4769 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4770     if (!np || !np->object || !curve)
4771         return;
4773     if (SP_IS_PATH(np->object)) {
4774         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4775             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4776         } else {
4777             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4778         }
4779     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4780         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4781         if (pathparam) {
4782             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4783             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4784         }
4785     }
4788 SPCanvasItem *
4789 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4790     SPCurve *flash_curve = curve->copy();
4791     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4792     flash_curve->transform(i2d);
4793     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4794     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4795     // unless we also flash the nodes...
4796     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4797     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4798     sp_canvas_item_show(canvasitem);
4799     flash_curve->unref();
4800     return canvasitem;
4803 SPCanvasItem *
4804 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4805     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4806                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4809 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4810     np->show_helperpath = show;
4812     if (show) {
4813         SPCurve *helper_curve = np->curve->copy();
4814         helper_curve->transform(to_2geom(np->i2d));
4815         if (!np->helper_path) {
4816             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4817             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);
4818             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4819             sp_canvas_item_move_to_z(np->helper_path, 0);
4820             sp_canvas_item_show(np->helper_path);
4821         } else {
4822             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4823         }
4824         helper_curve->unref();
4825     } else {
4826         if (np->helper_path) {
4827             GtkObject *temp = np->helper_path;
4828             np->helper_path = NULL;
4829             gtk_object_destroy(temp);
4830         }
4831     }
4834 /* sp_nodepath_make_straight_path:
4835  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4836  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4837  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4838  */
4839 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4840     np->straight_path = true;
4841     np->show_handles = false;
4842     g_message("add code to make the path straight.");
4843     // do sp_nodepath_convert_node_type on all nodes?
4844     // coding tip: search for this text : "Make selected segments lines"
4848 /*
4849   Local Variables:
4850   mode:c++
4851   c-file-style:"stroustrup"
4852   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4853   indent-tabs-mode:nil
4854   fill-column:99
4855   End:
4856 */
4857 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :