Code

when doubleclicking a nodepath to create new knot, use 2geom methods instead of livarot.
[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 <cmath>
55 #include <string>
56 #include "live_effects/lpeobject.h"
57 #include "live_effects/effect.h"
58 #include "live_effects/parameter/parameter.h"
59 #include "live_effects/parameter/path.h"
60 #include "util/mathfns.h"
61 #include "display/snap-indicator.h"
62 #include "snapped-point.h"
64 class NR::Matrix;
66 /// \todo
67 /// evil evil evil. FIXME: conflict of two different Path classes!
68 /// There is a conflict in the namespace between two classes named Path.
69 /// #include "sp-flowtext.h"
70 /// #include "sp-flowregion.h"
72 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
73 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
74 GType sp_flowregion_get_type (void);
75 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
76 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
77 GType sp_flowtext_get_type (void);
78 // end evil workaround
80 #include "helper/stlport.h"
83 /// \todo fixme: Implement these via preferences */
85 #define NODE_FILL          0xbfbfbf00
86 #define NODE_STROKE        0x000000ff
87 #define NODE_FILL_HI       0xff000000
88 #define NODE_STROKE_HI     0x000000ff
89 #define NODE_FILL_SEL      0x0000ffff
90 #define NODE_STROKE_SEL    0x000000ff
91 #define NODE_FILL_SEL_HI   0xff000000
92 #define NODE_STROKE_SEL_HI 0x000000ff
93 #define KNOT_FILL          0xffffffff
94 #define KNOT_STROKE        0x000000ff
95 #define KNOT_FILL_HI       0xff000000
96 #define KNOT_STROKE_HI     0x000000ff
98 static GMemChunk *nodechunk = NULL;
100 /* Creation from object */
102 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
103 static void add_curve_to_subpath( Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c,
104                                   Inkscape::NodePath::NodeType const *t, guint & i, NR::Point & ppos, NRPathcode & pcode  );
105 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
107 /* Object updating */
109 static void stamp_repr(Inkscape::NodePath::Path *np);
110 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
111 static gchar *create_typestr(Inkscape::NodePath::Path *np);
113 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
115 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
117 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
119 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
121 /* Adjust handle placement, if the node or the other handle is moved */
122 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
123 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
125 /* Node event callbacks */
126 static void node_clicked(SPKnot *knot, guint state, gpointer data);
127 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
128 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
129 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
131 /* Handle event callbacks */
132 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
135 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
137 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
139 /* Constructors and destructors */
141 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
142 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
143 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
144 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
145 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
146                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
147 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
149 /* Helpers */
151 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
152 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
153 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
156 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
158 // active_node indicates mouseover node
159 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
161 static void sp_nodepath_draw_helper_curve(Inkscape::NodePath::Path *np, SPDesktop *desktop) {
162     // Draw helper curve
163     if (np->show_helperpath) {
164         SPCurve *helper_curve = np->curve->copy();
165         helper_curve->transform(to_2geom(np->i2d));
166         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
167         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);
168         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
169         sp_canvas_item_move_to_z(np->helper_path, 0);
170         sp_canvas_item_show(np->helper_path);
171         helper_curve->unref();
172     }
175 /**
176  * \brief Creates new nodepath from item
177  */
178 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
180     Inkscape::XML::Node *repr = object->repr;
182     /** \todo
183      * FIXME: remove this. We don't want to edit paths inside flowtext.
184      * Instead we will build our flowtext with cloned paths, so that the
185      * real paths are outside the flowtext and thus editable as usual.
186      */
187     if (SP_IS_FLOWTEXT(object)) {
188         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
189             if SP_IS_FLOWREGION(child) {
190                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
191                 if (grandchild && SP_IS_PATH(grandchild)) {
192                     object = SP_ITEM(grandchild);
193                     break;
194                 }
195             }
196         }
197     }
199     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
201     if (curve == NULL)
202         return NULL;
204     if (curve->get_segment_count() < 1) {
205         curve->unref();
206         return NULL; // prevent crash for one-node paths
207     }
209     //Create new nodepath
210     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
211     if (!np) {
212         curve->unref();
213         return NULL;
214     }
216     // Set defaults
217     np->desktop     = desktop;
218     np->object      = object;
219     np->subpaths    = NULL;
220     np->selected    = NULL;
221     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
222     np->livarot_path = NULL;
223     np->local_change = 0;
224     np->show_handles = show_handles;
225     np->helper_path = NULL;
226     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
227     np->helperpath_width = 1.0;
228     np->curve = curve->copy();
229     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
230     if (SP_IS_LPE_ITEM(object)) {
231         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
232         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
233             np->show_helperpath = true;
234         }            
235     }
236     np->straight_path = false;
237     if (IS_LIVEPATHEFFECT(object) && item) {
238         np->item = item;
239     } else {
240         np->item = SP_ITEM(object);
241     }
243     // we need to update item's transform from the repr here,
244     // because they may be out of sync when we respond
245     // to a change in repr by regenerating nodepath     --bb
246     sp_object_read_attr(SP_OBJECT(np->item), "transform");
248     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
249     np->d2i  = np->i2d.inverse();
251     np->repr = repr;
252     if (repr_key_in) { // apparantly the object is an LPEObject
253         np->repr_key = g_strdup(repr_key_in);
254         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
255         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
256         if (lpeparam) {
257             lpeparam->param_setup_nodepath(np);
258         }
259     } else {
260         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
261         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
262             np->repr_key = g_strdup("inkscape:original-d");
264             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
265             if (lpe) {
266                 lpe->setup_nodepath(np);
267             }
268         } else {
269             np->repr_key = g_strdup("d");
270         }
271     }
273     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
274      * So for example a closed rectangle has a nodetypestring of length 5.
275      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
276     Geom::PathVector const &pathv = curve->get_pathvector();
277     guint length = curve->get_segment_count();
278     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
279         length += pit->empty() ? 0 : 1;
280     }
282     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
283     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
285     // create the subpath(s) from the bpath
286     subpaths_from_pathvector(np, pathv, typestr);
288     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
289     np->subpaths = g_list_reverse(np->subpaths);
291     delete[] typestr;
292     curve->unref();
294     // create the livarot representation from the same item
295     sp_nodepath_ensure_livarot_path(np);
297     sp_nodepath_draw_helper_curve(np, desktop);
299     return np;
302 /**
303  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
304  */
305 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
307     if (!np)  //soft fail, like delete
308         return;
310     while (np->subpaths) {
311         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
312     }
314     //Inform the ShapeEditor that made me, if any, that I am gone.
315     if (np->shape_editor)
316         np->shape_editor->nodepath_destroyed();
318     g_assert(!np->selected);
320     if (np->livarot_path) {
321         delete np->livarot_path;
322         np->livarot_path = NULL;
323     }
325     if (np->helper_path) {
326         GtkObject *temp = np->helper_path;
327         np->helper_path = NULL;
328         gtk_object_destroy(temp);
329     }
330     if (np->curve) {
331         np->curve->unref();
332         np->curve = NULL;
333     }
335     if (np->repr_key) {
336         g_free(np->repr_key);
337         np->repr_key = NULL;
338     }
339     if (np->repr_nodetypes_key) {
340         g_free(np->repr_nodetypes_key);
341         np->repr_nodetypes_key = NULL;
342     }
344     np->desktop = NULL;
346     g_free(np);
350 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
352     if (np && np->livarot_path == NULL) {
353         SPCurve *curve = create_curve(np);
354         np->livarot_path = new Path;
355         np->livarot_path->LoadPathVector(curve->get_pathvector());
357         if (np->livarot_path)
358             np->livarot_path->ConvertWithBackData(0.01);
360         curve->unref();
361     }
365 /**
366  *  Return the node count of a given NodeSubPath.
367  */
368 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
370     if (!subpath)
371         return 0;
372     gint nodeCount = g_list_length(subpath->nodes);
373     return nodeCount;
376 /**
377  *  Return the node count of a given NodePath.
378  */
379 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
381     if (!np)
382         return 0;
383     gint nodeCount = 0;
384     for (GList *item = np->subpaths ; item ; item=item->next) {
385        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
386         nodeCount += g_list_length(subpath->nodes);
387     }
388     return nodeCount;
391 /**
392  *  Return the subpath count of a given NodePath.
393  */
394 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
396     if (!np)
397         return 0;
398     return g_list_length (np->subpaths);
401 /**
402  *  Return the selected node count of a given NodePath.
403  */
404 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
406     if (!np)
407         return 0;
408     return g_list_length (np->selected);
411 /**
412  *  Return the number of subpaths where nodes are selected in a given NodePath.
413  */
414 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
416     if (!np)
417         return 0;
418     if (!np->selected)
419         return 0;
420     if (!np->selected->next)
421         return 1;
422     gint count = 0;
423     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
424         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
425         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
426             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
427             if (node->selected) {
428                 count ++;
429                 break;
430             }
431         }
432     }
433     return count;
436 /**
437  * Clean up a nodepath after editing.
438  *
439  * Currently we are deleting trivial subpaths.
440  */
441 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
443     GList *badSubPaths = NULL;
445     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
446     for (GList *l = nodepath->subpaths; l ; l=l->next) {
447        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
448        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
449             badSubPaths = g_list_append(badSubPaths, sp);
450     }
452     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
453     //also removes the subpath from nodepath->subpaths
454     for (GList *l = badSubPaths; l ; l=l->next) {
455        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
456         sp_nodepath_subpath_destroy(sp);
457     }
459     g_list_free(badSubPaths);
462 /**
463  * Create new nodepaths from pathvector, make it subpaths of np.
464  * \param t The node type array.
465  */
466 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
468     guint i = 0;  // index into node type array
469     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
470         if (pit->empty())
471             continue;  // don't add single knot paths
473         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
475         NR::Point ppos = from_2geom(pit->initialPoint()) * np->i2d;
476         NRPathcode pcode = NR_MOVETO;
478         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
479             add_curve_to_subpath(np, sp, *cit, t, i, ppos, pcode);
480         }
482         if (pit->closed()) {
483             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
484             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
485              * If the length is zero, don't add it to the nodepath. */
486             Geom::Curve const &closing_seg = pit->back_closed();
487             if ( ! closing_seg.isDegenerate() ) {
488                 NR::Point pos = from_2geom(closing_seg.finalPoint()) * np->i2d;
489                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
490             }
492             sp_nodepath_subpath_close(sp);
493         }
494     }
496 // should add initial point of curve with type of previous curve:
497 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,
498                                  NR::Point & ppos, NRPathcode & pcode)
500     if( dynamic_cast<Geom::LineSegment const*>(&c) ||
501         dynamic_cast<Geom::HLineSegment const*>(&c) ||
502         dynamic_cast<Geom::VLineSegment const*>(&c) )
503     {
504         NR::Point pos = from_2geom(c.initialPoint()) * np->i2d;
505         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
506         ppos = from_2geom(c.finalPoint());
507         pcode = NR_LINETO;
508     }
509     else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) {
510         std::vector<Geom::Point> points = cubic_bezier->points();
511         NR::Point pos = from_2geom(points[0]) * np->i2d;
512         NR::Point npos = from_2geom(points[1]) * np->i2d;
513         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
514         ppos = from_2geom(points[2]) * np->i2d;
515         pcode = NR_CURVETO;
516     }
517     else {
518         //this case handles sbasis as well as all other curve types
519         Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1);
521         for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
522             add_curve_to_subpath(np, sp, *iter, t, i, ppos, pcode);
523         }
524     }
528 /**
529  * Convert from sodipodi:nodetypes to new style type array.
530  */
531 static
532 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
534     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
536     guint pos = 0;
538     if (types) {
539         for (guint i = 0; types[i] && ( i < length ); i++) {
540             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
541             if (types[i] != '\0') {
542                 switch (types[i]) {
543                     case 's':
544                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
545                         break;
546                     case 'z':
547                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
548                         break;
549                     case 'c':
550                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
551                         break;
552                     default:
553                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
554                         break;
555                 }
556             }
557         }
558     }
560     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
562     return typestr;
565 /**
566  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
567  * updated but repr is not (for speed). Used during curve and node drag.
568  */
569 static void update_object(Inkscape::NodePath::Path *np)
571     g_assert(np);
573     np->curve->unref();
574     np->curve = create_curve(np);
576     sp_nodepath_set_curve(np, np->curve);
578     if (np->show_helperpath) {
579         SPCurve * helper_curve = np->curve->copy();
580         helper_curve->transform(to_2geom(np->i2d));
581         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
582         helper_curve->unref();
583     }
585     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
586     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
587     np->shape_editor->update_knotholder();
590 /**
591  * Update XML path node with data from path object.
592  */
593 static void update_repr_internal(Inkscape::NodePath::Path *np)
595     g_assert(np);
597     Inkscape::XML::Node *repr = np->object->repr;
599     np->curve->unref();
600     np->curve = create_curve(np);
602     gchar *typestr = create_typestr(np);
603     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
605     // determine if path has an effect applied and write to correct "d" attribute.
606     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
607         np->local_change++;
608         repr->setAttribute(np->repr_key, svgpath);
609     }
611     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
612         np->local_change++;
613         repr->setAttribute(np->repr_nodetypes_key, typestr);
614     }
616     g_free(svgpath);
617     g_free(typestr);
619     if (np->show_helperpath) {
620         SPCurve * helper_curve = np->curve->copy();
621         helper_curve->transform(to_2geom(np->i2d));
622         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
623         helper_curve->unref();
624     }
625  }
627 /**
628  * Update XML path node with data from path object, commit changes forever.
629  */
630 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
632     //fixme: np can be NULL, so check before proceeding
633     g_return_if_fail(np != NULL);
635     if (np->livarot_path) {
636         delete np->livarot_path;
637         np->livarot_path = NULL;
638     }
640     update_repr_internal(np);
641     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
643     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
644                      annotation);
647 /**
648  * Update XML path node with data from path object, commit changes with undo.
649  */
650 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
652     if (np->livarot_path) {
653         delete np->livarot_path;
654         np->livarot_path = NULL;
655     }
657     update_repr_internal(np);
658     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
659                            annotation);
662 /**
663  * Make duplicate of path, replace corresponding XML node in tree, commit.
664  */
665 static void stamp_repr(Inkscape::NodePath::Path *np)
667     g_assert(np);
669     Inkscape::XML::Node *old_repr = np->object->repr;
670     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
672     // remember the position of the item
673     gint pos = old_repr->position();
674     // remember parent
675     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
677     SPCurve *curve = create_curve(np);
678     gchar *typestr = create_typestr(np);
680     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
682     new_repr->setAttribute(np->repr_key, svgpath);
683     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
685     // add the new repr to the parent
686     parent->appendChild(new_repr);
687     // move to the saved position
688     new_repr->setPosition(pos > 0 ? pos : 0);
690     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
691                      _("Stamp"));
693     Inkscape::GC::release(new_repr);
694     g_free(svgpath);
695     g_free(typestr);
696     curve->unref();
699 /**
700  * Create curve from path.
701  */
702 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
704     SPCurve *curve = new SPCurve();
706     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
707        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
708         curve->moveto(sp->first->pos * np->d2i);
709        Inkscape::NodePath::Node *n = sp->first->n.other;
710         while (n) {
711             NR::Point const end_pt = n->pos * np->d2i;
712             switch (n->code) {
713                 case NR_LINETO:
714                     curve->lineto(end_pt);
715                     break;
716                 case NR_CURVETO:
717                     curve->curveto(n->p.other->n.pos * np->d2i,
718                                      n->p.pos * np->d2i,
719                                      end_pt);
720                     break;
721                 default:
722                     g_assert_not_reached();
723                     break;
724             }
725             if (n != sp->last) {
726                 n = n->n.other;
727             } else {
728                 n = NULL;
729             }
730         }
731         if (sp->closed) {
732             curve->closepath();
733         }
734     }
736     return curve;
739 /**
740  * Convert path type string to sodipodi:nodetypes style.
741  */
742 static gchar *create_typestr(Inkscape::NodePath::Path *np)
744     gchar *typestr = g_new(gchar, 32);
745     gint len = 32;
746     gint pos = 0;
748     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
749        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
751         if (pos >= len) {
752             typestr = g_renew(gchar, typestr, len + 32);
753             len += 32;
754         }
756         typestr[pos++] = 'c';
758        Inkscape::NodePath::Node *n;
759         n = sp->first->n.other;
760         while (n) {
761             gchar code;
763             switch (n->type) {
764                 case Inkscape::NodePath::NODE_CUSP:
765                     code = 'c';
766                     break;
767                 case Inkscape::NodePath::NODE_SMOOTH:
768                     code = 's';
769                     break;
770                 case Inkscape::NodePath::NODE_SYMM:
771                     code = 'z';
772                     break;
773                 default:
774                     g_assert_not_reached();
775                     code = '\0';
776                     break;
777             }
779             if (pos >= len) {
780                 typestr = g_renew(gchar, typestr, len + 32);
781                 len += 32;
782             }
784             typestr[pos++] = code;
786             if (n != sp->last) {
787                 n = n->n.other;
788             } else {
789                 n = NULL;
790             }
791         }
792     }
794     if (pos >= len) {
795         typestr = g_renew(gchar, typestr, len + 1);
796         len += 1;
797     }
799     typestr[pos++] = '\0';
801     return typestr;
804 /**
805  * Returns current path in context. // later eliminate this function at all!
806  */
807 static Inkscape::NodePath::Path *sp_nodepath_current()
809     if (!SP_ACTIVE_DESKTOP) {
810         return NULL;
811     }
813     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
815     if (!SP_IS_NODE_CONTEXT(event_context)) {
816         return NULL;
817     }
819     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
824 /**
825  \brief Fills node and handle positions for three nodes, splitting line
826   marked by end at distance t.
827  */
828 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
830     g_assert(new_path != NULL);
831     g_assert(end      != NULL);
833     g_assert(end->p.other == new_path);
834    Inkscape::NodePath::Node *start = new_path->p.other;
835     g_assert(start);
837     if (end->code == NR_LINETO) {
838         new_path->type =Inkscape::NodePath::NODE_CUSP;
839         new_path->code = NR_LINETO;
840         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
841     } else {
842         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
843         new_path->code = NR_CURVETO;
844         gdouble s      = 1 - t;
845         for (int dim = 0; dim < 2; dim++) {
846             NR::Coord const f000 = start->pos[dim];
847             NR::Coord const f001 = start->n.pos[dim];
848             NR::Coord const f011 = end->p.pos[dim];
849             NR::Coord const f111 = end->pos[dim];
850             NR::Coord const f00t = s * f000 + t * f001;
851             NR::Coord const f01t = s * f001 + t * f011;
852             NR::Coord const f11t = s * f011 + t * f111;
853             NR::Coord const f0tt = s * f00t + t * f01t;
854             NR::Coord const f1tt = s * f01t + t * f11t;
855             NR::Coord const fttt = s * f0tt + t * f1tt;
856             start->n.pos[dim]    = f00t;
857             new_path->p.pos[dim] = f0tt;
858             new_path->pos[dim]   = fttt;
859             new_path->n.pos[dim] = f1tt;
860             end->p.pos[dim]      = f11t;
861         }
862     }
865 /**
866  * Adds new node on direct line between two nodes, activates handles of all
867  * three nodes.
868  */
869 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
871     g_assert(end);
872     g_assert(end->subpath);
873     g_assert(g_list_find(end->subpath->nodes, end));
875    Inkscape::NodePath::Node *start = end->p.other;
876     g_assert( start->n.other == end );
877    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
878                                                end,
879                                                (NRPathcode)end->code == NR_LINETO?
880                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
881                                                (NRPathcode)end->code,
882                                                &start->pos, &start->pos, &start->n.pos);
883     sp_nodepath_line_midpoint(newnode, end, t);
885     sp_node_adjust_handles(start);
886     sp_node_update_handles(start);
887     sp_node_update_handles(newnode);
888     sp_node_adjust_handles(end);
889     sp_node_update_handles(end);
891     return newnode;
894 /**
895 \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
896 */
897 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
899     g_assert(node);
900     g_assert(node->subpath);
901     g_assert(g_list_find(node->subpath->nodes, node));
903    Inkscape::NodePath::SubPath *sp = node->subpath;
904     Inkscape::NodePath::Path *np    = sp->nodepath;
906     if (sp->closed) {
907         sp_nodepath_subpath_open(sp, node);
908         return sp->first;
909     } else {
910         // no break for end nodes
911         if (node == sp->first) return NULL;
912         if (node == sp->last ) return NULL;
914         // create a new subpath
915        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
917         // duplicate the break node as start of the new subpath
918         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
920         // attach rest of curve to new node
921         g_assert(node->n.other);
922         newnode->n.other = node->n.other; node->n.other = NULL;
923         newnode->n.other->p.other = newnode;
924         newsubpath->last = sp->last;
925         sp->last = node;
926         node = newnode;
927         while (node->n.other) {
928             node = node->n.other;
929             node->subpath = newsubpath;
930             sp->nodes = g_list_remove(sp->nodes, node);
931             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
932         }
935         return newnode;
936     }
939 /**
940  * Duplicate node and connect to neighbours.
941  */
942 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
944     g_assert(node);
945     g_assert(node->subpath);
946     g_assert(g_list_find(node->subpath->nodes, node));
948    Inkscape::NodePath::SubPath *sp = node->subpath;
950     NRPathcode code = (NRPathcode) node->code;
951     if (code == NR_MOVETO) { // if node is the endnode,
952         node->code = NR_LINETO; // new one is inserted before it, so change that to line
953     }
955     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
957     if (!node->n.other || !node->p.other) // if node is an endnode, select it
958         return node;
959     else
960         return newnode; // otherwise select the newly created node
963 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
965     node->p.pos = (node->pos + (node->pos - node->n.pos));
968 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
970     node->n.pos = (node->pos + (node->pos - node->p.pos));
973 /**
974  * Change line type at node, with side effects on neighbours.
975  */
976 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
978     g_assert(end);
979     g_assert(end->subpath);
980     g_assert(end->p.other);
982     if (end->code == static_cast< guint > ( code ) )
983         return;
985    Inkscape::NodePath::Node *start = end->p.other;
987     end->code = code;
989     if (code == NR_LINETO) {
990         if (start->code == NR_LINETO) {
991             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
992         }
993         if (end->n.other) {
994             if (end->n.other->code == NR_LINETO) {
995                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
996             }
997         }
998     } else {
999         NR::Point delta = end->pos - start->pos;
1000         start->n.pos = start->pos + delta / 3;
1001         end->p.pos = end->pos - delta / 3;
1002         sp_node_adjust_handle(start, 1);
1003         sp_node_adjust_handle(end, -1);
1004     }
1006     sp_node_update_handles(start);
1007     sp_node_update_handles(end);
1010 /**
1011  * Change node type, and its handles accordingly.
1012  */
1013 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1015     g_assert(node);
1016     g_assert(node->subpath);
1018     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1019         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1020             type =Inkscape::NodePath::NODE_CUSP;
1021         }
1022     }
1024     node->type = type;
1026     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1027         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1028         node->knot->setSize (node->selected? 11 : 9);
1029         sp_knot_update_ctrl(node->knot);
1030     } else {
1031         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1032         node->knot->setSize (node->selected? 9 : 7);
1033         sp_knot_update_ctrl(node->knot);
1034     }
1036     // if one of handles is mouseovered, preserve its position
1037     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1038         sp_node_adjust_handle(node, 1);
1039     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1040         sp_node_adjust_handle(node, -1);
1041     } else {
1042         sp_node_adjust_handles(node);
1043     }
1045     sp_node_update_handles(node);
1047     sp_nodepath_update_statusbar(node->subpath->nodepath);
1049     return node;
1052 bool
1053 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1055         Inkscape::NodePath::Node *othernode = side->other;
1056         if (!othernode)
1057             return false;
1058         NRPathcode const code = sp_node_path_code_from_side(node, side);
1059         if (code == NR_LINETO)
1060             return true;
1061         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1062         if (&node->p == side) {
1063             other_to_me = &othernode->n;
1064         } else if (&node->n == side) {
1065             other_to_me = &othernode->p;
1066         } 
1067         if (!other_to_me)
1068             return false;
1069         bool is_line = 
1070              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1071               NR::L2(node->pos - side->pos) < 1e-6);
1072         return is_line;
1075 /**
1076  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1077  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1078  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1079  * If already cusp and set to cusp, retracts handles.
1080 */
1081 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1083     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1085 /* 
1086   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1087  
1088         if (two_handles) {
1089             // do nothing, adjust_handles called via set_node_type will line them up
1090         } else if (one_handle) {
1091             if (opposite_to_handle_is_line) {
1092                 if (lined_up) {
1093                     // already half-smooth; pull opposite handle too making it fully smooth
1094                 } else {
1095                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1096                 }
1097             } else {
1098                 // pull opposite handle in line with the existing one
1099             }
1100         } else if (no_handles) {
1101             if (both_segments_are_lines OR both_segments_are_curves) {
1102                 //pull both handles
1103             } else {
1104                 // pull the handle opposite to line segment, making node half-smooth
1105             }
1106         }
1107 */
1108         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1109         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1110         bool p_is_line = sp_node_side_is_line(node, &node->p);
1111         bool n_is_line = sp_node_side_is_line(node, &node->n);
1113         if (p_has_handle && n_has_handle) {
1114             // do nothing, adjust_handles will line them up
1115         } else if (p_has_handle || n_has_handle) {
1116             if (p_has_handle && n_is_line) {
1117                 Radial line (node->n.other->pos - node->pos);
1118                 Radial handle (node->pos - node->p.pos);
1119                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1120                     // already half-smooth; pull opposite handle too making it fully smooth
1121                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1122                 } else {
1123                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1124                 }
1125             } else if (n_has_handle && p_is_line) {
1126                 Radial line (node->p.other->pos - node->pos);
1127                 Radial handle (node->pos - node->n.pos);
1128                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1129                     // already half-smooth; pull opposite handle too making it fully smooth
1130                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1131                 } else {
1132                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1133                 }
1134             } else if (p_has_handle && node->n.other) {
1135                 // pull n handle
1136                 node->n.other->code = NR_CURVETO;
1137                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1138                     NR::L2(node->p.pos - node->pos) :
1139                     NR::L2(node->n.other->pos - node->pos) / 3;
1140                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1141             } else if (n_has_handle && node->p.other) {
1142                 // pull p handle
1143                 node->code = NR_CURVETO;
1144                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1145                     NR::L2(node->n.pos - node->pos) :
1146                     NR::L2(node->p.other->pos - node->pos) / 3;
1147                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1148             }
1149         } else if (!p_has_handle && !n_has_handle) {
1150             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1151                 // no handles, but both segments are either lnes or curves:
1152                 //pull both handles
1154                 // convert both to curves:
1155                 node->code = NR_CURVETO;
1156                 node->n.other->code = NR_CURVETO;
1158                 NR::Point leg_prev = node->pos - node->p.other->pos;
1159                 NR::Point leg_next = node->pos - node->n.other->pos;
1161                 double norm_leg_prev = L2(leg_prev);
1162                 double norm_leg_next = L2(leg_next);
1164                 NR::Point delta;
1165                 if (norm_leg_next > 0.0) {
1166                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1167                     (&delta)->normalize();
1168                 }
1170                 if (type == Inkscape::NodePath::NODE_SYMM) {
1171                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1172                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1173                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1174                 } else {
1175                     // length of handle is proportional to distance to adjacent node
1176                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1177                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1178                 }
1180             } else {
1181                 // pull the handle opposite to line segment, making it half-smooth
1182                 if (p_is_line && node->n.other) {
1183                     if (type != Inkscape::NodePath::NODE_SYMM) {
1184                         // pull n handle
1185                         node->n.other->code = NR_CURVETO;
1186                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1187                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1188                     }
1189                 } else if (n_is_line && node->p.other) {
1190                     if (type != Inkscape::NodePath::NODE_SYMM) {
1191                         // pull p handle
1192                         node->code = NR_CURVETO;
1193                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1194                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1195                     }
1196                 }
1197             }
1198         }
1199     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1200         // cusping a cusp: retract nodes
1201         node->p.pos = node->pos;
1202         node->n.pos = node->pos;
1203     }
1205     sp_nodepath_set_node_type (node, type);
1208 /**
1209  * Move node to point, and adjust its and neighbouring handles.
1210  */
1211 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1213     NR::Point delta = p - node->pos;
1214     node->pos = p;
1216     node->p.pos += delta;
1217     node->n.pos += delta;
1219     Inkscape::NodePath::Node *node_p = NULL;
1220     Inkscape::NodePath::Node *node_n = NULL;
1222     if (node->p.other) {
1223         if (node->code == NR_LINETO) {
1224             sp_node_adjust_handle(node, 1);
1225             sp_node_adjust_handle(node->p.other, -1);
1226             node_p = node->p.other;
1227         }
1228     }
1229     if (node->n.other) {
1230         if (node->n.other->code == NR_LINETO) {
1231             sp_node_adjust_handle(node, -1);
1232             sp_node_adjust_handle(node->n.other, 1);
1233             node_n = node->n.other;
1234         }
1235     }
1237     // this function is only called from batch movers that will update display at the end
1238     // themselves, so here we just move all the knots without emitting move signals, for speed
1239     sp_node_update_handles(node, false);
1240     if (node_n) {
1241         sp_node_update_handles(node_n, false);
1242     }
1243     if (node_p) {
1244         sp_node_update_handles(node_p, false);
1245     }
1248 /**
1249  * Call sp_node_moveto() for node selection and handle possible snapping.
1250  */
1251 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1252                                             bool const snap, bool constrained = false, 
1253                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1255     NR::Coord best = NR_HUGE;
1256     NR::Point delta(dx, dy);
1257     NR::Point best_pt = delta;
1258     Inkscape::SnappedPoint best_abs;
1259     
1260     if (snap) {    
1261         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1262          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1263          * must provide that information. */
1264           
1265         // Build a list of the unselected nodes to which the snapper should snap 
1266         std::vector<NR::Point> unselected_nodes;
1267         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1268             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1269             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1270                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1271                 if (!node->selected) {
1272                     unselected_nodes.push_back(node->pos);
1273                 }    
1274             }
1275         }        
1276         
1277         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1278         
1279         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1280             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1281             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1282             Inkscape::SnappedPoint s;
1283             if (constrained) {
1284                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1285                 dedicated_constraint.setPoint(n->pos);
1286                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1287             } else {
1288                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1289             }            
1290             if (s.getSnapped() && (s.getDistance() < best)) {
1291                 best = s.getDistance();
1292                 best_abs = s;
1293                 best_pt = s.getPoint() - n->pos;
1294             }
1295         }
1296                         
1297         if (best_abs.getSnapped()) {
1298             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1299         } else {
1300             nodepath->desktop->snapindicator->remove_snappoint();    
1301         }
1302     }
1304     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1305         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1306         sp_node_moveto(n, n->pos + best_pt);
1307     }
1309     // do not update repr here so that node dragging is acceptably fast
1310     update_object(nodepath);
1313 /**
1314 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1315 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1316 near x = 0.
1317  */
1318 double
1319 sculpt_profile (double x, double alpha, guint profile)
1321     if (x >= 1)
1322         return 0;
1323     if (x <= 0)
1324         return 1;
1326     switch (profile) {
1327         case SCULPT_PROFILE_LINEAR:
1328         return 1 - x;
1329         case SCULPT_PROFILE_BELL:
1330         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1331         case SCULPT_PROFILE_ELLIPTIC:
1332         return sqrt(1 - x*x);
1333     }
1335     return 1;
1338 double
1339 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1341     // extremely primitive for now, don't have time to look for the real one
1342     double lower = NR::L2(b - a);
1343     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1344     return (lower + upper)/2;
1347 void
1348 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1350     n->pos = n->origin + delta;
1351     n->n.pos = n->n.origin + delta_n;
1352     n->p.pos = n->p.origin + delta_p;
1353     sp_node_adjust_handles(n);
1354     sp_node_update_handles(n, false);
1357 /**
1358  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1359  * on how far they are from the dragged node n.
1360  */
1361 static void
1362 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1364     g_assert (n);
1365     g_assert (nodepath);
1366     g_assert (n->subpath->nodepath == nodepath);
1368     double pressure = n->knot->pressure;
1369     if (pressure == 0)
1370         pressure = 0.5; // default
1371     pressure = CLAMP (pressure, 0.2, 0.8);
1373     // map pressure to alpha = 1/5 ... 5
1374     double alpha = 1 - 2 * fabs(pressure - 0.5);
1375     if (pressure > 0.5)
1376         alpha = 1/alpha;
1378     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1380     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1381         // Only one subpath has selected nodes:
1382         // use linear mode, where the distance from n to node being dragged is calculated along the path
1384         double n_sel_range = 0, p_sel_range = 0;
1385         guint n_nodes = 0, p_nodes = 0;
1386         guint n_sel_nodes = 0, p_sel_nodes = 0;
1388         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1389         {
1390             double n_range = 0, p_range = 0;
1391             bool n_going = true, p_going = true;
1392             Inkscape::NodePath::Node *n_node = n;
1393             Inkscape::NodePath::Node *p_node = n;
1394             do {
1395                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1396                 if (n_node && n_going)
1397                     n_node = n_node->n.other;
1398                 if (n_node == NULL) {
1399                     n_going = false;
1400                 } else {
1401                     n_nodes ++;
1402                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1403                     if (n_node->selected) {
1404                         n_sel_nodes ++;
1405                         n_sel_range = n_range;
1406                     }
1407                     if (n_node == p_node) {
1408                         n_going = false;
1409                         p_going = false;
1410                     }
1411                 }
1412                 if (p_node && p_going)
1413                     p_node = p_node->p.other;
1414                 if (p_node == NULL) {
1415                     p_going = false;
1416                 } else {
1417                     p_nodes ++;
1418                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1419                     if (p_node->selected) {
1420                         p_sel_nodes ++;
1421                         p_sel_range = p_range;
1422                     }
1423                     if (p_node == n_node) {
1424                         n_going = false;
1425                         p_going = false;
1426                     }
1427                 }
1428             } while (n_going || p_going);
1429         }
1431         // Second pass: actually move nodes in this subpath
1432         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1433         {
1434             double n_range = 0, p_range = 0;
1435             bool n_going = true, p_going = true;
1436             Inkscape::NodePath::Node *n_node = n;
1437             Inkscape::NodePath::Node *p_node = n;
1438             do {
1439                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1440                 if (n_node && n_going)
1441                     n_node = n_node->n.other;
1442                 if (n_node == NULL) {
1443                     n_going = false;
1444                 } else {
1445                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1446                     if (n_node->selected) {
1447                         sp_nodepath_move_node_and_handles (n_node,
1448                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1449                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1450                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1451                     }
1452                     if (n_node == p_node) {
1453                         n_going = false;
1454                         p_going = false;
1455                     }
1456                 }
1457                 if (p_node && p_going)
1458                     p_node = p_node->p.other;
1459                 if (p_node == NULL) {
1460                     p_going = false;
1461                 } else {
1462                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1463                     if (p_node->selected) {
1464                         sp_nodepath_move_node_and_handles (p_node,
1465                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1466                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1467                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1468                     }
1469                     if (p_node == n_node) {
1470                         n_going = false;
1471                         p_going = false;
1472                     }
1473                 }
1474             } while (n_going || p_going);
1475         }
1477     } else {
1478         // Multiple subpaths have selected nodes:
1479         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1480         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1481         // fix the pear-like shape when sculpting e.g. a ring
1483         // First pass: calculate range
1484         gdouble direct_range = 0;
1485         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1486             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1487             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1488                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1489                 if (node->selected) {
1490                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1491                 }
1492             }
1493         }
1495         // Second pass: actually move nodes
1496         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1497             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1498             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1499                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1500                 if (node->selected) {
1501                     if (direct_range > 1e-6) {
1502                         sp_nodepath_move_node_and_handles (node,
1503                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1504                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1505                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1506                     } else {
1507                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1508                     }
1510                 }
1511             }
1512         }
1513     }
1515     // do not update repr here so that node dragging is acceptably fast
1516     update_object(nodepath);
1520 /**
1521  * Move node selection to point, adjust its and neighbouring handles,
1522  * handle possible snapping, and commit the change with possible undo.
1523  */
1524 void
1525 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1527     if (!nodepath) return;
1529     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1531     if (dx == 0) {
1532         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1533     } else if (dy == 0) {
1534         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1535     } else {
1536         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1537     }
1540 /**
1541  * Move node selection off screen and commit the change.
1542  */
1543 void
1544 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1546     // borrowed from sp_selection_move_screen in selection-chemistry.c
1547     // we find out the current zoom factor and divide deltas by it
1548     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1550     gdouble zoom = desktop->current_zoom();
1551     gdouble zdx = dx / zoom;
1552     gdouble zdy = dy / zoom;
1554     if (!nodepath) return;
1556     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1558     if (dx == 0) {
1559         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1560     } else if (dy == 0) {
1561         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1562     } else {
1563         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1564     }
1567 /**
1568  * Move selected nodes to the absolute position given
1569  */
1570 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1572     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1573         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1574         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1575         sp_node_moveto(n, npos);
1576     }
1578     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1581 /**
1582  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1583  */
1584 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1586     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1587     g_return_val_if_fail(nodepath->selected, no_coord);
1589     // determine coordinate of first selected node
1590     GList *nsel = nodepath->selected;
1591     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1592     NR::Coord coord = n->pos[axis];
1593     bool coincide = true;
1595     // compare it to the coordinates of all the other selected nodes
1596     for (GList *l = nsel->next; l != NULL; l = l->next) {
1597         n = (Inkscape::NodePath::Node *) l->data;
1598         if (n->pos[axis] != coord) {
1599             coincide = false;
1600         }
1601     }
1602     if (coincide) {
1603         return coord;
1604     } else {
1605         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1606         // currently we return the coordinate of the bounding box midpoint because I don't know how
1607         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1608         return bbox.midpoint()[axis];
1609     }
1612 /** If they don't yet exist, creates knot and line for the given side of the node */
1613 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1615     if (!side->knot) {
1616         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"));
1618         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1619         side->knot->setSize (7);
1620         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1621         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1622         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1623         sp_knot_update_ctrl(side->knot);
1625         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1626         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1627         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1628         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1629         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1630         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1631     }
1633     if (!side->line) {
1634         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1635                                         SP_TYPE_CTRLLINE, NULL);
1636     }
1639 /**
1640  * Ensure the given handle of the node is visible/invisible, update its screen position
1641  */
1642 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1644     g_assert(node != NULL);
1646    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1647     NRPathcode code = sp_node_path_code_from_side(node, side);
1649     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1651     if (show_handle) {
1652         if (!side->knot) { // No handle knot at all
1653             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1654             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1655             side->knot->pos = side->pos;
1656             if (side->knot->item)
1657                 SP_CTRL(side->knot->item)->moveto(side->pos);
1658             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1659             sp_knot_show(side->knot);
1660         } else {
1661             if (side->knot->pos != side->pos) { // only if it's really moved
1662                 if (fire_move_signals) {
1663                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1664                 } else {
1665                     sp_knot_moveto(side->knot, &side->pos);
1666                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1667                 }
1668             }
1669             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1670                 sp_knot_show(side->knot);
1671             }
1672         }
1673         sp_canvas_item_show(side->line);
1674     } else {
1675         if (side->knot) {
1676             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1677                 sp_knot_hide(side->knot);
1678             }
1679         }
1680         if (side->line) {
1681             sp_canvas_item_hide(side->line);
1682         }
1683     }
1686 /**
1687  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1688  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1689  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1690  * updated; otherwise, just move the knots silently (used in batch moves).
1691  */
1692 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1694     g_assert(node != NULL);
1696     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1697         sp_knot_show(node->knot);
1698     }
1700     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1701         if (fire_move_signals)
1702             sp_knot_set_position(node->knot, &node->pos, 0);
1703         else
1704             sp_knot_moveto(node->knot, &node->pos);
1705     }
1707     gboolean show_handles = node->selected;
1708     if (node->p.other != NULL) {
1709         if (node->p.other->selected) show_handles = TRUE;
1710     }
1711     if (node->n.other != NULL) {
1712         if (node->n.other->selected) show_handles = TRUE;
1713     }
1715     if (node->subpath->nodepath->show_handles == false)
1716         show_handles = FALSE;
1718     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1719     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1722 /**
1723  * Call sp_node_update_handles() for all nodes on subpath.
1724  */
1725 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1727     g_assert(subpath != NULL);
1729     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1730         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1731     }
1734 /**
1735  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1736  */
1737 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1739     g_assert(nodepath != NULL);
1741     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1742         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1743     }
1746 void
1747 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1749     if (nodepath == NULL) return;
1751     nodepath->show_handles = show;
1752     sp_nodepath_update_handles(nodepath);
1755 /**
1756  * Adds all selected nodes in nodepath to list.
1757  */
1758 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1760     StlConv<Node *>::list(l, selected);
1761 /// \todo this adds a copying, rework when the selection becomes a stl list
1764 /**
1765  * Align selected nodes on the specified axis.
1766  */
1767 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1769     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1770         return;
1771     }
1773     if ( !nodepath->selected->next ) { // only one node selected
1774         return;
1775     }
1776    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1777     NR::Point dest(pNode->pos);
1778     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1779         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1780         if (pNode) {
1781             dest[axis] = pNode->pos[axis];
1782             sp_node_moveto(pNode, dest);
1783         }
1784     }
1786     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1789 /// Helper struct.
1790 struct NodeSort
1792    Inkscape::NodePath::Node *_node;
1793     NR::Coord _coord;
1794     /// \todo use vectorof pointers instead of calling copy ctor
1795     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1796         _node(node), _coord(node->pos[axis])
1797     {}
1799 };
1801 static bool operator<(NodeSort const &a, NodeSort const &b)
1803     return (a._coord < b._coord);
1806 /**
1807  * Distribute selected nodes on the specified axis.
1808  */
1809 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1811     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1812         return;
1813     }
1815     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1816         return;
1817     }
1819    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1820     std::vector<NodeSort> sorted;
1821     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1822         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1823         if (pNode) {
1824             NodeSort n(pNode, axis);
1825             sorted.push_back(n);
1826             //dest[axis] = pNode->pos[axis];
1827             //sp_node_moveto(pNode, dest);
1828         }
1829     }
1830     std::sort(sorted.begin(), sorted.end());
1831     unsigned int len = sorted.size();
1832     //overall bboxes span
1833     float dist = (sorted.back()._coord -
1834                   sorted.front()._coord);
1835     //new distance between each bbox
1836     float step = (dist) / (len - 1);
1837     float pos = sorted.front()._coord;
1838     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1839           it < sorted.end();
1840           it ++ )
1841     {
1842         NR::Point dest((*it)._node->pos);
1843         dest[axis] = pos;
1844         sp_node_moveto((*it)._node, dest);
1845         pos += step;
1846     }
1848     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1852 /**
1853  * Call sp_nodepath_line_add_node() for all selected segments.
1854  */
1855 void
1856 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1858     if (!nodepath) {
1859         return;
1860     }
1862     GList *nl = NULL;
1864     int n_added = 0;
1866     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1867        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1868         g_assert(t->selected);
1869         if (t->p.other && t->p.other->selected) {
1870             nl = g_list_prepend(nl, t);
1871         }
1872     }
1874     while (nl) {
1875        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1876        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1877        sp_nodepath_node_select(n, TRUE, FALSE);
1878        n_added ++;
1879        nl = g_list_remove(nl, t);
1880     }
1882     /** \todo fixme: adjust ? */
1883     sp_nodepath_update_handles(nodepath);
1885     if (n_added > 1) {
1886         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1887     } else if (n_added > 0) {
1888         sp_nodepath_update_repr(nodepath, _("Add node"));
1889     }
1891     sp_nodepath_update_statusbar(nodepath);
1894 /**
1895  * Select segment nearest to point
1896  */
1897 void
1898 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1900     if (!nodepath) {
1901         return;
1902     }
1904     sp_nodepath_ensure_livarot_path(nodepath);
1905     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1906     if (!maybe_position) {
1907         return;
1908     }
1909     Path::cut_position position = *maybe_position;
1911     //find segment to segment
1912     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1914     //fixme: this can return NULL, so check before proceeding.
1915     g_return_if_fail(e != NULL);
1917     gboolean force = FALSE;
1918     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1919         force = TRUE;
1920     }
1921     sp_nodepath_node_select(e, (gboolean) toggle, force);
1922     if (e->p.other)
1923         sp_nodepath_node_select(e->p.other, TRUE, force);
1925     sp_nodepath_update_handles(nodepath);
1927     sp_nodepath_update_statusbar(nodepath);
1930 /**
1931  * Add a node nearest to point
1932  */
1933 void
1934 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1936     if (!nodepath) {
1937         return;
1938     }
1940     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1941     Geom::PathVector const &pathv = curve->get_pathvector();
1942     Geom::PathVectorPosition pvpos = nearestPoint(pathv, p);
1944     // calculate index for nodepath's representation.
1945     double int_part;
1946     double t = std::modf(pvpos.t, &int_part);
1947     unsigned int segment_index = (unsigned int)int_part + 1;
1948     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1949         segment_index += pathv[i].size() + 1;
1950         if (pathv[i].closed()) {
1951             segment_index += 1;
1952         }
1953     }
1955     curve->unref();
1957     //find segment to split
1958     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
1960     //don't know why but t seems to flip for lines
1961     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1962         t = 1.0 - t;
1963     }
1965     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
1966     sp_nodepath_node_select(n, FALSE, TRUE);
1968     /* fixme: adjust ? */
1969     sp_nodepath_update_handles(nodepath);
1971     sp_nodepath_update_repr(nodepath, _("Add node"));
1973     sp_nodepath_update_statusbar(nodepath);
1976 /*
1977  * Adjusts a segment so that t moves by a certain delta for dragging
1978  * converts lines to curves
1979  *
1980  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1981  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1982  */
1983 void
1984 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1986     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1988     //fixme: e and e->p can be NULL, so check for those before proceeding
1989     g_return_if_fail(e != NULL);
1990     g_return_if_fail(&e->p != NULL);
1992     /* feel good is an arbitrary parameter that distributes the delta between handles
1993      * if t of the drag point is less than 1/6 distance form the endpoint only
1994      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1995      */
1996     double feel_good;
1997     if (t <= 1.0 / 6.0)
1998         feel_good = 0;
1999     else if (t <= 0.5)
2000         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2001     else if (t <= 5.0 / 6.0)
2002         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2003     else
2004         feel_good = 1;
2006     //if we're dragging a line convert it to a curve
2007     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2008         sp_nodepath_set_line_type(e, NR_CURVETO);
2009     }
2011     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2012     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2013     e->p.other->n.pos += offsetcoord0;
2014     e->p.pos += offsetcoord1;
2016     // adjust handles of adjacent nodes where necessary
2017     sp_node_adjust_handle(e,1);
2018     sp_node_adjust_handle(e->p.other,-1);
2020     sp_nodepath_update_handles(e->subpath->nodepath);
2022     update_object(e->subpath->nodepath);
2024     sp_nodepath_update_statusbar(e->subpath->nodepath);
2028 /**
2029  * Call sp_nodepath_break() for all selected segments.
2030  */
2031 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2033     if (!nodepath) return;
2035     GList *tempin = g_list_copy(nodepath->selected);
2036     GList *temp = NULL;
2037     for (GList *l = tempin; l != NULL; l = l->next) {
2038        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2039        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2040         if (nn == NULL) continue; // no break, no new node
2041         temp = g_list_prepend(temp, nn);
2042     }
2043     g_list_free(tempin);
2045     if (temp) {
2046         sp_nodepath_deselect(nodepath);
2047     }
2048     for (GList *l = temp; l != NULL; l = l->next) {
2049         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2050     }
2052     sp_nodepath_update_handles(nodepath);
2054     sp_nodepath_update_repr(nodepath, _("Break path"));
2057 /**
2058  * Duplicate the selected node(s).
2059  */
2060 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2062     if (!nodepath) {
2063         return;
2064     }
2066     GList *temp = NULL;
2067     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2068        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2069        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2070         if (nn == NULL) continue; // could not duplicate
2071         temp = g_list_prepend(temp, nn);
2072     }
2074     if (temp) {
2075         sp_nodepath_deselect(nodepath);
2076     }
2077     for (GList *l = temp; l != NULL; l = l->next) {
2078         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2079     }
2081     sp_nodepath_update_handles(nodepath);
2083     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2086 /**
2087  *  Internal function to join two nodes by merging them into one.
2088  */
2089 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2091     /* a and b are endpoints */
2093     // if one of the two nodes is mouseovered, fix its position
2094     NR::Point c;
2095     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2096         c = a->pos;
2097     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2098         c = b->pos;
2099     } else {
2100         // otherwise, move joined node to the midpoint
2101         c = (a->pos + b->pos) / 2;
2102     }
2104     if (a->subpath == b->subpath) {
2105        Inkscape::NodePath::SubPath *sp = a->subpath;
2106         sp_nodepath_subpath_close(sp);
2107         sp_node_moveto (sp->first, c);
2109         sp_nodepath_update_handles(sp->nodepath);
2110         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2111         return;
2112     }
2114     /* a and b are separate subpaths */
2115     Inkscape::NodePath::SubPath *sa = a->subpath;
2116     Inkscape::NodePath::SubPath *sb = b->subpath;
2117     NR::Point p;
2118     Inkscape::NodePath::Node *n;
2119     NRPathcode code;
2120     if (a == sa->first) {
2121         // we will now reverse sa, so that a is its last node, not first, and drop that node
2122         p = sa->first->n.pos;
2123         code = (NRPathcode)sa->first->n.other->code;
2124         // create new subpath
2125        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2126        // create a first moveto node on it
2127         n = sa->last;
2128         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2129         n = n->p.other;
2130         if (n == sa->first) n = NULL;
2131         while (n) {
2132             // copy the rest of the nodes from sa to t, going backwards
2133             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2134             n = n->p.other;
2135             if (n == sa->first) n = NULL;
2136         }
2137         // replace sa with t
2138         sp_nodepath_subpath_destroy(sa);
2139         sa = t;
2140     } else if (a == sa->last) {
2141         // a is already last, just drop it
2142         p = sa->last->p.pos;
2143         code = (NRPathcode)sa->last->code;
2144         sp_nodepath_node_destroy(sa->last);
2145     } else {
2146         code = NR_END;
2147         g_assert_not_reached();
2148     }
2150     if (b == sb->first) {
2151         // copy all nodes from b to a, forward 
2152         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2153         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2154             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2155         }
2156     } else if (b == sb->last) {
2157         // copy all nodes from b to a, backward 
2158         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2159         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2160             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2161         }
2162     } else {
2163         g_assert_not_reached();
2164     }
2165     /* and now destroy sb */
2167     sp_nodepath_subpath_destroy(sb);
2169     sp_nodepath_update_handles(sa->nodepath);
2171     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2173     sp_nodepath_update_statusbar(nodepath);
2176 /**
2177  *  Internal function to join two nodes by adding a segment between them.
2178  */
2179 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2181     if (a->subpath == b->subpath) {
2182        Inkscape::NodePath::SubPath *sp = a->subpath;
2184         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2185         sp->closed = TRUE;
2187         sp->first->p.other = sp->last;
2188         sp->last->n.other  = sp->first;
2190         sp_node_handle_mirror_p_to_n(sp->last);
2191         sp_node_handle_mirror_n_to_p(sp->first);
2193         sp->first->code = sp->last->code;
2194         sp->first       = sp->last;
2196         sp_nodepath_update_handles(sp->nodepath);
2198         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2200         return;
2201     }
2203     /* a and b are separate subpaths */
2204     Inkscape::NodePath::SubPath *sa = a->subpath;
2205     Inkscape::NodePath::SubPath *sb = b->subpath;
2207     Inkscape::NodePath::Node *n;
2208     NR::Point p;
2209     NRPathcode code;
2210     if (a == sa->first) {
2211         code = (NRPathcode) sa->first->n.other->code;
2212        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2213         n = sa->last;
2214         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2215         for (n = n->p.other; n != NULL; n = n->p.other) {
2216             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2217         }
2218         sp_nodepath_subpath_destroy(sa);
2219         sa = t;
2220     } else if (a == sa->last) {
2221         code = (NRPathcode)sa->last->code;
2222     } else {
2223         code = NR_END;
2224         g_assert_not_reached();
2225     }
2227     if (b == sb->first) {
2228         n = sb->first;
2229         sp_node_handle_mirror_p_to_n(sa->last);
2230         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2231         sp_node_handle_mirror_n_to_p(sa->last);
2232         for (n = n->n.other; n != NULL; n = n->n.other) {
2233             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2234         }
2235     } else if (b == sb->last) {
2236         n = sb->last;
2237         sp_node_handle_mirror_p_to_n(sa->last);
2238         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2239         sp_node_handle_mirror_n_to_p(sa->last);
2240         for (n = n->p.other; n != NULL; n = n->p.other) {
2241             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2242         }
2243     } else {
2244         g_assert_not_reached();
2245     }
2246     /* and now destroy sb */
2248     sp_nodepath_subpath_destroy(sb);
2250     sp_nodepath_update_handles(sa->nodepath);
2252     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2255 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2257 /**
2258  * Internal function to handle joining two nodes.
2259  */
2260 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2262     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2264     if (g_list_length(nodepath->selected) != 2) {
2265         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2266         return;
2267     }
2269     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2270     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2272     g_assert(a != b);
2273     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2274         // someone tried to join an orphan node (i.e. a single-node subpath).
2275         // this is not worth an error message, just fail silently.
2276         return;
2277     }
2279     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2280         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2281         return;
2282     }
2284     switch(mode) {
2285         case NODE_JOIN_ENDPOINTS:
2286             do_node_selected_join(nodepath, a, b);
2287             break;
2288         case NODE_JOIN_SEGMENT:
2289             do_node_selected_join_segment(nodepath, a, b);
2290             break;
2291     }
2294 /**
2295  *  Join two nodes by merging them into one.
2296  */
2297 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2299     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2302 /**
2303  *  Join two nodes by adding a segment between them.
2304  */
2305 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2307     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2310 /**
2311  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2312  */
2313 void sp_node_delete_preserve(GList *nodes_to_delete)
2315     GSList *nodepaths = NULL;
2317     while (nodes_to_delete) {
2318         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2319         Inkscape::NodePath::SubPath *sp = node->subpath;
2320         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2321         Inkscape::NodePath::Node *sample_cursor = NULL;
2322         Inkscape::NodePath::Node *sample_end = NULL;
2323         Inkscape::NodePath::Node *delete_cursor = node;
2324         bool just_delete = false;
2326         //find the start of this contiguous selection
2327         //move left to the first node that is not selected
2328         //or the start of the non-closed path
2329         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2330             delete_cursor = curr;
2331         }
2333         //just delete at the beginning of an open path
2334         if (!delete_cursor->p.other) {
2335             sample_cursor = delete_cursor;
2336             just_delete = true;
2337         } else {
2338             sample_cursor = delete_cursor->p.other;
2339         }
2341         //calculate points for each segment
2342         int rate = 5;
2343         float period = 1.0 / rate;
2344         std::vector<NR::Point> data;
2345         if (!just_delete) {
2346             data.push_back(sample_cursor->pos);
2347             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2348                 //just delete at the end of an open path
2349                 if (!sp->closed && curr == sp->last) {
2350                     just_delete = true;
2351                     break;
2352                 }
2354                 //sample points on the contiguous selected segment
2355                 NR::Point *bez;
2356                 bez = new NR::Point [4];
2357                 bez[0] = curr->pos;
2358                 bez[1] = curr->n.pos;
2359                 bez[2] = curr->n.other->p.pos;
2360                 bez[3] = curr->n.other->pos;
2361                 for (int i=1; i<rate; i++) {
2362                     gdouble t = i * period;
2363                     NR::Point p = bezier_pt(3, bez, t);
2364                     data.push_back(p);
2365                 }
2366                 data.push_back(curr->n.other->pos);
2368                 sample_end = curr->n.other;
2369                 //break if we've come full circle or hit the end of the selection
2370                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2371                     break;
2372                 }
2373             }
2374         }
2376         if (!just_delete) {
2377             //calculate the best fitting single segment and adjust the endpoints
2378             NR::Point *adata;
2379             adata = new NR::Point [data.size()];
2380             copy(data.begin(), data.end(), adata);
2382             NR::Point *bez;
2383             bez = new NR::Point [4];
2384             //would decreasing error create a better fitting approximation?
2385             gdouble error = 1.0;
2386             gint ret;
2387             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2389             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2390             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2391             //the resulting nodes behave as expected.
2392             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2393                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2394             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2395                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2397             //adjust endpoints
2398             sample_cursor->n.pos = bez[1];
2399             sample_end->p.pos = bez[2];
2400         }
2402         //destroy this contiguous selection
2403         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2404             Inkscape::NodePath::Node *temp = delete_cursor;
2405             if (delete_cursor->n.other == delete_cursor) {
2406                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2407                 delete_cursor = NULL;
2408             } else {
2409                 delete_cursor = delete_cursor->n.other;
2410             }
2411             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2412             sp_nodepath_node_destroy(temp);
2413         }
2415         sp_nodepath_update_handles(nodepath);
2417         if (!g_slist_find(nodepaths, nodepath))
2418             nodepaths = g_slist_prepend (nodepaths, nodepath);
2419     }
2421     for (GSList *i = nodepaths; i; i = i->next) {
2422         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2423         // different nodepaths will give us one undo event per nodepath
2424         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2426         // if the entire nodepath is removed, delete the selected object.
2427         if (nodepath->subpaths == NULL ||
2428             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2429             //at least 2
2430             sp_nodepath_get_node_count(nodepath) < 2) {
2431             SPDocument *document = sp_desktop_document (nodepath->desktop);
2432             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2433             //delete this nodepath's object, not the entire selection! (though at this time, this
2434             //does not matter)
2435             sp_selection_delete();
2436             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2437                               _("Delete nodes"));
2438         } else {
2439             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2440             sp_nodepath_update_statusbar(nodepath);
2441         }
2442     }
2444     g_slist_free (nodepaths);
2447 /**
2448  * Delete one or more selected nodes.
2449  */
2450 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2452     if (!nodepath) return;
2453     if (!nodepath->selected) return;
2455     /** \todo fixme: do it the right way */
2456     while (nodepath->selected) {
2457        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2458         sp_nodepath_node_destroy(node);
2459     }
2462     //clean up the nodepath (such as for trivial subpaths)
2463     sp_nodepath_cleanup(nodepath);
2465     sp_nodepath_update_handles(nodepath);
2467     // if the entire nodepath is removed, delete the selected object.
2468     if (nodepath->subpaths == NULL ||
2469         sp_nodepath_get_node_count(nodepath) < 2) {
2470         SPDocument *document = sp_desktop_document (nodepath->desktop);
2471         sp_selection_delete();
2472         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2473                           _("Delete nodes"));
2474         return;
2475     }
2477     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2479     sp_nodepath_update_statusbar(nodepath);
2482 /**
2483  * Delete one or more segments between two selected nodes.
2484  * This is the code for 'split'.
2485  */
2486 void
2487 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2489    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2490    Inkscape::NodePath::Node *curr, *next;     //Iterators
2492     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2494     if (g_list_length(nodepath->selected) != 2) {
2495         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2496                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2497         return;
2498     }
2500     //Selected nodes, not inclusive
2501    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2502    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2504     if ( ( a==b)                       ||  //same node
2505          (a->subpath  != b->subpath )  ||  //not the same path
2506          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2507          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2508     {
2509         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2510                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2511         return;
2512     }
2514     //###########################################
2515     //# BEGIN EDITS
2516     //###########################################
2517     //##################################
2518     //# CLOSED PATH
2519     //##################################
2520     if (a->subpath->closed) {
2523         gboolean reversed = FALSE;
2525         //Since we can go in a circle, we need to find the shorter distance.
2526         //  a->b or b->a
2527         start = end = NULL;
2528         int distance    = 0;
2529         int minDistance = 0;
2530         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2531             if (curr==b) {
2532                 //printf("a to b:%d\n", distance);
2533                 start = a;//go from a to b
2534                 end   = b;
2535                 minDistance = distance;
2536                 //printf("A to B :\n");
2537                 break;
2538             }
2539             distance++;
2540         }
2542         //try again, the other direction
2543         distance = 0;
2544         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2545             if (curr==a) {
2546                 //printf("b to a:%d\n", distance);
2547                 if (distance < minDistance) {
2548                     start    = b;  //we go from b to a
2549                     end      = a;
2550                     reversed = TRUE;
2551                     //printf("B to A\n");
2552                 }
2553                 break;
2554             }
2555             distance++;
2556         }
2559         //Copy everything from 'end' to 'start' to a new subpath
2560        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2561         for (curr=end ; curr ; curr=curr->n.other) {
2562             NRPathcode code = (NRPathcode) curr->code;
2563             if (curr == end)
2564                 code = NR_MOVETO;
2565             sp_nodepath_node_new(t, NULL,
2566                                  (Inkscape::NodePath::NodeType)curr->type, code,
2567                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2568             if (curr == start)
2569                 break;
2570         }
2571         sp_nodepath_subpath_destroy(a->subpath);
2574     }
2578     //##################################
2579     //# OPEN PATH
2580     //##################################
2581     else {
2583         //We need to get the direction of the list between A and B
2584         //Can we walk from a to b?
2585         start = end = NULL;
2586         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2587             if (curr==b) {
2588                 start = a;  //did it!  we go from a to b
2589                 end   = b;
2590                 //printf("A to B\n");
2591                 break;
2592             }
2593         }
2594         if (!start) {//didn't work?  let's try the other direction
2595             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2596                 if (curr==a) {
2597                     start = b;  //did it!  we go from b to a
2598                     end   = a;
2599                     //printf("B to A\n");
2600                     break;
2601                 }
2602             }
2603         }
2604         if (!start) {
2605             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2606                                                      _("Cannot find path between nodes."));
2607             return;
2608         }
2612         //Copy everything after 'end' to a new subpath
2613        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2614         for (curr=end ; curr ; curr=curr->n.other) {
2615             NRPathcode code = (NRPathcode) curr->code;
2616             if (curr == end)
2617                 code = NR_MOVETO;
2618             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2619                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2620         }
2622         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2623         for (curr = start->n.other ; curr  ; curr=next) {
2624             next = curr->n.other;
2625             sp_nodepath_node_destroy(curr);
2626         }
2628     }
2629     //###########################################
2630     //# END EDITS
2631     //###########################################
2633     //clean up the nodepath (such as for trivial subpaths)
2634     sp_nodepath_cleanup(nodepath);
2636     sp_nodepath_update_handles(nodepath);
2638     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2640     sp_nodepath_update_statusbar(nodepath);
2643 /**
2644  * Call sp_nodepath_set_line() for all selected segments.
2645  */
2646 void
2647 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2649     if (nodepath == NULL) return;
2651     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2652        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2653         g_assert(n->selected);
2654         if (n->p.other && n->p.other->selected) {
2655             sp_nodepath_set_line_type(n, code);
2656         }
2657     }
2659     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2662 /**
2663  * Call sp_nodepath_convert_node_type() for all selected nodes.
2664  */
2665 void
2666 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2668     if (nodepath == NULL) return;
2670     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2672     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2673         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2674     }
2676     sp_nodepath_update_repr(nodepath, _("Change node type"));
2679 /**
2680  * Change select status of node, update its own and neighbour handles.
2681  */
2682 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2684     node->selected = selected;
2686     if (selected) {
2687         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2688         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2689         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2690         sp_knot_update_ctrl(node->knot);
2691     } else {
2692         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2693         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2694         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2695         sp_knot_update_ctrl(node->knot);
2696     }
2698     sp_node_update_handles(node);
2699     if (node->n.other) sp_node_update_handles(node->n.other);
2700     if (node->p.other) sp_node_update_handles(node->p.other);
2703 /**
2704 \brief Select a node
2705 \param node     The node to select
2706 \param incremental   If true, add to selection, otherwise deselect others
2707 \param override   If true, always select this node, otherwise toggle selected status
2708 */
2709 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2711     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2713     if (incremental) {
2714         if (override) {
2715             if (!g_list_find(nodepath->selected, node)) {
2716                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2717             }
2718             sp_node_set_selected(node, TRUE);
2719         } else { // toggle
2720             if (node->selected) {
2721                 g_assert(g_list_find(nodepath->selected, node));
2722                 nodepath->selected = g_list_remove(nodepath->selected, node);
2723             } else {
2724                 g_assert(!g_list_find(nodepath->selected, node));
2725                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2726             }
2727             sp_node_set_selected(node, !node->selected);
2728         }
2729     } else {
2730         sp_nodepath_deselect(nodepath);
2731         nodepath->selected = g_list_prepend(nodepath->selected, node);
2732         sp_node_set_selected(node, TRUE);
2733     }
2735     sp_nodepath_update_statusbar(nodepath);
2739 /**
2740 \brief Deselect all nodes in the nodepath
2741 */
2742 void
2743 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2745     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2747     while (nodepath->selected) {
2748         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2749         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2750     }
2751     sp_nodepath_update_statusbar(nodepath);
2754 /**
2755 \brief Select or invert selection of all nodes in the nodepath
2756 */
2757 void
2758 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2760     if (!nodepath) return;
2762     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2763        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2764         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2765            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2766            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2767         }
2768     }
2771 /**
2772  * If nothing selected, does the same as sp_nodepath_select_all();
2773  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2774  * (i.e., similar to "select all in layer", with the "selected" subpaths
2775  * being treated as "layers" in the path).
2776  */
2777 void
2778 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2780     if (!nodepath) return;
2782     if (g_list_length (nodepath->selected) == 0) {
2783         sp_nodepath_select_all (nodepath, invert);
2784         return;
2785     }
2787     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2788     GSList *subpaths = NULL;
2790     for (GList *l = copy; l != NULL; l = l->next) {
2791         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2792         Inkscape::NodePath::SubPath *subpath = n->subpath;
2793         if (!g_slist_find (subpaths, subpath))
2794             subpaths = g_slist_prepend (subpaths, subpath);
2795     }
2797     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2798         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2799         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2800             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2801             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2802         }
2803     }
2805     g_slist_free (subpaths);
2806     g_list_free (copy);
2809 /**
2810  * \brief Select the node after the last selected; if none is selected,
2811  * select the first within path.
2812  */
2813 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2815     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2817    Inkscape::NodePath::Node *last = NULL;
2818     if (nodepath->selected) {
2819         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2820            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2821             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2822             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2823                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2824                 if (node->selected) {
2825                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2826                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2827                             if (spl->next) { // there's a next subpath
2828                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2829                                 last = subpath_next->first;
2830                             } else if (spl->prev) { // there's a previous subpath
2831                                 last = NULL; // to be set later to the first node of first subpath
2832                             } else {
2833                                 last = node->n.other;
2834                             }
2835                         } else {
2836                             last = node->n.other;
2837                         }
2838                     } else {
2839                         if (node->n.other) {
2840                             last = node->n.other;
2841                         } else {
2842                             if (spl->next) { // there's a next subpath
2843                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2844                                 last = subpath_next->first;
2845                             } else if (spl->prev) { // there's a previous subpath
2846                                 last = NULL; // to be set later to the first node of first subpath
2847                             } else {
2848                                 last = (Inkscape::NodePath::Node *) subpath->first;
2849                             }
2850                         }
2851                     }
2852                 }
2853             }
2854         }
2855         sp_nodepath_deselect(nodepath);
2856     }
2858     if (last) { // there's at least one more node after selected
2859         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2860     } else { // no more nodes, select the first one in first subpath
2861        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2862         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2863     }
2866 /**
2867  * \brief Select the node before the first selected; if none is selected,
2868  * select the last within path
2869  */
2870 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2872     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2874    Inkscape::NodePath::Node *last = NULL;
2875     if (nodepath->selected) {
2876         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2877            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2878             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2879                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2880                 if (node->selected) {
2881                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2882                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2883                             if (spl->prev) { // there's a prev subpath
2884                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2885                                 last = subpath_prev->last;
2886                             } else if (spl->next) { // there's a next subpath
2887                                 last = NULL; // to be set later to the last node of last subpath
2888                             } else {
2889                                 last = node->p.other;
2890                             }
2891                         } else {
2892                             last = node->p.other;
2893                         }
2894                     } else {
2895                         if (node->p.other) {
2896                             last = node->p.other;
2897                         } else {
2898                             if (spl->prev) { // there's a prev subpath
2899                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2900                                 last = subpath_prev->last;
2901                             } else if (spl->next) { // there's a next subpath
2902                                 last = NULL; // to be set later to the last node of last subpath
2903                             } else {
2904                                 last = (Inkscape::NodePath::Node *) subpath->last;
2905                             }
2906                         }
2907                     }
2908                 }
2909             }
2910         }
2911         sp_nodepath_deselect(nodepath);
2912     }
2914     if (last) { // there's at least one more node before selected
2915         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2916     } else { // no more nodes, select the last one in last subpath
2917         GList *spl = g_list_last(nodepath->subpaths);
2918        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2919         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2920     }
2923 /**
2924  * \brief Select all nodes that are within the rectangle.
2925  */
2926 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2928     if (!incremental) {
2929         sp_nodepath_deselect(nodepath);
2930     }
2932     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2933        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2934         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2935            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2937             if (b.contains(node->pos)) {
2938                 sp_nodepath_node_select(node, TRUE, TRUE);
2939             }
2940         }
2941     }
2945 void
2946 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2948     g_assert (n);
2949     g_assert (nodepath);
2950     g_assert (n->subpath->nodepath == nodepath);
2952     if (g_list_length (nodepath->selected) == 0) {
2953         if (grow > 0) {
2954             sp_nodepath_node_select(n, TRUE, TRUE);
2955         }
2956         return;
2957     }
2959     if (g_list_length (nodepath->selected) == 1) {
2960         if (grow < 0) {
2961             sp_nodepath_deselect (nodepath);
2962             return;
2963         }
2964     }
2966         double n_sel_range = 0, p_sel_range = 0;
2967             Inkscape::NodePath::Node *farthest_n_node = n;
2968             Inkscape::NodePath::Node *farthest_p_node = n;
2970         // Calculate ranges
2971         {
2972             double n_range = 0, p_range = 0;
2973             bool n_going = true, p_going = true;
2974             Inkscape::NodePath::Node *n_node = n;
2975             Inkscape::NodePath::Node *p_node = n;
2976             do {
2977                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2978                 if (n_node && n_going)
2979                     n_node = n_node->n.other;
2980                 if (n_node == NULL) {
2981                     n_going = false;
2982                 } else {
2983                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2984                     if (n_node->selected) {
2985                         n_sel_range = n_range;
2986                         farthest_n_node = n_node;
2987                     }
2988                     if (n_node == p_node) {
2989                         n_going = false;
2990                         p_going = false;
2991                     }
2992                 }
2993                 if (p_node && p_going)
2994                     p_node = p_node->p.other;
2995                 if (p_node == NULL) {
2996                     p_going = false;
2997                 } else {
2998                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2999                     if (p_node->selected) {
3000                         p_sel_range = p_range;
3001                         farthest_p_node = p_node;
3002                     }
3003                     if (p_node == n_node) {
3004                         n_going = false;
3005                         p_going = false;
3006                     }
3007                 }
3008             } while (n_going || p_going);
3009         }
3011     if (grow > 0) {
3012         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3013                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3014         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3015                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3016         }
3017     } else {
3018         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3019                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3020         } else if (farthest_p_node && farthest_p_node->selected) {
3021                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3022         }
3023     }
3026 void
3027 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3029     g_assert (n);
3030     g_assert (nodepath);
3031     g_assert (n->subpath->nodepath == nodepath);
3033     if (g_list_length (nodepath->selected) == 0) {
3034         if (grow > 0) {
3035             sp_nodepath_node_select(n, TRUE, TRUE);
3036         }
3037         return;
3038     }
3040     if (g_list_length (nodepath->selected) == 1) {
3041         if (grow < 0) {
3042             sp_nodepath_deselect (nodepath);
3043             return;
3044         }
3045     }
3047     Inkscape::NodePath::Node *farthest_selected = NULL;
3048     double farthest_dist = 0;
3050     Inkscape::NodePath::Node *closest_unselected = NULL;
3051     double closest_dist = NR_HUGE;
3053     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3054        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3055         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3056            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3057            if (node == n)
3058                continue;
3059            if (node->selected) {
3060                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3061                    farthest_dist = NR::L2(node->pos - n->pos);
3062                    farthest_selected = node;
3063                }
3064            } else {
3065                if (NR::L2(node->pos - n->pos) < closest_dist) {
3066                    closest_dist = NR::L2(node->pos - n->pos);
3067                    closest_unselected = node;
3068                }
3069            }
3070         }
3071     }
3073     if (grow > 0) {
3074         if (closest_unselected) {
3075             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3076         }
3077     } else {
3078         if (farthest_selected) {
3079             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3080         }
3081     }
3085 /**
3086 \brief  Saves all nodes' and handles' current positions in their origin members
3087 */
3088 void
3089 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3091     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3092        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3093         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3094            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3095            n->origin = n->pos;
3096            n->p.origin = n->p.pos;
3097            n->n.origin = n->n.pos;
3098         }
3099     }
3102 /**
3103 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3104 */
3105 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3107     if (!nodepath->selected) {
3108         return NULL;
3109     }
3111     GList *r = NULL;
3112     guint i = 0;
3113     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3114        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3115         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3116            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3117             i++;
3118             if (node->selected) {
3119                 r = g_list_append(r, GINT_TO_POINTER(i));
3120             }
3121         }
3122     }
3123     return r;
3126 /**
3127 \brief  Restores selection by selecting nodes whose positions are in the list
3128 */
3129 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3131     sp_nodepath_deselect(nodepath);
3133     guint i = 0;
3134     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3135        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3136         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3137            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3138             i++;
3139             if (g_list_find(r, GINT_TO_POINTER(i))) {
3140                 sp_nodepath_node_select(node, TRUE, TRUE);
3141             }
3142         }
3143     }
3147 /**
3148 \brief Adjusts handle according to node type and line code.
3149 */
3150 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3152     g_assert(node);
3154    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3155    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3157    // nothing to do if we are an end node
3158     if (me->other == NULL) return;
3159     if (other->other == NULL) return;
3161     // nothing to do if we are a cusp node
3162     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3164     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3165     NRPathcode mecode;
3166     if (which_adjust == 1) {
3167         mecode = (NRPathcode)me->other->code;
3168     } else {
3169         mecode = (NRPathcode)node->code;
3170     }
3171     if (mecode == NR_LINETO) return;
3173     if (sp_node_side_is_line(node, other)) {
3174         // other is a line, and we are either smooth or symm
3175        Inkscape::NodePath::Node *othernode = other->other;
3176         double len = NR::L2(me->pos - node->pos);
3177         NR::Point delta = node->pos - othernode->pos;
3178         double linelen = NR::L2(delta);
3179         if (linelen < 1e-18)
3180             return;
3181         me->pos = node->pos + (len / linelen)*delta;
3182         return;
3183     }
3185     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3186         // symmetrize 
3187         me->pos = 2 * node->pos - other->pos;
3188         return;
3189     } else {
3190         // smoothify
3191         double len = NR::L2(me->pos - node->pos);
3192         NR::Point delta = other->pos - node->pos;
3193         double otherlen = NR::L2(delta);
3194         if (otherlen < 1e-18) return;
3195         me->pos = node->pos - (len / otherlen) * delta;
3196     }
3199 /**
3200  \brief Adjusts both handles according to node type and line code
3201  */
3202 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3204     g_assert(node);
3206     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3208     /* we are either smooth or symm */
3210     if (node->p.other == NULL) return;
3211     if (node->n.other == NULL) return;
3213     if (sp_node_side_is_line(node, &node->p)) {
3214         sp_node_adjust_handle(node, 1);
3215         return;
3216     }
3218     if (sp_node_side_is_line(node, &node->n)) {
3219         sp_node_adjust_handle(node, -1);
3220         return;
3221     }
3223     /* both are curves */
3224     NR::Point const delta( node->n.pos - node->p.pos );
3226     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3227         node->p.pos = node->pos - delta / 2;
3228         node->n.pos = node->pos + delta / 2;
3229         return;
3230     }
3232     /* We are smooth */
3233     double plen = NR::L2(node->p.pos - node->pos);
3234     if (plen < 1e-18) return;
3235     double nlen = NR::L2(node->n.pos - node->pos);
3236     if (nlen < 1e-18) return;
3237     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3238     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3241 /**
3242  * Node event callback.
3243  */
3244 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3246     gboolean ret = FALSE;
3247     switch (event->type) {
3248         case GDK_ENTER_NOTIFY:
3249             Inkscape::NodePath::Path::active_node = n;
3250             break;
3251         case GDK_LEAVE_NOTIFY:
3252             Inkscape::NodePath::Path::active_node = NULL;
3253             break;
3254         case GDK_SCROLL:
3255             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3256                 switch (event->scroll.direction) {
3257                     case GDK_SCROLL_UP:
3258                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3259                         break;
3260                     case GDK_SCROLL_DOWN:
3261                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3262                         break;
3263                     default:
3264                         break;
3265                 }
3266                 ret = TRUE;
3267             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3268                 switch (event->scroll.direction) {
3269                     case GDK_SCROLL_UP:
3270                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3271                         break;
3272                     case GDK_SCROLL_DOWN:
3273                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3274                         break;
3275                     default:
3276                         break;
3277                 }
3278                 ret = TRUE;
3279             }
3280             break;
3281         case GDK_KEY_PRESS:
3282             switch (get_group0_keyval (&event->key)) {
3283                 case GDK_space:
3284                     if (event->key.state & GDK_BUTTON1_MASK) {
3285                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3286                         stamp_repr(nodepath);
3287                         ret = TRUE;
3288                     }
3289                     break;
3290                 case GDK_Page_Up:
3291                     if (event->key.state & GDK_CONTROL_MASK) {
3292                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3293                     } else {
3294                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3295                     }
3296                     break;
3297                 case GDK_Page_Down:
3298                     if (event->key.state & GDK_CONTROL_MASK) {
3299                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3300                     } else {
3301                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3302                     }
3303                     break;
3304                 default:
3305                     break;
3306             }
3307             break;
3308         default:
3309             break;
3310     }
3312     return ret;
3315 /**
3316  * Handle keypress on node; directly called.
3317  */
3318 gboolean node_key(GdkEvent *event)
3320     Inkscape::NodePath::Path *np;
3322     // there is no way to verify nodes so set active_node to nil when deleting!!
3323     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3325     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3326         gint ret = FALSE;
3327         switch (get_group0_keyval (&event->key)) {
3328             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3329             case GDK_BackSpace:
3330                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3331                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3332                 sp_nodepath_update_repr(np, _("Delete node"));
3333                 Inkscape::NodePath::Path::active_node = NULL;
3334                 ret = TRUE;
3335                 break;
3336             case GDK_c:
3337                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3338                 ret = TRUE;
3339                 break;
3340             case GDK_s:
3341                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3342                 ret = TRUE;
3343                 break;
3344             case GDK_y:
3345                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3346                 ret = TRUE;
3347                 break;
3348             case GDK_b:
3349                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3350                 ret = TRUE;
3351                 break;
3352         }
3353         return ret;
3354     }
3355     return FALSE;
3358 /**
3359  * Mouseclick on node callback.
3360  */
3361 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3363    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3365     if (state & GDK_CONTROL_MASK) {
3366         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3368         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3369             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3370                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3371             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3372                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3373             } else {
3374                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3375             }
3376             sp_nodepath_update_repr(nodepath, _("Change node type"));
3377             sp_nodepath_update_statusbar(nodepath);
3379         } else { //ctrl+alt+click: delete node
3380             GList *node_to_delete = NULL;
3381             node_to_delete = g_list_append(node_to_delete, n);
3382             sp_node_delete_preserve(node_to_delete);
3383         }
3385     } else {
3386         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3387     }
3390 /**
3391  * Mouse grabbed node callback.
3392  */
3393 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3395    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3397     if (!n->selected) {
3398         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3399     }
3401     n->is_dragging = true;
3402     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3404     sp_nodepath_remember_origins (n->subpath->nodepath);
3407 /**
3408  * Mouse ungrabbed node callback.
3409  */
3410 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3412    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3414    n->dragging_out = NULL;
3415    n->is_dragging = false;
3416    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3418    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3421 /**
3422  * The point on a line, given by its angle, closest to the given point.
3423  * \param p  A point.
3424  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3425  * \param closest  Pointer to the point struct where the result is stored.
3426  * \todo FIXME: use dot product perhaps?
3427  */
3428 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3430     if (a == HUGE_VAL) { // vertical
3431         *closest = NR::Point(0, (*p)[NR::Y]);
3432     } else {
3433         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3434         (*closest)[NR::Y] = a * (*closest)[NR::X];
3435     }
3438 /**
3439  * Distance from the point to a line given by its angle.
3440  * \param p  A point.
3441  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3442  */
3443 static double point_line_distance(NR::Point *p, double a)
3445     NR::Point c;
3446     point_line_closest(p, a, &c);
3447     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]));
3450 /**
3451  * Callback for node "request" signal.
3452  * \todo fixme: This goes to "moved" event? (lauris)
3453  */
3454 static gboolean
3455 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3457     double yn, xn, yp, xp;
3458     double an, ap, na, pa;
3459     double d_an, d_ap, d_na, d_pa;
3460     gboolean collinear = FALSE;
3461     NR::Point c;
3462     NR::Point pr;
3464     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3466     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3468     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3469     if ( (!n->subpath->nodepath->straight_path) &&
3470          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3471            || n->dragging_out ) )
3472     {
3473        NR::Point mouse = (*p);
3475        if (!n->dragging_out) {
3476            // This is the first drag-out event; find out which handle to drag out
3477            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3478            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3480            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3481                return FALSE;
3483            Inkscape::NodePath::NodeSide *opposite;
3484            if (appr_p > appr_n) { // closer to p
3485                n->dragging_out = &n->p;
3486                opposite = &n->n;
3487                n->code = NR_CURVETO;
3488            } else if (appr_p < appr_n) { // closer to n
3489                n->dragging_out = &n->n;
3490                opposite = &n->p;
3491                n->n.other->code = NR_CURVETO;
3492            } else { // p and n nodes are the same
3493                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3494                    n->dragging_out = &n->p;
3495                    opposite = &n->n;
3496                    n->code = NR_CURVETO;
3497                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3498                    n->dragging_out = &n->n;
3499                    opposite = &n->p;
3500                    n->n.other->code = NR_CURVETO;
3501                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3502                    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);
3503                    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);
3504                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3505                        n->dragging_out = &n->n;
3506                        opposite = &n->p;
3507                        n->n.other->code = NR_CURVETO;
3508                    } else { // closer to other's n handle
3509                        n->dragging_out = &n->p;
3510                        opposite = &n->n;
3511                        n->code = NR_CURVETO;
3512                    }
3513                }
3514            }
3516            // if there's another handle, make sure the one we drag out starts parallel to it
3517            if (opposite->pos != n->pos) {
3518                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3519            }
3521            // knots might not be created yet!
3522            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3523            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3524        }
3526        // pass this on to the handle-moved callback
3527        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3528        sp_node_update_handles(n);
3529        return TRUE;
3530    }
3532     if (state & GDK_CONTROL_MASK) { // constrained motion
3534         // calculate relative distances of handles
3535         // n handle:
3536         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3537         xn = n->n.pos[NR::X] - n->pos[NR::X];
3538         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3539         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3540             if (n->n.other) { // if there is the next point
3541                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3542                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3543                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3544             }
3545         }
3546         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3547         if (yn < 0) { xn = -xn; yn = -yn; }
3549         // p handle:
3550         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3551         xp = n->p.pos[NR::X] - n->pos[NR::X];
3552         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3553         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3554             if (n->p.other) {
3555                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3556                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3557                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3558             }
3559         }
3560         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3561         if (yp < 0) { xp = -xp; yp = -yp; }
3563         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3564             // sliding on handles, only if at least one of the handles is non-vertical
3565             // (otherwise it's the same as ctrl+drag anyway)
3567             // calculate angles of the handles
3568             if (xn == 0) {
3569                 if (yn == 0) { // no handle, consider it the continuation of the other one
3570                     an = 0;
3571                     collinear = TRUE;
3572                 }
3573                 else an = 0; // vertical; set the angle to horizontal
3574             } else an = yn/xn;
3576             if (xp == 0) {
3577                 if (yp == 0) { // no handle, consider it the continuation of the other one
3578                     ap = an;
3579                 }
3580                 else ap = 0; // vertical; set the angle to horizontal
3581             } else  ap = yp/xp;
3583             if (collinear) an = ap;
3585             // angles of the perpendiculars; HUGE_VAL means vertical
3586             if (an == 0) na = HUGE_VAL; else na = -1/an;
3587             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3589             // mouse point relative to the node's original pos
3590             pr = (*p) - n->origin;
3592             // distances to the four lines (two handles and two perpendiculars)
3593             d_an = point_line_distance(&pr, an);
3594             d_na = point_line_distance(&pr, na);
3595             d_ap = point_line_distance(&pr, ap);
3596             d_pa = point_line_distance(&pr, pa);
3598             // find out which line is the closest, save its closest point in c
3599             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3600                 point_line_closest(&pr, an, &c);
3601             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3602                 point_line_closest(&pr, ap, &c);
3603             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3604                 point_line_closest(&pr, na, &c);
3605             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3606                 point_line_closest(&pr, pa, &c);
3607             }
3609             // move the node to the closest point
3610             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3611                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3612                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3613                                             true);
3615         } else {  // constraining to hor/vert
3617             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3618                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3619                                                 (*p)[NR::X] - n->pos[NR::X], 
3620                                                 n->origin[NR::Y] - n->pos[NR::Y],
3621                                                 true, 
3622                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3623             } else { // snap to vert
3624                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3625                                                 n->origin[NR::X] - n->pos[NR::X],
3626                                                 (*p)[NR::Y] - n->pos[NR::Y],
3627                                                 true,
3628                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3629             }
3630         }
3631     } else { // move freely
3632         if (n->is_dragging) {
3633             if (state & GDK_MOD1_MASK) { // sculpt
3634                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3635             } else {
3636                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3637                                             (*p)[NR::X] - n->pos[NR::X],
3638                                             (*p)[NR::Y] - n->pos[NR::Y],
3639                                             (state & GDK_SHIFT_MASK) == 0);
3640             }
3641         }
3642     }
3644     n->subpath->nodepath->desktop->scroll_to_point(p);
3646     return TRUE;
3649 /**
3650  * Node handle clicked callback.
3651  */
3652 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3654    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3656     if (state & GDK_CONTROL_MASK) { // "delete" handle
3657         if (n->p.knot == knot) {
3658             n->p.pos = n->pos;
3659         } else if (n->n.knot == knot) {
3660             n->n.pos = n->pos;
3661         }
3662         sp_node_update_handles(n);
3663         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3664         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3665         sp_nodepath_update_statusbar(nodepath);
3667     } else { // just select or add to selection, depending in Shift
3668         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3669     }
3672 /**
3673  * Node handle grabbed callback.
3674  */
3675 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3677    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3679     if (!n->selected) {
3680         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3681     }
3683     // remember the origin point of the handle
3684     if (n->p.knot == knot) {
3685         n->p.origin_radial = n->p.pos - n->pos;
3686     } else if (n->n.knot == knot) {
3687         n->n.origin_radial = n->n.pos - n->pos;
3688     } else {
3689         g_assert_not_reached();
3690     }
3692     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3695 /**
3696  * Node handle ungrabbed callback.
3697  */
3698 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3700    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3702     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3703     if (n->p.knot == knot) {
3704         n->p.origin_radial.a = 0;
3705         sp_knot_set_position(knot, &n->p.pos, state);
3706     } else if (n->n.knot == knot) {
3707         n->n.origin_radial.a = 0;
3708         sp_knot_set_position(knot, &n->n.pos, state);
3709     } else {
3710         g_assert_not_reached();
3711     }
3713     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3716 /**
3717  * Node handle "request" signal callback.
3718  */
3719 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3721     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3723     Inkscape::NodePath::NodeSide *me, *opposite;
3724     gint which;
3725     if (n->p.knot == knot) {
3726         me = &n->p;
3727         opposite = &n->n;
3728         which = -1;
3729     } else if (n->n.knot == knot) {
3730         me = &n->n;
3731         opposite = &n->p;
3732         which = 1;
3733     } else {
3734         me = opposite = NULL;
3735         which = 0;
3736         g_assert_not_reached();
3737     }
3739     SPDesktop *desktop = n->subpath->nodepath->desktop;
3740     SnapManager &m = desktop->namedview->snap_manager;
3741     m.setup(desktop, n->subpath->nodepath->item);
3742     Inkscape::SnappedPoint s;
3743     
3744     if ((state & GDK_SHIFT_MASK) != 0) {
3745         // We will not try to snap when the shift-key is pressed
3746         // so remove the old snap indicator and don't wait for it to time-out  
3747         desktop->snapindicator->remove_snappoint();     
3748     }
3750     Inkscape::NodePath::Node *othernode = opposite->other;
3751     if (othernode) {
3752         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3753             /* We are smooth node adjacent with line */
3754             NR::Point const delta = *p - n->pos;
3755             NR::Coord const len = NR::L2(delta);
3756             Inkscape::NodePath::Node *othernode = opposite->other;
3757             NR::Point const ndelta = n->pos - othernode->pos;
3758             NR::Coord const linelen = NR::L2(ndelta);
3759             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3760                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3761                 (*p) = n->pos + (scal / linelen) * ndelta;
3762             }
3763             if ((state & GDK_SHIFT_MASK) == 0) {
3764                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3765             }
3766         } else {
3767                 if ((state & GDK_SHIFT_MASK) == 0) {
3768                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3769                 }
3770         }
3771     } else {
3772         if ((state & GDK_SHIFT_MASK) == 0) {
3773                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3774         }
3775     }
3776     
3777     s.getPoint(*p);
3778     
3779     sp_node_adjust_handle(n, -which);
3781     return FALSE;
3784 /**
3785  * Node handle moved callback.
3786  */
3787 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3789    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3791    Inkscape::NodePath::NodeSide *me;
3792    Inkscape::NodePath::NodeSide *other;
3793     if (n->p.knot == knot) {
3794         me = &n->p;
3795         other = &n->n;
3796     } else if (n->n.knot == knot) {
3797         me = &n->n;
3798         other = &n->p;
3799     } else {
3800         me = NULL;
3801         other = NULL;
3802         g_assert_not_reached();
3803     }
3805     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3806     Radial rme(me->pos - n->pos);
3807     Radial rother(other->pos - n->pos);
3808     Radial rnew(*p - n->pos);
3810     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3811         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3812         /* 0 interpreted as "no snapping". */
3814         // 1. Snap to the closest PI/snaps angle, starting from zero.
3815         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3817         // 2. Snap to the original angle, its opposite and perpendiculars
3818         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3819             /* The closest PI/2 angle, starting from original angle */
3820             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3822             // Snap to the closest.
3823             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3824                        ? a_snapped
3825                        : a_ortho );
3826         }
3828         // 3. Snap to the angle of the opposite line, if any
3829         Inkscape::NodePath::Node *othernode = other->other;
3830         if (othernode) {
3831             NR::Point other_to_snap(0,0);
3832             if (sp_node_side_is_line(n, other)) {
3833                 other_to_snap = othernode->pos - n->pos;
3834             } else {
3835                 other_to_snap = other->pos - n->pos;
3836             }
3837             if (NR::L2(other_to_snap) > 1e-3) {
3838                 Radial rother_to_snap(other_to_snap);
3839                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3840                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3842                 // Snap to the closest.
3843                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3844                        ? a_snapped
3845                        : a_oppo );
3846             }
3847         }
3849         rnew.a = a_snapped;
3850     }
3852     if (state & GDK_MOD1_MASK) {
3853         // lock handle length
3854         rnew.r = me->origin_radial.r;
3855     }
3857     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3858         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3859         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3860         rother.a += rnew.a - rme.a;
3861         other->pos = NR::Point(rother) + n->pos;
3862         if (other->knot) {
3863             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3864             sp_knot_moveto(other->knot, &other->pos);
3865         }
3866     }
3868     me->pos = NR::Point(rnew) + n->pos;
3869     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3871     // move knot, but without emitting the signal:
3872     // we cannot emit a "moved" signal because we're now processing it
3873     sp_knot_moveto(me->knot, &(me->pos));
3875     update_object(n->subpath->nodepath);
3877     /* status text */
3878     SPDesktop *desktop = n->subpath->nodepath->desktop;
3879     if (!desktop) return;
3880     SPEventContext *ec = desktop->event_context;
3881     if (!ec) return;
3882     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3883     if (!mc) return;
3885     double degrees = 180 / M_PI * rnew.a;
3886     if (degrees > 180) degrees -= 360;
3887     if (degrees < -180) degrees += 360;
3888     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3889         degrees = angle_to_compass (degrees);
3891     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3893     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3894          _("<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);
3896     g_string_free(length, TRUE);
3899 /**
3900  * Node handle event callback.
3901  */
3902 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3904     gboolean ret = FALSE;
3905     switch (event->type) {
3906         case GDK_KEY_PRESS:
3907             switch (get_group0_keyval (&event->key)) {
3908                 case GDK_space:
3909                     if (event->key.state & GDK_BUTTON1_MASK) {
3910                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3911                         stamp_repr(nodepath);
3912                         ret = TRUE;
3913                     }
3914                     break;
3915                 default:
3916                     break;
3917             }
3918             break;
3919         case GDK_ENTER_NOTIFY:
3920             // we use an experimentally determined threshold that seems to work fine
3921             if (NR::L2(n->pos - knot->pos) < 0.75)
3922                 Inkscape::NodePath::Path::active_node = n;
3923             break;
3924         case GDK_LEAVE_NOTIFY:
3925             // we use an experimentally determined threshold that seems to work fine
3926             if (NR::L2(n->pos - knot->pos) < 0.75)
3927                 Inkscape::NodePath::Path::active_node = NULL;
3928             break;
3929         default:
3930             break;
3931     }
3933     return ret;
3936 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3937                                  Radial &rme, Radial &rother, gboolean const both)
3939     rme.a += angle;
3940     if ( both
3941          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3942          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3943     {
3944         rother.a += angle;
3945     }
3948 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3949                                         Radial &rme, Radial &rother, gboolean const both)
3951     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3953     gdouble r;
3954     if ( both
3955          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3956          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3957     {
3958         r = MAX(rme.r, rother.r);
3959     } else {
3960         r = rme.r;
3961     }
3963     gdouble const weird_angle = atan2(norm_angle, r);
3964 /* Bulia says norm_angle is just the visible distance that the
3965  * object's end must travel on the screen.  Left as 'angle' for want of
3966  * a better name.*/
3968     rme.a += weird_angle;
3969     if ( both
3970          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3971          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3972     {
3973         rother.a += weird_angle;
3974     }
3977 /**
3978  * Rotate one node.
3979  */
3980 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3982     Inkscape::NodePath::NodeSide *me, *other;
3983     bool both = false;
3985     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3986     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3988     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3989         me = &(n->p);
3990         other = &(n->n);
3991     } else if (!n->p.other) {
3992         me = &(n->n);
3993         other = &(n->p);
3994     } else {
3995         if (which > 0) { // right handle
3996             if (xn > xp) {
3997                 me = &(n->n);
3998                 other = &(n->p);
3999             } else {
4000                 me = &(n->p);
4001                 other = &(n->n);
4002             }
4003         } else if (which < 0){ // left handle
4004             if (xn <= xp) {
4005                 me = &(n->n);
4006                 other = &(n->p);
4007             } else {
4008                 me = &(n->p);
4009                 other = &(n->n);
4010             }
4011         } else { // both handles
4012             me = &(n->n);
4013             other = &(n->p);
4014             both = true;
4015         }
4016     }
4018     Radial rme(me->pos - n->pos);
4019     Radial rother(other->pos - n->pos);
4021     if (screen) {
4022         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4023     } else {
4024         node_rotate_one_internal (*n, angle, rme, rother, both);
4025     }
4027     me->pos = n->pos + NR::Point(rme);
4029     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4030         other->pos =  n->pos + NR::Point(rother);
4031     }
4033     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4034     // so here we just move all the knots without emitting move signals, for speed
4035     sp_node_update_handles(n, false);
4038 /**
4039  * Rotate selected nodes.
4040  */
4041 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4043     if (!nodepath || !nodepath->selected) return;
4045     if (g_list_length(nodepath->selected) == 1) {
4046        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4047         node_rotate_one (n, angle, which, screen);
4048     } else {
4049        // rotate as an object:
4051         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4052         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4053         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4054             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4055             box.expandTo (n->pos); // contain all selected nodes
4056         }
4058         gdouble rot;
4059         if (screen) {
4060             gdouble const zoom = nodepath->desktop->current_zoom();
4061             gdouble const zmove = angle / zoom;
4062             gdouble const r = NR::L2(box.max() - box.midpoint());
4063             rot = atan2(zmove, r);
4064         } else {
4065             rot = angle;
4066         }
4068         NR::Point rot_center;
4069         if (Inkscape::NodePath::Path::active_node == NULL)
4070             rot_center = box.midpoint();
4071         else
4072             rot_center = Inkscape::NodePath::Path::active_node->pos;
4074         NR::Matrix t =
4075             NR::Matrix (NR::translate(-rot_center)) *
4076             NR::Matrix (NR::rotate(rot)) *
4077             NR::Matrix (NR::translate(rot_center));
4079         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4080             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4081             n->pos *= t;
4082             n->n.pos *= t;
4083             n->p.pos *= t;
4084             sp_node_update_handles(n, false);
4085         }
4086     }
4088     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4091 /**
4092  * Scale one node.
4093  */
4094 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4096     bool both = false;
4097     Inkscape::NodePath::NodeSide *me, *other;
4099     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4100     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4102     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4103         me = &(n->p);
4104         other = &(n->n);
4105         n->code = NR_CURVETO;
4106     } else if (!n->p.other) {
4107         me = &(n->n);
4108         other = &(n->p);
4109         if (n->n.other)
4110             n->n.other->code = NR_CURVETO;
4111     } else {
4112         if (which > 0) { // right handle
4113             if (xn > xp) {
4114                 me = &(n->n);
4115                 other = &(n->p);
4116                 if (n->n.other)
4117                     n->n.other->code = NR_CURVETO;
4118             } else {
4119                 me = &(n->p);
4120                 other = &(n->n);
4121                 n->code = NR_CURVETO;
4122             }
4123         } else if (which < 0){ // left handle
4124             if (xn <= xp) {
4125                 me = &(n->n);
4126                 other = &(n->p);
4127                 if (n->n.other)
4128                     n->n.other->code = NR_CURVETO;
4129             } else {
4130                 me = &(n->p);
4131                 other = &(n->n);
4132                 n->code = NR_CURVETO;
4133             }
4134         } else { // both handles
4135             me = &(n->n);
4136             other = &(n->p);
4137             both = true;
4138             n->code = NR_CURVETO;
4139             if (n->n.other)
4140                 n->n.other->code = NR_CURVETO;
4141         }
4142     }
4144     Radial rme(me->pos - n->pos);
4145     Radial rother(other->pos - n->pos);
4147     rme.r += grow;
4148     if (rme.r < 0) rme.r = 0;
4149     if (rme.a == HUGE_VAL) {
4150         if (me->other) { // if direction is unknown, initialize it towards the next node
4151             Radial rme_next(me->other->pos - n->pos);
4152             rme.a = rme_next.a;
4153         } else { // if there's no next, initialize to 0
4154             rme.a = 0;
4155         }
4156     }
4157     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4158         rother.r += grow;
4159         if (rother.r < 0) rother.r = 0;
4160         if (rother.a == HUGE_VAL) {
4161             rother.a = rme.a + M_PI;
4162         }
4163     }
4165     me->pos = n->pos + NR::Point(rme);
4167     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4168         other->pos = n->pos + NR::Point(rother);
4169     }
4171     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4172     // so here we just move all the knots without emitting move signals, for speed
4173     sp_node_update_handles(n, false);
4176 /**
4177  * Scale selected nodes.
4178  */
4179 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4181     if (!nodepath || !nodepath->selected) return;
4183     if (g_list_length(nodepath->selected) == 1) {
4184         // scale handles of the single selected node
4185         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4186         node_scale_one (n, grow, which);
4187     } else {
4188         // scale nodes as an "object":
4190         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4191         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4192         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4193             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4194             box.expandTo (n->pos); // contain all selected nodes
4195         }
4197         double scale = (box.maxExtent() + grow)/box.maxExtent();
4199         NR::Point scale_center;
4200         if (Inkscape::NodePath::Path::active_node == NULL)
4201             scale_center = box.midpoint();
4202         else
4203             scale_center = Inkscape::NodePath::Path::active_node->pos;
4205         NR::Matrix t =
4206             NR::Matrix (NR::translate(-scale_center)) *
4207             NR::Matrix (NR::scale(scale, scale)) *
4208             NR::Matrix (NR::translate(scale_center));
4210         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4211             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4212             n->pos *= t;
4213             n->n.pos *= t;
4214             n->p.pos *= t;
4215             sp_node_update_handles(n, false);
4216         }
4217     }
4219     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4222 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4224     if (!nodepath) return;
4225     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4228 /**
4229  * Flip selected nodes horizontally/vertically.
4230  */
4231 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4233     if (!nodepath || !nodepath->selected) return;
4235     if (g_list_length(nodepath->selected) == 1 && !center) {
4236         // flip handles of the single selected node
4237         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4238         double temp = n->p.pos[axis];
4239         n->p.pos[axis] = n->n.pos[axis];
4240         n->n.pos[axis] = temp;
4241         sp_node_update_handles(n, false);
4242     } else {
4243         // scale nodes as an "object":
4245         NR::Rect box = sp_node_selected_bbox (nodepath);
4246         if (!center) {
4247             center = box.midpoint();
4248         }
4249         NR::Matrix t =
4250             NR::Matrix (NR::translate(- *center)) *
4251             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4252             NR::Matrix (NR::translate(*center));
4254         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4255             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4256             n->pos *= t;
4257             n->n.pos *= t;
4258             n->p.pos *= t;
4259             sp_node_update_handles(n, false);
4260         }
4261     }
4263     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4266 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4268     g_assert (nodepath->selected);
4270     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4271     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4272     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4273         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4274         box.expandTo (n->pos); // contain all selected nodes
4275     }
4276     return box;
4279 //-----------------------------------------------
4280 /**
4281  * Return new subpath under given nodepath.
4282  */
4283 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4285     g_assert(nodepath);
4286     g_assert(nodepath->desktop);
4288    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4290     s->nodepath = nodepath;
4291     s->closed = FALSE;
4292     s->nodes = NULL;
4293     s->first = NULL;
4294     s->last = NULL;
4296     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4297     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4298     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4300     return s;
4303 /**
4304  * Destroy nodes in subpath, then subpath itself.
4305  */
4306 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4308     g_assert(subpath);
4309     g_assert(subpath->nodepath);
4310     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4312     while (subpath->nodes) {
4313         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4314     }
4316     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4318     g_free(subpath);
4321 /**
4322  * Link head to tail in subpath.
4323  */
4324 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4326     g_assert(!sp->closed);
4327     g_assert(sp->last != sp->first);
4328     g_assert(sp->first->code == NR_MOVETO);
4330     sp->closed = TRUE;
4332     //Link the head to the tail
4333     sp->first->p.other = sp->last;
4334     sp->last->n.other  = sp->first;
4335     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4336     sp->first          = sp->last;
4338     //Remove the extra end node
4339     sp_nodepath_node_destroy(sp->last->n.other);
4342 /**
4343  * Open closed (loopy) subpath at node.
4344  */
4345 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4347     g_assert(sp->closed);
4348     g_assert(n->subpath == sp);
4349     g_assert(sp->first == sp->last);
4351     /* We create new startpoint, current node will become last one */
4353    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4354                                                 &n->pos, &n->pos, &n->n.pos);
4357     sp->closed        = FALSE;
4359     //Unlink to make a head and tail
4360     sp->first         = new_path;
4361     sp->last          = n;
4362     n->n.other        = NULL;
4363     new_path->p.other = NULL;
4366 /**
4367  * Return new node in subpath with given properties.
4368  * \param pos Position of node.
4369  * \param ppos Handle position in previous direction
4370  * \param npos Handle position in previous direction
4371  */
4372 Inkscape::NodePath::Node *
4373 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)
4375     g_assert(sp);
4376     g_assert(sp->nodepath);
4377     g_assert(sp->nodepath->desktop);
4379     if (nodechunk == NULL)
4380         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4382     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4384     n->subpath  = sp;
4386     if (type != Inkscape::NodePath::NODE_NONE) {
4387         // use the type from sodipodi:nodetypes
4388         n->type = type;
4389     } else {
4390         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4391             // points are (almost) collinear
4392             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4393                 // endnode, or a node with a retracted handle
4394                 n->type = Inkscape::NodePath::NODE_CUSP;
4395             } else {
4396                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4397             }
4398         } else {
4399             n->type = Inkscape::NodePath::NODE_CUSP;
4400         }
4401     }
4403     n->code     = code;
4404     n->selected = FALSE;
4405     n->pos      = *pos;
4406     n->p.pos    = *ppos;
4407     n->n.pos    = *npos;
4409     n->dragging_out = NULL;
4411     Inkscape::NodePath::Node *prev;
4412     if (next) {
4413         //g_assert(g_list_find(sp->nodes, next));
4414         prev = next->p.other;
4415     } else {
4416         prev = sp->last;
4417     }
4419     if (prev)
4420         prev->n.other = n;
4421     else
4422         sp->first = n;
4424     if (next)
4425         next->p.other = n;
4426     else
4427         sp->last = n;
4429     n->p.other = prev;
4430     n->n.other = next;
4432     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"));
4433     sp_knot_set_position(n->knot, pos, 0);
4435     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4436     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4437     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4438     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4439     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4440     sp_knot_update_ctrl(n->knot);
4442     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4443     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4444     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4445     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4446     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4447     sp_knot_show(n->knot);
4449     // We only create handle knots and lines on demand
4450     n->p.knot = NULL;
4451     n->p.line = NULL;
4452     n->n.knot = NULL;
4453     n->n.line = NULL;
4455     sp->nodes = g_list_prepend(sp->nodes, n);
4457     return n;
4460 /**
4461  * Destroy node and its knots, link neighbors in subpath.
4462  */
4463 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4465     g_assert(node);
4466     g_assert(node->subpath);
4467     g_assert(SP_IS_KNOT(node->knot));
4469    Inkscape::NodePath::SubPath *sp = node->subpath;
4471     if (node->selected) { // first, deselect
4472         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4473         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4474     }
4476     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4478     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4479     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4480     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4481     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4482     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4483     g_object_unref(G_OBJECT(node->knot));
4485     if (node->p.knot) {
4486         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4487         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4488         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4489         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4490         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4491         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4492         g_object_unref(G_OBJECT(node->p.knot));
4493         node->p.knot = NULL;
4494     }
4496     if (node->n.knot) {
4497         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4498         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4499         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4500         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4501         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4502         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4503         g_object_unref(G_OBJECT(node->n.knot));
4504         node->n.knot = NULL;
4505     }
4507     if (node->p.line)
4508         gtk_object_destroy(GTK_OBJECT(node->p.line));
4509     if (node->n.line)
4510         gtk_object_destroy(GTK_OBJECT(node->n.line));
4512     if (sp->nodes) { // there are others nodes on the subpath
4513         if (sp->closed) {
4514             if (sp->first == node) {
4515                 g_assert(sp->last == node);
4516                 sp->first = node->n.other;
4517                 sp->last = sp->first;
4518             }
4519             node->p.other->n.other = node->n.other;
4520             node->n.other->p.other = node->p.other;
4521         } else {
4522             if (sp->first == node) {
4523                 sp->first = node->n.other;
4524                 sp->first->code = NR_MOVETO;
4525             }
4526             if (sp->last == node) sp->last = node->p.other;
4527             if (node->p.other) node->p.other->n.other = node->n.other;
4528             if (node->n.other) node->n.other->p.other = node->p.other;
4529         }
4530     } else { // this was the last node on subpath
4531         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4532     }
4534     g_mem_chunk_free(nodechunk, node);
4537 /**
4538  * Returns one of the node's two sides.
4539  * \param which Indicates which side.
4540  * \return Pointer to previous node side if which==-1, next if which==1.
4541  */
4542 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4544     g_assert(node);
4546     switch (which) {
4547         case -1:
4548             return &node->p;
4549         case 1:
4550             return &node->n;
4551         default:
4552             break;
4553     }
4555     g_assert_not_reached();
4557     return NULL;
4560 /**
4561  * Return the other side of the node, given one of its sides.
4562  */
4563 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4565     g_assert(node);
4567     if (me == &node->p) return &node->n;
4568     if (me == &node->n) return &node->p;
4570     g_assert_not_reached();
4572     return NULL;
4575 /**
4576  * Return NRPathcode on the given side of the node.
4577  */
4578 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4580     g_assert(node);
4582     if (me == &node->p) {
4583         if (node->p.other) return (NRPathcode)node->code;
4584         return NR_MOVETO;
4585     }
4587     if (me == &node->n) {
4588         if (node->n.other) return (NRPathcode)node->n.other->code;
4589         return NR_MOVETO;
4590     }
4592     g_assert_not_reached();
4594     return NR_END;
4597 /**
4598  * Return node with the given index
4599  */
4600 Inkscape::NodePath::Node *
4601 sp_nodepath_get_node_by_index(int index)
4603     Inkscape::NodePath::Node *e = NULL;
4605     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4606     if (!nodepath) {
4607         return e;
4608     }
4610     //find segment
4611     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4613         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4614         int n = g_list_length(sp->nodes);
4615         if (sp->closed) {
4616             n++;
4617         }
4619         //if the piece belongs to this subpath grab it
4620         //otherwise move onto the next subpath
4621         if (index < n) {
4622             e = sp->first;
4623             for (int i = 0; i < index; ++i) {
4624                 e = e->n.other;
4625             }
4626             break;
4627         } else {
4628             if (sp->closed) {
4629                 index -= (n+1);
4630             } else {
4631                 index -= n;
4632             }
4633         }
4634     }
4636     return e;
4639 /**
4640  * Returns plain text meaning of node type.
4641  */
4642 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4644     unsigned retracted = 0;
4645     bool endnode = false;
4647     for (int which = -1; which <= 1; which += 2) {
4648         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4649         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4650             retracted ++;
4651         if (!side->other)
4652             endnode = true;
4653     }
4655     if (retracted == 0) {
4656         if (endnode) {
4657                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4658                 return _("end node");
4659         } else {
4660             switch (node->type) {
4661                 case Inkscape::NodePath::NODE_CUSP:
4662                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4663                     return _("cusp");
4664                 case Inkscape::NodePath::NODE_SMOOTH:
4665                     // TRANSLATORS: "smooth" is an adjective here
4666                     return _("smooth");
4667                 case Inkscape::NodePath::NODE_SYMM:
4668                     return _("symmetric");
4669             }
4670         }
4671     } else if (retracted == 1) {
4672         if (endnode) {
4673             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4674             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4675         } else {
4676             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4677         }
4678     } else {
4679         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4680     }
4682     return NULL;
4685 /**
4686  * Handles content of statusbar as long as node tool is active.
4687  */
4688 void
4689 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4691     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");
4692     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4694     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4695     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4696     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4697     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4699     SPDesktop *desktop = NULL;
4700     if (nodepath) {
4701         desktop = nodepath->desktop;
4702     } else {
4703         desktop = SP_ACTIVE_DESKTOP;
4704     }
4706     SPEventContext *ec = desktop->event_context;
4707     if (!ec) return;
4708     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4709     if (!mc) return;
4711     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4713     if (selected_nodes == 0) {
4714         Inkscape::Selection *sel = desktop->selection;
4715         if (!sel || sel->isEmpty()) {
4716             mc->setF(Inkscape::NORMAL_MESSAGE,
4717                      _("Select a single object to edit its nodes or handles."));
4718         } else {
4719             if (nodepath) {
4720             mc->setF(Inkscape::NORMAL_MESSAGE,
4721                      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.",
4722                               "<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.",
4723                               total_nodes),
4724                      total_nodes);
4725             } else {
4726                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4727                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4728                 } else {
4729                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4730                 }
4731             }
4732         }
4733     } else if (nodepath && selected_nodes == 1) {
4734         mc->setF(Inkscape::NORMAL_MESSAGE,
4735                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4736                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4737                           total_nodes),
4738                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4739     } else {
4740         if (selected_subpaths > 1) {
4741             mc->setF(Inkscape::NORMAL_MESSAGE,
4742                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4743                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4744                               total_nodes),
4745                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4746         } else {
4747             mc->setF(Inkscape::NORMAL_MESSAGE,
4748                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4749                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4750                               total_nodes),
4751                      selected_nodes, total_nodes, when_selected);
4752         }
4753     }
4756 /*
4757  * returns a *copy* of the curve of that object.
4758  */
4759 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4760     if (!object)
4761         return NULL;
4763     SPCurve *curve = NULL;
4764     if (SP_IS_PATH(object)) {
4765         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4766         curve = curve_new->copy();
4767     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4768         const gchar *svgd = object->repr->attribute(key);
4769         if (svgd) {
4770             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4771             SPCurve *curve_new = new SPCurve(pv);
4772             if (curve_new) {
4773                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4774             }
4775         }
4776     }
4778     return curve;
4781 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4782     if (!np || !np->object || !curve)
4783         return;
4785     if (SP_IS_PATH(np->object)) {
4786         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4787             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4788         } else {
4789             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4790         }
4791     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4792         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4793         if (pathparam) {
4794             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4795             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4796         }
4797     }
4800 SPCanvasItem *
4801 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4802     SPCurve *flash_curve = curve->copy();
4803     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4804     flash_curve->transform(i2d);
4805     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4806     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4807     // unless we also flash the nodes...
4808     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4809     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4810     sp_canvas_item_show(canvasitem);
4811     flash_curve->unref();
4812     return canvasitem;
4815 SPCanvasItem *
4816 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4817     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4818                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4821 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4822     np->show_helperpath = show;
4824     if (show) {
4825         SPCurve *helper_curve = np->curve->copy();
4826         helper_curve->transform(to_2geom(np->i2d));
4827         if (!np->helper_path) {
4828             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4829             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);
4830             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4831             sp_canvas_item_move_to_z(np->helper_path, 0);
4832             sp_canvas_item_show(np->helper_path);
4833         } else {
4834             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4835         }
4836         helper_curve->unref();
4837     } else {
4838         if (np->helper_path) {
4839             GtkObject *temp = np->helper_path;
4840             np->helper_path = NULL;
4841             gtk_object_destroy(temp);
4842         }
4843     }
4846 /* sp_nodepath_make_straight_path:
4847  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4848  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4849  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4850  */
4851 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4852     np->straight_path = true;
4853     np->show_handles = false;
4854     g_message("add code to make the path straight.");
4855     // do sp_nodepath_convert_node_type on all nodes?
4856     // coding tip: search for this text : "Make selected segments lines"
4860 /*
4861   Local Variables:
4862   mode:c++
4863   c-file-style:"stroustrup"
4864   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4865   indent-tabs-mode:nil
4866   fill-column:99
4867   End:
4868 */
4869 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :