Code

clean up and revert recent change to nodepath that broke things
[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 "svg/svg.h"
48 #include "verbs.h"
49 #include "display/bezier-utils.h"
50 #include <vector>
51 #include <algorithm>
52 #include <cstring>
53 #include <cmath>
54 #include <string>
55 #include "live_effects/lpeobject.h"
56 #include "live_effects/lpeobject-reference.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 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
104 Geom::PathVector sp_nodepath_sanitize_path(Geom::PathVector const &pathv_in);
106 /* Object updating */
108 static void stamp_repr(Inkscape::NodePath::Path *np);
109 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
110 static gchar *create_typestr(Inkscape::NodePath::Path *np);
112 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
114 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
116 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
118 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
120 /* Adjust handle placement, if the node or the other handle is moved */
121 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
122 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
124 /* Node event callbacks */
125 static void node_clicked(SPKnot *knot, guint state, gpointer data);
126 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
127 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
128 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 /* Handle event callbacks */
131 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
132 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
134 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
135 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
138 /* Constructors and destructors */
140 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
141 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
142 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
143 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
144 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
145                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
146 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
148 /* Helpers */
150 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
151 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
152 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
154 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
155 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
157 // active_node indicates mouseover node
158 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
160 static SPCanvasItem *
161 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
162     SPCurve *helper_curve = curve->copy();
163     helper_curve->transform(to_2geom(np->i2d));
164     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
165     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
166     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
167     sp_canvas_item_move_to_z(helper_path, 0);
168     if (show) {
169         sp_canvas_item_show(helper_path);
170     }
171     helper_curve->unref();
172     return helper_path;
175 static SPCanvasItem *
176 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
177     SPCurve *helper_curve = new SPCurve(pathv);
178     return sp_nodepath_make_helper_item(np, helper_curve, show);
181 static void
182 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
183     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
184     if (!SP_IS_LPE_ITEM(np->item)) {
185         g_print ("Only LPEItems can have helperpaths!\n");
186         return;
187     }
189     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
190     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
191     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
192         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
193         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
194         // create new canvas items from the effect's helper paths
195         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
196         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
197             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
198         }
199     }
202 void
203 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
204     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
205     if (!SP_IS_LPE_ITEM(np->item)) {
206         g_print ("Only LPEItems can have helperpaths!\n");
207         return;
208     }
210     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
211     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
212     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
213         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
214         /* update canvas items from the effect's helper paths; note that this code relies on the
215          * fact that getHelperPaths() will always return the same number of helperpaths in the same
216          * order as during their creation in sp_nodepath_create_helperpaths
217          */
218         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
219         for (unsigned int j = 0; j < hpaths.size(); ++j) {
220             SPCurve *curve = new SPCurve(hpaths[j]);
221             curve->transform(to_2geom(np->i2d));
222             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
223             curve = curve->unref();
224         }
225     }
228 static void
229 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
230     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
231         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
232             GtkObject *temp = *j;
233             *j = NULL;
234             gtk_object_destroy(temp);
235         }
236     }
240 /**
241  * \brief Creates new nodepath from item
242  */
243 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
245     Inkscape::XML::Node *repr = object->repr;
247     /** \todo
248      * FIXME: remove this. We don't want to edit paths inside flowtext.
249      * Instead we will build our flowtext with cloned paths, so that the
250      * real paths are outside the flowtext and thus editable as usual.
251      */
252     if (SP_IS_FLOWTEXT(object)) {
253         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
254             if SP_IS_FLOWREGION(child) {
255                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
256                 if (grandchild && SP_IS_PATH(grandchild)) {
257                     object = SP_ITEM(grandchild);
258                     break;
259                 }
260             }
261         }
262     }
264     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
266     if (curve == NULL)
267         return NULL;
269     if (curve->get_segment_count() < 1) {
270         curve->unref();
271         return NULL; // prevent crash for one-node paths
272     }
274     //Create new nodepath
275     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
276     if (!np) {
277         curve->unref();
278         return NULL;
279     }
281     // Set defaults
282     np->desktop     = desktop;
283     np->object      = object;
284     np->subpaths    = NULL;
285     np->selected    = NULL;
286     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
287     np->local_change = 0;
288     np->show_handles = show_handles;
289     np->helper_path = NULL;
290     np->helper_path_vec = new HelperPathList;
291     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
292     np->helperpath_width = 1.0;
293     np->curve = curve->copy();
294     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
295     if (SP_IS_LPE_ITEM(object)) {
296         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
297         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
298             np->show_helperpath = true;
299         }            
300     }
301     np->straight_path = false;
302     if (IS_LIVEPATHEFFECT(object) && item) {
303         np->item = item;
304     } else {
305         np->item = SP_ITEM(object);
306     }
308     // we need to update item's transform from the repr here,
309     // because they may be out of sync when we respond
310     // to a change in repr by regenerating nodepath     --bb
311     sp_object_read_attr(SP_OBJECT(np->item), "transform");
313     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
314     np->d2i  = np->i2d.inverse();
316     np->repr = repr;
317     if (repr_key_in) { // apparantly the object is an LPEObject
318         np->repr_key = g_strdup(repr_key_in);
319         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
320         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
321         if (lpeparam) {
322             lpeparam->param_setup_nodepath(np);
323         }
324     } else {
325         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
326         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
327             np->repr_key = g_strdup("inkscape:original-d");
329             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
330             if (lpe) {
331                 lpe->setup_nodepath(np);
332             }
333         } else {
334             np->repr_key = g_strdup("d");
335         }
336     }
338     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
339      * So for example a closed rectangle has a nodetypestring of length 5.
340      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
341     Geom::PathVector pathv_sanitized = sp_nodepath_sanitize_path(np->curve->get_pathvector());
342     np->curve->set_pathvector(pathv_sanitized);
343     guint length = np->curve->get_segment_count();
344     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
345         length += pit->empty() ? 0 : 1;
346     }
348     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
349     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
351     // create the subpath(s) from the bpath
352     subpaths_from_pathvector(np, pathv_sanitized, typestr);
354     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
355     np->subpaths = g_list_reverse(np->subpaths);
357     delete[] typestr;
358     curve->unref();
360     // Draw helper curve
361     if (np->show_helperpath) {
362         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
363     }
365     sp_nodepath_create_helperpaths(np);
367     return np;
370 /**
371  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
372  */
373 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
375     if (!np)  //soft fail, like delete
376         return;
378     while (np->subpaths) {
379         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
380     }
382     //Inform the ShapeEditor that made me, if any, that I am gone.
383     if (np->shape_editor)
384         np->shape_editor->nodepath_destroyed();
386     g_assert(!np->selected);
388     if (np->helper_path) {
389         GtkObject *temp = np->helper_path;
390         np->helper_path = NULL;
391         gtk_object_destroy(temp);
392     }
393     if (np->curve) {
394         np->curve->unref();
395         np->curve = NULL;
396     }
398     if (np->repr_key) {
399         g_free(np->repr_key);
400         np->repr_key = NULL;
401     }
402     if (np->repr_nodetypes_key) {
403         g_free(np->repr_nodetypes_key);
404         np->repr_nodetypes_key = NULL;
405     }
407     sp_nodepath_destroy_helperpaths(np);
408     delete np->helper_path_vec;
409     np->helper_path_vec = NULL;
411     np->desktop = NULL;
413     g_free(np);
416 /**
417  *  Return the node count of a given NodeSubPath.
418  */
419 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
421     if (!subpath)
422         return 0;
423     gint nodeCount = g_list_length(subpath->nodes);
424     return nodeCount;
427 /**
428  *  Return the node count of a given NodePath.
429  */
430 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
432     if (!np)
433         return 0;
434     gint nodeCount = 0;
435     for (GList *item = np->subpaths ; item ; item=item->next) {
436        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
437         nodeCount += g_list_length(subpath->nodes);
438     }
439     return nodeCount;
442 /**
443  *  Return the subpath count of a given NodePath.
444  */
445 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
447     if (!np)
448         return 0;
449     return g_list_length (np->subpaths);
452 /**
453  *  Return the selected node count of a given NodePath.
454  */
455 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
457     if (!np)
458         return 0;
459     return g_list_length (np->selected);
462 /**
463  *  Return the number of subpaths where nodes are selected in a given NodePath.
464  */
465 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
467     if (!np)
468         return 0;
469     if (!np->selected)
470         return 0;
471     if (!np->selected->next)
472         return 1;
473     gint count = 0;
474     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
475         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
476         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
477             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
478             if (node->selected) {
479                 count ++;
480                 break;
481             }
482         }
483     }
484     return count;
487 /**
488  * Clean up a nodepath after editing.
489  *
490  * Currently we are deleting trivial subpaths.
491  */
492 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
494     GList *badSubPaths = NULL;
496     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
497     for (GList *l = nodepath->subpaths; l ; l=l->next) {
498        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
499        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
500             badSubPaths = g_list_append(badSubPaths, sp);
501     }
503     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
504     //also removes the subpath from nodepath->subpaths
505     for (GList *l = badSubPaths; l ; l=l->next) {
506        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
507         sp_nodepath_subpath_destroy(sp);
508     }
510     g_list_free(badSubPaths);
513 /**
514  * Create new nodepaths from pathvector, make it subpaths of np.
515  * \param t The node type array.
516  */
517 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
519     guint i = 0;  // index into node type array
520     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
521         if (pit->empty())
522             continue;  // don't add single knot paths
524         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
526         NR::Point ppos = from_2geom(pit->initialPoint()) * np->i2d;
527         NRPathcode pcode = NR_MOVETO;
529         /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/
530         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
531             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
532                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
533                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
534             {
535                 NR::Point pos = from_2geom(cit->initialPoint()) * np->i2d;
536                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
537                 ppos = from_2geom(cit->finalPoint());
538                 pcode = NR_LINETO;
539             }
540             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
541                 std::vector<Geom::Point> points = cubic_bezier->points();
542                 NR::Point pos = from_2geom(points[0]) * np->i2d;
543                 NR::Point npos = from_2geom(points[1]) * np->i2d;
544                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
545                 ppos = from_2geom(points[2]) * np->i2d;
546                 pcode = NR_CURVETO;
547             }
548         }
550         if (pit->closed()) {
551             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
552             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
553              * If the length is zero, don't add it to the nodepath. */
554             Geom::Curve const &closing_seg = pit->back_closed();
555             if ( ! closing_seg.isDegenerate() ) {
556                 NR::Point pos = from_2geom(closing_seg.finalPoint()) * np->i2d;
557                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
558             }
560             sp_nodepath_subpath_close(sp);
561         }
562     }
565 /**
566  * Convert from sodipodi:nodetypes to new style type array.
567  */
568 static
569 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
571     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
573     guint pos = 0;
575     if (types) {
576         for (guint i = 0; types[i] && ( i < length ); i++) {
577             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
578             if (types[i] != '\0') {
579                 switch (types[i]) {
580                     case 's':
581                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
582                         break;
583                     case 'z':
584                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
585                         break;
586                     case 'c':
587                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
588                         break;
589                     default:
590                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
591                         break;
592                 }
593             }
594         }
595     }
597     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
599     return typestr;
602 /**
603  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
604  * updated but repr is not (for speed). Used during curve and node drag.
605  */
606 static void update_object(Inkscape::NodePath::Path *np)
608     g_assert(np);
610     np->curve->unref();
611     np->curve = create_curve(np);
613     sp_nodepath_set_curve(np, np->curve);
615     if (np->show_helperpath) {
616         SPCurve * helper_curve = np->curve->copy();
617         helper_curve->transform(to_2geom(np->i2d));
618         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
619         helper_curve->unref();
620     }
622     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
623     //sp_nodepath_update_helperpaths(np);
625     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
626     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
627     np->shape_editor->update_knotholder();
630 /**
631  * Update XML path node with data from path object.
632  */
633 static void update_repr_internal(Inkscape::NodePath::Path *np)
635     g_assert(np);
637     Inkscape::XML::Node *repr = np->object->repr;
639     np->curve->unref();
640     np->curve = create_curve(np);
642     gchar *typestr = create_typestr(np);
643     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
645     // determine if path has an effect applied and write to correct "d" attribute.
646     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
647         np->local_change++;
648         repr->setAttribute(np->repr_key, svgpath);
649     }
651     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
652         np->local_change++;
653         repr->setAttribute(np->repr_nodetypes_key, typestr);
654     }
656     g_free(svgpath);
657     g_free(typestr);
659     if (np->show_helperpath) {
660         SPCurve * helper_curve = np->curve->copy();
661         helper_curve->transform(to_2geom(np->i2d));
662         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
663         helper_curve->unref();
664     }
666     // TODO: do we need this call here? after all, update_object() should have been called just before
667     //sp_nodepath_update_helperpaths(np);
670 /**
671  * Update XML path node with data from path object, commit changes forever.
672  */
673 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
675     //fixme: np can be NULL, so check before proceeding
676     g_return_if_fail(np != NULL);
678     update_repr_internal(np);
679     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
681     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
682                      annotation);
685 /**
686  * Update XML path node with data from path object, commit changes with undo.
687  */
688 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
690     update_repr_internal(np);
691     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
692                            annotation);
695 /**
696  * Make duplicate of path, replace corresponding XML node in tree, commit.
697  */
698 static void stamp_repr(Inkscape::NodePath::Path *np)
700     g_assert(np);
702     Inkscape::XML::Node *old_repr = np->object->repr;
703     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
705     // remember the position of the item
706     gint pos = old_repr->position();
707     // remember parent
708     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
710     SPCurve *curve = create_curve(np);
711     gchar *typestr = create_typestr(np);
713     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
715     new_repr->setAttribute(np->repr_key, svgpath);
716     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
718     // add the new repr to the parent
719     parent->appendChild(new_repr);
720     // move to the saved position
721     new_repr->setPosition(pos > 0 ? pos : 0);
723     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
724                      _("Stamp"));
726     Inkscape::GC::release(new_repr);
727     g_free(svgpath);
728     g_free(typestr);
729     curve->unref();
732 /**
733  * Create curve from path.
734  */
735 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
737     SPCurve *curve = new SPCurve();
739     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
740        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
741         curve->moveto(sp->first->pos * np->d2i);
742        Inkscape::NodePath::Node *n = sp->first->n.other;
743         while (n) {
744             NR::Point const end_pt = n->pos * np->d2i;
745             switch (n->code) {
746                 case NR_LINETO:
747                     curve->lineto(end_pt);
748                     break;
749                 case NR_CURVETO:
750                     curve->curveto(n->p.other->n.pos * np->d2i,
751                                      n->p.pos * np->d2i,
752                                      end_pt);
753                     break;
754                 default:
755                     g_assert_not_reached();
756                     break;
757             }
758             if (n != sp->last) {
759                 n = n->n.other;
760             } else {
761                 n = NULL;
762             }
763         }
764         if (sp->closed) {
765             curve->closepath();
766         }
767     }
769     return curve;
772 /**
773  * Convert path type string to sodipodi:nodetypes style.
774  */
775 static gchar *create_typestr(Inkscape::NodePath::Path *np)
777     gchar *typestr = g_new(gchar, 32);
778     gint len = 32;
779     gint pos = 0;
781     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
782        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
784         if (pos >= len) {
785             typestr = g_renew(gchar, typestr, len + 32);
786             len += 32;
787         }
789         typestr[pos++] = 'c';
791        Inkscape::NodePath::Node *n;
792         n = sp->first->n.other;
793         while (n) {
794             gchar code;
796             switch (n->type) {
797                 case Inkscape::NodePath::NODE_CUSP:
798                     code = 'c';
799                     break;
800                 case Inkscape::NodePath::NODE_SMOOTH:
801                     code = 's';
802                     break;
803                 case Inkscape::NodePath::NODE_SYMM:
804                     code = 'z';
805                     break;
806                 default:
807                     g_assert_not_reached();
808                     code = '\0';
809                     break;
810             }
812             if (pos >= len) {
813                 typestr = g_renew(gchar, typestr, len + 32);
814                 len += 32;
815             }
817             typestr[pos++] = code;
819             if (n != sp->last) {
820                 n = n->n.other;
821             } else {
822                 n = NULL;
823             }
824         }
825     }
827     if (pos >= len) {
828         typestr = g_renew(gchar, typestr, len + 1);
829         len += 1;
830     }
832     typestr[pos++] = '\0';
834     return typestr;
837 /**
838  * Returns current path in context. // later eliminate this function at all!
839  */
840 static Inkscape::NodePath::Path *sp_nodepath_current()
842     if (!SP_ACTIVE_DESKTOP) {
843         return NULL;
844     }
846     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
848     if (!SP_IS_NODE_CONTEXT(event_context)) {
849         return NULL;
850     }
852     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
857 /**
858  \brief Fills node and handle positions for three nodes, splitting line
859   marked by end at distance t.
860  */
861 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
863     g_assert(new_path != NULL);
864     g_assert(end      != NULL);
866     g_assert(end->p.other == new_path);
867    Inkscape::NodePath::Node *start = new_path->p.other;
868     g_assert(start);
870     if (end->code == NR_LINETO) {
871         new_path->type =Inkscape::NodePath::NODE_CUSP;
872         new_path->code = NR_LINETO;
873         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
874     } else {
875         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
876         new_path->code = NR_CURVETO;
877         gdouble s      = 1 - t;
878         for (int dim = 0; dim < 2; dim++) {
879             NR::Coord const f000 = start->pos[dim];
880             NR::Coord const f001 = start->n.pos[dim];
881             NR::Coord const f011 = end->p.pos[dim];
882             NR::Coord const f111 = end->pos[dim];
883             NR::Coord const f00t = s * f000 + t * f001;
884             NR::Coord const f01t = s * f001 + t * f011;
885             NR::Coord const f11t = s * f011 + t * f111;
886             NR::Coord const f0tt = s * f00t + t * f01t;
887             NR::Coord const f1tt = s * f01t + t * f11t;
888             NR::Coord const fttt = s * f0tt + t * f1tt;
889             start->n.pos[dim]    = f00t;
890             new_path->p.pos[dim] = f0tt;
891             new_path->pos[dim]   = fttt;
892             new_path->n.pos[dim] = f1tt;
893             end->p.pos[dim]      = f11t;
894         }
895     }
898 /**
899  * Adds new node on direct line between two nodes, activates handles of all
900  * three nodes.
901  */
902 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
904     g_assert(end);
905     g_assert(end->subpath);
906     g_assert(g_list_find(end->subpath->nodes, end));
908    Inkscape::NodePath::Node *start = end->p.other;
909     g_assert( start->n.other == end );
910    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
911                                                end,
912                                                (NRPathcode)end->code == NR_LINETO?
913                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
914                                                (NRPathcode)end->code,
915                                                &start->pos, &start->pos, &start->n.pos);
916     sp_nodepath_line_midpoint(newnode, end, t);
918     sp_node_adjust_handles(start);
919     sp_node_update_handles(start);
920     sp_node_update_handles(newnode);
921     sp_node_adjust_handles(end);
922     sp_node_update_handles(end);
924     return newnode;
927 /**
928 \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
929 */
930 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
932     g_assert(node);
933     g_assert(node->subpath);
934     g_assert(g_list_find(node->subpath->nodes, node));
936    Inkscape::NodePath::SubPath *sp = node->subpath;
937     Inkscape::NodePath::Path *np    = sp->nodepath;
939     if (sp->closed) {
940         sp_nodepath_subpath_open(sp, node);
941         return sp->first;
942     } else {
943         // no break for end nodes
944         if (node == sp->first) return NULL;
945         if (node == sp->last ) return NULL;
947         // create a new subpath
948        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
950         // duplicate the break node as start of the new subpath
951         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
953         // attach rest of curve to new node
954         g_assert(node->n.other);
955         newnode->n.other = node->n.other; node->n.other = NULL;
956         newnode->n.other->p.other = newnode;
957         newsubpath->last = sp->last;
958         sp->last = node;
959         node = newnode;
960         while (node->n.other) {
961             node = node->n.other;
962             node->subpath = newsubpath;
963             sp->nodes = g_list_remove(sp->nodes, node);
964             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
965         }
968         return newnode;
969     }
972 /**
973  * Duplicate node and connect to neighbours.
974  */
975 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
977     g_assert(node);
978     g_assert(node->subpath);
979     g_assert(g_list_find(node->subpath->nodes, node));
981    Inkscape::NodePath::SubPath *sp = node->subpath;
983     NRPathcode code = (NRPathcode) node->code;
984     if (code == NR_MOVETO) { // if node is the endnode,
985         node->code = NR_LINETO; // new one is inserted before it, so change that to line
986     }
988     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
990     if (!node->n.other || !node->p.other) // if node is an endnode, select it
991         return node;
992     else
993         return newnode; // otherwise select the newly created node
996 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
998     node->p.pos = (node->pos + (node->pos - node->n.pos));
1001 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1003     node->n.pos = (node->pos + (node->pos - node->p.pos));
1006 /**
1007  * Change line type at node, with side effects on neighbours.
1008  */
1009 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1011     g_assert(end);
1012     g_assert(end->subpath);
1013     g_assert(end->p.other);
1015     if (end->code == static_cast< guint > ( code ) )
1016         return;
1018    Inkscape::NodePath::Node *start = end->p.other;
1020     end->code = code;
1022     if (code == NR_LINETO) {
1023         if (start->code == NR_LINETO) {
1024             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
1025         }
1026         if (end->n.other) {
1027             if (end->n.other->code == NR_LINETO) {
1028                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
1029             }
1030         }
1031     } else {
1032         NR::Point delta = end->pos - start->pos;
1033         start->n.pos = start->pos + delta / 3;
1034         end->p.pos = end->pos - delta / 3;
1035         sp_node_adjust_handle(start, 1);
1036         sp_node_adjust_handle(end, -1);
1037     }
1039     sp_node_update_handles(start);
1040     sp_node_update_handles(end);
1043 /**
1044  * Change node type, and its handles accordingly.
1045  */
1046 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1048     g_assert(node);
1049     g_assert(node->subpath);
1051     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1052         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1053             type =Inkscape::NodePath::NODE_CUSP;
1054         }
1055     }
1057     node->type = type;
1059     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1060         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1061         node->knot->setSize (node->selected? 11 : 9);
1062         sp_knot_update_ctrl(node->knot);
1063     } else {
1064         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1065         node->knot->setSize (node->selected? 9 : 7);
1066         sp_knot_update_ctrl(node->knot);
1067     }
1069     // if one of handles is mouseovered, preserve its position
1070     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1071         sp_node_adjust_handle(node, 1);
1072     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1073         sp_node_adjust_handle(node, -1);
1074     } else {
1075         sp_node_adjust_handles(node);
1076     }
1078     sp_node_update_handles(node);
1080     sp_nodepath_update_statusbar(node->subpath->nodepath);
1082     return node;
1085 bool
1086 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1088         Inkscape::NodePath::Node *othernode = side->other;
1089         if (!othernode)
1090             return false;
1091         NRPathcode const code = sp_node_path_code_from_side(node, side);
1092         if (code == NR_LINETO)
1093             return true;
1094         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1095         if (&node->p == side) {
1096             other_to_me = &othernode->n;
1097         } else if (&node->n == side) {
1098             other_to_me = &othernode->p;
1099         } 
1100         if (!other_to_me)
1101             return false;
1102         bool is_line = 
1103              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1104               NR::L2(node->pos - side->pos) < 1e-6);
1105         return is_line;
1108 /**
1109  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1110  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1111  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1112  * If already cusp and set to cusp, retracts handles.
1113 */
1114 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1116     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1118 /* 
1119   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1120  
1121         if (two_handles) {
1122             // do nothing, adjust_handles called via set_node_type will line them up
1123         } else if (one_handle) {
1124             if (opposite_to_handle_is_line) {
1125                 if (lined_up) {
1126                     // already half-smooth; pull opposite handle too making it fully smooth
1127                 } else {
1128                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1129                 }
1130             } else {
1131                 // pull opposite handle in line with the existing one
1132             }
1133         } else if (no_handles) {
1134             if (both_segments_are_lines OR both_segments_are_curves) {
1135                 //pull both handles
1136             } else {
1137                 // pull the handle opposite to line segment, making node half-smooth
1138             }
1139         }
1140 */
1141         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1142         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1143         bool p_is_line = sp_node_side_is_line(node, &node->p);
1144         bool n_is_line = sp_node_side_is_line(node, &node->n);
1146         if (p_has_handle && n_has_handle) {
1147             // do nothing, adjust_handles will line them up
1148         } else if (p_has_handle || n_has_handle) {
1149             if (p_has_handle && n_is_line) {
1150                 Radial line (node->n.other->pos - node->pos);
1151                 Radial handle (node->pos - node->p.pos);
1152                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1153                     // already half-smooth; pull opposite handle too making it fully smooth
1154                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1155                 } else {
1156                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1157                 }
1158             } else if (n_has_handle && p_is_line) {
1159                 Radial line (node->p.other->pos - node->pos);
1160                 Radial handle (node->pos - node->n.pos);
1161                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1162                     // already half-smooth; pull opposite handle too making it fully smooth
1163                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1164                 } else {
1165                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1166                 }
1167             } else if (p_has_handle && node->n.other) {
1168                 // pull n handle
1169                 node->n.other->code = NR_CURVETO;
1170                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1171                     NR::L2(node->p.pos - node->pos) :
1172                     NR::L2(node->n.other->pos - node->pos) / 3;
1173                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1174             } else if (n_has_handle && node->p.other) {
1175                 // pull p handle
1176                 node->code = NR_CURVETO;
1177                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1178                     NR::L2(node->n.pos - node->pos) :
1179                     NR::L2(node->p.other->pos - node->pos) / 3;
1180                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1181             }
1182         } else if (!p_has_handle && !n_has_handle) {
1183             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1184                 // no handles, but both segments are either lnes or curves:
1185                 //pull both handles
1187                 // convert both to curves:
1188                 node->code = NR_CURVETO;
1189                 node->n.other->code = NR_CURVETO;
1191                 NR::Point leg_prev = node->pos - node->p.other->pos;
1192                 NR::Point leg_next = node->pos - node->n.other->pos;
1194                 double norm_leg_prev = L2(leg_prev);
1195                 double norm_leg_next = L2(leg_next);
1197                 NR::Point delta;
1198                 if (norm_leg_next > 0.0) {
1199                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1200                     (&delta)->normalize();
1201                 }
1203                 if (type == Inkscape::NodePath::NODE_SYMM) {
1204                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1205                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1206                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1207                 } else {
1208                     // length of handle is proportional to distance to adjacent node
1209                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1210                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1211                 }
1213             } else {
1214                 // pull the handle opposite to line segment, making it half-smooth
1215                 if (p_is_line && node->n.other) {
1216                     if (type != Inkscape::NodePath::NODE_SYMM) {
1217                         // pull n handle
1218                         node->n.other->code = NR_CURVETO;
1219                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1220                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1221                     }
1222                 } else if (n_is_line && node->p.other) {
1223                     if (type != Inkscape::NodePath::NODE_SYMM) {
1224                         // pull p handle
1225                         node->code = NR_CURVETO;
1226                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1227                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1228                     }
1229                 }
1230             }
1231         }
1232     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1233         // cusping a cusp: retract nodes
1234         node->p.pos = node->pos;
1235         node->n.pos = node->pos;
1236     }
1238     sp_nodepath_set_node_type (node, type);
1241 /**
1242  * Move node to point, and adjust its and neighbouring handles.
1243  */
1244 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1246     NR::Point delta = p - node->pos;
1247     node->pos = p;
1249     node->p.pos += delta;
1250     node->n.pos += delta;
1252     Inkscape::NodePath::Node *node_p = NULL;
1253     Inkscape::NodePath::Node *node_n = NULL;
1255     if (node->p.other) {
1256         if (node->code == NR_LINETO) {
1257             sp_node_adjust_handle(node, 1);
1258             sp_node_adjust_handle(node->p.other, -1);
1259             node_p = node->p.other;
1260         }
1261     }
1262     if (node->n.other) {
1263         if (node->n.other->code == NR_LINETO) {
1264             sp_node_adjust_handle(node, -1);
1265             sp_node_adjust_handle(node->n.other, 1);
1266             node_n = node->n.other;
1267         }
1268     }
1270     // this function is only called from batch movers that will update display at the end
1271     // themselves, so here we just move all the knots without emitting move signals, for speed
1272     sp_node_update_handles(node, false);
1273     if (node_n) {
1274         sp_node_update_handles(node_n, false);
1275     }
1276     if (node_p) {
1277         sp_node_update_handles(node_p, false);
1278     }
1281 /**
1282  * Call sp_node_moveto() for node selection and handle possible snapping.
1283  */
1284 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1285                                             bool const snap, bool constrained = false, 
1286                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1288     NR::Coord best = NR_HUGE;
1289     NR::Point delta(dx, dy);
1290     NR::Point best_pt = delta;
1291     Inkscape::SnappedPoint best_abs;
1292     
1293     if (snap) {    
1294         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1295          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1296          * must provide that information. */
1297           
1298         // Build a list of the unselected nodes to which the snapper should snap 
1299         std::vector<NR::Point> unselected_nodes;
1300         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1301             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1302             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1303                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1304                 if (!node->selected) {
1305                     unselected_nodes.push_back(node->pos);
1306                 }    
1307             }
1308         }        
1309         
1310         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1311         
1312         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1313             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1314             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1315             Inkscape::SnappedPoint s;
1316             if (constrained) {
1317                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1318                 dedicated_constraint.setPoint(n->pos);
1319                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1320             } else {
1321                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1322             }            
1323             if (s.getSnapped() && (s.getDistance() < best)) {
1324                 best = s.getDistance();
1325                 best_abs = s;
1326                 best_pt = s.getPoint() - n->pos;
1327             }
1328         }
1329                         
1330         if (best_abs.getSnapped()) {
1331             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1332         } else {
1333             nodepath->desktop->snapindicator->remove_snappoint();    
1334         }
1335     }
1337     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1338         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1339         sp_node_moveto(n, n->pos + best_pt);
1340     }
1342     // do not update repr here so that node dragging is acceptably fast
1343     update_object(nodepath);
1346 /**
1347 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1348 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1349 near x = 0.
1350  */
1351 double
1352 sculpt_profile (double x, double alpha, guint profile)
1354     if (x >= 1)
1355         return 0;
1356     if (x <= 0)
1357         return 1;
1359     switch (profile) {
1360         case SCULPT_PROFILE_LINEAR:
1361         return 1 - x;
1362         case SCULPT_PROFILE_BELL:
1363         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1364         case SCULPT_PROFILE_ELLIPTIC:
1365         return sqrt(1 - x*x);
1366     }
1368     return 1;
1371 double
1372 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1374     // extremely primitive for now, don't have time to look for the real one
1375     double lower = NR::L2(b - a);
1376     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1377     return (lower + upper)/2;
1380 void
1381 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1383     n->pos = n->origin + delta;
1384     n->n.pos = n->n.origin + delta_n;
1385     n->p.pos = n->p.origin + delta_p;
1386     sp_node_adjust_handles(n);
1387     sp_node_update_handles(n, false);
1390 /**
1391  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1392  * on how far they are from the dragged node n.
1393  */
1394 static void
1395 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1397     g_assert (n);
1398     g_assert (nodepath);
1399     g_assert (n->subpath->nodepath == nodepath);
1401     double pressure = n->knot->pressure;
1402     if (pressure == 0)
1403         pressure = 0.5; // default
1404     pressure = CLAMP (pressure, 0.2, 0.8);
1406     // map pressure to alpha = 1/5 ... 5
1407     double alpha = 1 - 2 * fabs(pressure - 0.5);
1408     if (pressure > 0.5)
1409         alpha = 1/alpha;
1411     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1413     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1414         // Only one subpath has selected nodes:
1415         // use linear mode, where the distance from n to node being dragged is calculated along the path
1417         double n_sel_range = 0, p_sel_range = 0;
1418         guint n_nodes = 0, p_nodes = 0;
1419         guint n_sel_nodes = 0, p_sel_nodes = 0;
1421         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1422         {
1423             double n_range = 0, p_range = 0;
1424             bool n_going = true, p_going = true;
1425             Inkscape::NodePath::Node *n_node = n;
1426             Inkscape::NodePath::Node *p_node = n;
1427             do {
1428                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1429                 if (n_node && n_going)
1430                     n_node = n_node->n.other;
1431                 if (n_node == NULL) {
1432                     n_going = false;
1433                 } else {
1434                     n_nodes ++;
1435                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1436                     if (n_node->selected) {
1437                         n_sel_nodes ++;
1438                         n_sel_range = n_range;
1439                     }
1440                     if (n_node == p_node) {
1441                         n_going = false;
1442                         p_going = false;
1443                     }
1444                 }
1445                 if (p_node && p_going)
1446                     p_node = p_node->p.other;
1447                 if (p_node == NULL) {
1448                     p_going = false;
1449                 } else {
1450                     p_nodes ++;
1451                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1452                     if (p_node->selected) {
1453                         p_sel_nodes ++;
1454                         p_sel_range = p_range;
1455                     }
1456                     if (p_node == n_node) {
1457                         n_going = false;
1458                         p_going = false;
1459                     }
1460                 }
1461             } while (n_going || p_going);
1462         }
1464         // Second pass: actually move nodes in this subpath
1465         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1466         {
1467             double n_range = 0, p_range = 0;
1468             bool n_going = true, p_going = true;
1469             Inkscape::NodePath::Node *n_node = n;
1470             Inkscape::NodePath::Node *p_node = n;
1471             do {
1472                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1473                 if (n_node && n_going)
1474                     n_node = n_node->n.other;
1475                 if (n_node == NULL) {
1476                     n_going = false;
1477                 } else {
1478                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1479                     if (n_node->selected) {
1480                         sp_nodepath_move_node_and_handles (n_node,
1481                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1482                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1483                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1484                     }
1485                     if (n_node == p_node) {
1486                         n_going = false;
1487                         p_going = false;
1488                     }
1489                 }
1490                 if (p_node && p_going)
1491                     p_node = p_node->p.other;
1492                 if (p_node == NULL) {
1493                     p_going = false;
1494                 } else {
1495                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1496                     if (p_node->selected) {
1497                         sp_nodepath_move_node_and_handles (p_node,
1498                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1499                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1500                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1501                     }
1502                     if (p_node == n_node) {
1503                         n_going = false;
1504                         p_going = false;
1505                     }
1506                 }
1507             } while (n_going || p_going);
1508         }
1510     } else {
1511         // Multiple subpaths have selected nodes:
1512         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1513         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1514         // fix the pear-like shape when sculpting e.g. a ring
1516         // First pass: calculate range
1517         gdouble direct_range = 0;
1518         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1519             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1520             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1521                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1522                 if (node->selected) {
1523                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1524                 }
1525             }
1526         }
1528         // Second pass: actually move nodes
1529         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1530             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1531             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1532                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1533                 if (node->selected) {
1534                     if (direct_range > 1e-6) {
1535                         sp_nodepath_move_node_and_handles (node,
1536                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1537                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1538                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1539                     } else {
1540                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1541                     }
1543                 }
1544             }
1545         }
1546     }
1548     // do not update repr here so that node dragging is acceptably fast
1549     update_object(nodepath);
1553 /**
1554  * Move node selection to point, adjust its and neighbouring handles,
1555  * handle possible snapping, and commit the change with possible undo.
1556  */
1557 void
1558 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1560     if (!nodepath) return;
1562     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1564     if (dx == 0) {
1565         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1566     } else if (dy == 0) {
1567         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1568     } else {
1569         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1570     }
1573 /**
1574  * Move node selection off screen and commit the change.
1575  */
1576 void
1577 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1579     // borrowed from sp_selection_move_screen in selection-chemistry.c
1580     // we find out the current zoom factor and divide deltas by it
1581     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1583     gdouble zoom = desktop->current_zoom();
1584     gdouble zdx = dx / zoom;
1585     gdouble zdy = dy / zoom;
1587     if (!nodepath) return;
1589     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1591     if (dx == 0) {
1592         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1593     } else if (dy == 0) {
1594         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1595     } else {
1596         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1597     }
1600 /**
1601  * Move selected nodes to the absolute position given
1602  */
1603 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1605     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1606         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1607         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1608         sp_node_moveto(n, npos);
1609     }
1611     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1614 /**
1615  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1616  */
1617 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1619     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1620     g_return_val_if_fail(nodepath->selected, no_coord);
1622     // determine coordinate of first selected node
1623     GList *nsel = nodepath->selected;
1624     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1625     NR::Coord coord = n->pos[axis];
1626     bool coincide = true;
1628     // compare it to the coordinates of all the other selected nodes
1629     for (GList *l = nsel->next; l != NULL; l = l->next) {
1630         n = (Inkscape::NodePath::Node *) l->data;
1631         if (n->pos[axis] != coord) {
1632             coincide = false;
1633         }
1634     }
1635     if (coincide) {
1636         return coord;
1637     } else {
1638         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1639         // currently we return the coordinate of the bounding box midpoint because I don't know how
1640         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1641         return bbox.midpoint()[axis];
1642     }
1645 /** If they don't yet exist, creates knot and line for the given side of the node */
1646 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1648     if (!side->knot) {
1649         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"));
1651         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1652         side->knot->setSize (7);
1653         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1654         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1655         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1656         sp_knot_update_ctrl(side->knot);
1658         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1659         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1660         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1661         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1662         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1663         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1664     }
1666     if (!side->line) {
1667         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1668                                         SP_TYPE_CTRLLINE, NULL);
1669     }
1672 /**
1673  * Ensure the given handle of the node is visible/invisible, update its screen position
1674  */
1675 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1677     g_assert(node != NULL);
1679    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1680     NRPathcode code = sp_node_path_code_from_side(node, side);
1682     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1684     if (show_handle) {
1685         if (!side->knot) { // No handle knot at all
1686             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1687             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1688             side->knot->pos = side->pos;
1689             if (side->knot->item)
1690                 SP_CTRL(side->knot->item)->moveto(side->pos);
1691             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1692             sp_knot_show(side->knot);
1693         } else {
1694             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1695                 if (fire_move_signals) {
1696                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1697                 } else {
1698                     sp_knot_moveto(side->knot, side->pos);
1699                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1700                 }
1701             }
1702             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1703                 sp_knot_show(side->knot);
1704             }
1705         }
1706         sp_canvas_item_show(side->line);
1707     } else {
1708         if (side->knot) {
1709             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1710                 sp_knot_hide(side->knot);
1711             }
1712         }
1713         if (side->line) {
1714             sp_canvas_item_hide(side->line);
1715         }
1716     }
1719 /**
1720  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1721  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1722  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1723  * updated; otherwise, just move the knots silently (used in batch moves).
1724  */
1725 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1727     g_assert(node != NULL);
1729     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1730         sp_knot_show(node->knot);
1731     }
1733     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1734         if (fire_move_signals)
1735             sp_knot_set_position(node->knot, node->pos, 0);
1736         else
1737             sp_knot_moveto(node->knot, node->pos);
1738     }
1740     gboolean show_handles = node->selected;
1741     if (node->p.other != NULL) {
1742         if (node->p.other->selected) show_handles = TRUE;
1743     }
1744     if (node->n.other != NULL) {
1745         if (node->n.other->selected) show_handles = TRUE;
1746     }
1748     if (node->subpath->nodepath->show_handles == false)
1749         show_handles = FALSE;
1751     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1752     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1755 /**
1756  * Call sp_node_update_handles() for all nodes on subpath.
1757  */
1758 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1760     g_assert(subpath != NULL);
1762     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1763         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1764     }
1767 /**
1768  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1769  */
1770 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1772     g_assert(nodepath != NULL);
1774     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1775         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1776     }
1779 void
1780 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1782     if (nodepath == NULL) return;
1784     nodepath->show_handles = show;
1785     sp_nodepath_update_handles(nodepath);
1788 /**
1789  * Adds all selected nodes in nodepath to list.
1790  */
1791 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1793     StlConv<Node *>::list(l, selected);
1794 /// \todo this adds a copying, rework when the selection becomes a stl list
1797 /**
1798  * Align selected nodes on the specified axis.
1799  */
1800 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1802     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1803         return;
1804     }
1806     if ( !nodepath->selected->next ) { // only one node selected
1807         return;
1808     }
1809    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1810     NR::Point dest(pNode->pos);
1811     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1812         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1813         if (pNode) {
1814             dest[axis] = pNode->pos[axis];
1815             sp_node_moveto(pNode, dest);
1816         }
1817     }
1819     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1822 /// Helper struct.
1823 struct NodeSort
1825    Inkscape::NodePath::Node *_node;
1826     NR::Coord _coord;
1827     /// \todo use vectorof pointers instead of calling copy ctor
1828     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1829         _node(node), _coord(node->pos[axis])
1830     {}
1832 };
1834 static bool operator<(NodeSort const &a, NodeSort const &b)
1836     return (a._coord < b._coord);
1839 /**
1840  * Distribute selected nodes on the specified axis.
1841  */
1842 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1844     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1845         return;
1846     }
1848     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1849         return;
1850     }
1852    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1853     std::vector<NodeSort> sorted;
1854     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1855         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1856         if (pNode) {
1857             NodeSort n(pNode, axis);
1858             sorted.push_back(n);
1859             //dest[axis] = pNode->pos[axis];
1860             //sp_node_moveto(pNode, dest);
1861         }
1862     }
1863     std::sort(sorted.begin(), sorted.end());
1864     unsigned int len = sorted.size();
1865     //overall bboxes span
1866     float dist = (sorted.back()._coord -
1867                   sorted.front()._coord);
1868     //new distance between each bbox
1869     float step = (dist) / (len - 1);
1870     float pos = sorted.front()._coord;
1871     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1872           it < sorted.end();
1873           it ++ )
1874     {
1875         NR::Point dest((*it)._node->pos);
1876         dest[axis] = pos;
1877         sp_node_moveto((*it)._node, dest);
1878         pos += step;
1879     }
1881     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1885 /**
1886  * Call sp_nodepath_line_add_node() for all selected segments.
1887  */
1888 void
1889 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1891     if (!nodepath) {
1892         return;
1893     }
1895     GList *nl = NULL;
1897     int n_added = 0;
1899     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1900        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1901         g_assert(t->selected);
1902         if (t->p.other && t->p.other->selected) {
1903             nl = g_list_prepend(nl, t);
1904         }
1905     }
1907     while (nl) {
1908        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1909        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1910        sp_nodepath_node_select(n, TRUE, FALSE);
1911        n_added ++;
1912        nl = g_list_remove(nl, t);
1913     }
1915     /** \todo fixme: adjust ? */
1916     sp_nodepath_update_handles(nodepath);
1918     if (n_added > 1) {
1919         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1920     } else if (n_added > 0) {
1921         sp_nodepath_update_repr(nodepath, _("Add node"));
1922     }
1924     sp_nodepath_update_statusbar(nodepath);
1927 /**
1928  * Select segment nearest to point
1929  */
1930 void
1931 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1933     if (!nodepath) {
1934         return;
1935     }
1937     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1938     Geom::PathVector const &pathv = curve->get_pathvector();
1939     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1941     // calculate index for nodepath's representation.
1942     unsigned int segment_index = floor(pvpos.t) + 1;
1943     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1944         segment_index += pathv[i].size() + 1;
1945         if (pathv[i].closed()) {
1946             segment_index += 1;
1947         }
1948     }
1950     curve->unref();
1952     //find segment to segment
1953     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
1955     //fixme: this can return NULL, so check before proceeding.
1956     g_return_if_fail(e != NULL);
1958     gboolean force = FALSE;
1959     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1960         force = TRUE;
1961     }
1962     sp_nodepath_node_select(e, (gboolean) toggle, force);
1963     if (e->p.other)
1964         sp_nodepath_node_select(e->p.other, TRUE, force);
1966     sp_nodepath_update_handles(nodepath);
1968     sp_nodepath_update_statusbar(nodepath);
1971 /**
1972  * Add a node nearest to point
1973  */
1974 void
1975 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1977     if (!nodepath) {
1978         return;
1979     }
1981     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1982     Geom::PathVector const &pathv = curve->get_pathvector();
1983     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1985     // calculate index for nodepath's representation.
1986     double int_part;
1987     double t = std::modf(pvpos.t, &int_part);
1988     unsigned int segment_index = (unsigned int)int_part + 1;
1989     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1990         segment_index += pathv[i].size() + 1;
1991         if (pathv[i].closed()) {
1992             segment_index += 1;
1993         }
1994     }
1996     curve->unref();
1998     //find segment to split
1999     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
2001     //don't know why but t seems to flip for lines
2002     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2003         t = 1.0 - t;
2004     }
2006     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2007     sp_nodepath_node_select(n, FALSE, TRUE);
2009     /* fixme: adjust ? */
2010     sp_nodepath_update_handles(nodepath);
2012     sp_nodepath_update_repr(nodepath, _("Add node"));
2014     sp_nodepath_update_statusbar(nodepath);
2017 /*
2018  * Adjusts a segment so that t moves by a certain delta for dragging
2019  * converts lines to curves
2020  *
2021  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2022  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2023  */
2024 void
2025 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
2027     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
2029     //fixme: e and e->p can be NULL, so check for those before proceeding
2030     g_return_if_fail(e != NULL);
2031     g_return_if_fail(&e->p != NULL);
2033     /* feel good is an arbitrary parameter that distributes the delta between handles
2034      * if t of the drag point is less than 1/6 distance form the endpoint only
2035      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2036      */
2037     double feel_good;
2038     if (t <= 1.0 / 6.0)
2039         feel_good = 0;
2040     else if (t <= 0.5)
2041         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2042     else if (t <= 5.0 / 6.0)
2043         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2044     else
2045         feel_good = 1;
2047     //if we're dragging a line convert it to a curve
2048     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2049         sp_nodepath_set_line_type(e, NR_CURVETO);
2050     }
2052     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2053     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2054     e->p.other->n.pos += offsetcoord0;
2055     e->p.pos += offsetcoord1;
2057     // adjust handles of adjacent nodes where necessary
2058     sp_node_adjust_handle(e,1);
2059     sp_node_adjust_handle(e->p.other,-1);
2061     sp_nodepath_update_handles(e->subpath->nodepath);
2063     update_object(e->subpath->nodepath);
2065     sp_nodepath_update_statusbar(e->subpath->nodepath);
2069 /**
2070  * Call sp_nodepath_break() for all selected segments.
2071  */
2072 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2074     if (!nodepath) return;
2076     GList *tempin = g_list_copy(nodepath->selected);
2077     GList *temp = NULL;
2078     for (GList *l = tempin; l != NULL; l = l->next) {
2079        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2080        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2081         if (nn == NULL) continue; // no break, no new node
2082         temp = g_list_prepend(temp, nn);
2083     }
2084     g_list_free(tempin);
2086     if (temp) {
2087         sp_nodepath_deselect(nodepath);
2088     }
2089     for (GList *l = temp; l != NULL; l = l->next) {
2090         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2091     }
2093     sp_nodepath_update_handles(nodepath);
2095     sp_nodepath_update_repr(nodepath, _("Break path"));
2098 /**
2099  * Duplicate the selected node(s).
2100  */
2101 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2103     if (!nodepath) {
2104         return;
2105     }
2107     GList *temp = NULL;
2108     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2109        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2110        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2111         if (nn == NULL) continue; // could not duplicate
2112         temp = g_list_prepend(temp, nn);
2113     }
2115     if (temp) {
2116         sp_nodepath_deselect(nodepath);
2117     }
2118     for (GList *l = temp; l != NULL; l = l->next) {
2119         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2120     }
2122     sp_nodepath_update_handles(nodepath);
2124     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2127 /**
2128  *  Internal function to join two nodes by merging them into one.
2129  */
2130 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2132     /* a and b are endpoints */
2134     // if one of the two nodes is mouseovered, fix its position
2135     NR::Point c;
2136     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2137         c = a->pos;
2138     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2139         c = b->pos;
2140     } else {
2141         // otherwise, move joined node to the midpoint
2142         c = (a->pos + b->pos) / 2;
2143     }
2145     if (a->subpath == b->subpath) {
2146        Inkscape::NodePath::SubPath *sp = a->subpath;
2147         sp_nodepath_subpath_close(sp);
2148         sp_node_moveto (sp->first, c);
2150         sp_nodepath_update_handles(sp->nodepath);
2151         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2152         return;
2153     }
2155     /* a and b are separate subpaths */
2156     Inkscape::NodePath::SubPath *sa = a->subpath;
2157     Inkscape::NodePath::SubPath *sb = b->subpath;
2158     NR::Point p;
2159     Inkscape::NodePath::Node *n;
2160     NRPathcode code;
2161     if (a == sa->first) {
2162         // we will now reverse sa, so that a is its last node, not first, and drop that node
2163         p = sa->first->n.pos;
2164         code = (NRPathcode)sa->first->n.other->code;
2165         // create new subpath
2166        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2167        // create a first moveto node on it
2168         n = sa->last;
2169         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2170         n = n->p.other;
2171         if (n == sa->first) n = NULL;
2172         while (n) {
2173             // copy the rest of the nodes from sa to t, going backwards
2174             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2175             n = n->p.other;
2176             if (n == sa->first) n = NULL;
2177         }
2178         // replace sa with t
2179         sp_nodepath_subpath_destroy(sa);
2180         sa = t;
2181     } else if (a == sa->last) {
2182         // a is already last, just drop it
2183         p = sa->last->p.pos;
2184         code = (NRPathcode)sa->last->code;
2185         sp_nodepath_node_destroy(sa->last);
2186     } else {
2187         code = NR_END;
2188         g_assert_not_reached();
2189     }
2191     if (b == sb->first) {
2192         // copy all nodes from b to a, forward 
2193         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2194         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2195             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2196         }
2197     } else if (b == sb->last) {
2198         // copy all nodes from b to a, backward 
2199         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2200         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2201             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2202         }
2203     } else {
2204         g_assert_not_reached();
2205     }
2206     /* and now destroy sb */
2208     sp_nodepath_subpath_destroy(sb);
2210     sp_nodepath_update_handles(sa->nodepath);
2212     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2214     sp_nodepath_update_statusbar(nodepath);
2217 /**
2218  *  Internal function to join two nodes by adding a segment between them.
2219  */
2220 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2222     if (a->subpath == b->subpath) {
2223        Inkscape::NodePath::SubPath *sp = a->subpath;
2225         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2226         sp->closed = TRUE;
2228         sp->first->p.other = sp->last;
2229         sp->last->n.other  = sp->first;
2231         sp_node_handle_mirror_p_to_n(sp->last);
2232         sp_node_handle_mirror_n_to_p(sp->first);
2234         sp->first->code = sp->last->code;
2235         sp->first       = sp->last;
2237         sp_nodepath_update_handles(sp->nodepath);
2239         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2241         return;
2242     }
2244     /* a and b are separate subpaths */
2245     Inkscape::NodePath::SubPath *sa = a->subpath;
2246     Inkscape::NodePath::SubPath *sb = b->subpath;
2248     Inkscape::NodePath::Node *n;
2249     NR::Point p;
2250     NRPathcode code;
2251     if (a == sa->first) {
2252         code = (NRPathcode) sa->first->n.other->code;
2253        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2254         n = sa->last;
2255         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2256         for (n = n->p.other; n != NULL; n = n->p.other) {
2257             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2258         }
2259         sp_nodepath_subpath_destroy(sa);
2260         sa = t;
2261     } else if (a == sa->last) {
2262         code = (NRPathcode)sa->last->code;
2263     } else {
2264         code = NR_END;
2265         g_assert_not_reached();
2266     }
2268     if (b == sb->first) {
2269         n = sb->first;
2270         sp_node_handle_mirror_p_to_n(sa->last);
2271         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2272         sp_node_handle_mirror_n_to_p(sa->last);
2273         for (n = n->n.other; n != NULL; n = n->n.other) {
2274             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2275         }
2276     } else if (b == sb->last) {
2277         n = sb->last;
2278         sp_node_handle_mirror_p_to_n(sa->last);
2279         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2280         sp_node_handle_mirror_n_to_p(sa->last);
2281         for (n = n->p.other; n != NULL; n = n->p.other) {
2282             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2283         }
2284     } else {
2285         g_assert_not_reached();
2286     }
2287     /* and now destroy sb */
2289     sp_nodepath_subpath_destroy(sb);
2291     sp_nodepath_update_handles(sa->nodepath);
2293     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2296 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2298 /**
2299  * Internal function to handle joining two nodes.
2300  */
2301 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2303     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2305     if (g_list_length(nodepath->selected) != 2) {
2306         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2307         return;
2308     }
2310     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2311     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2313     g_assert(a != b);
2314     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2315         // someone tried to join an orphan node (i.e. a single-node subpath).
2316         // this is not worth an error message, just fail silently.
2317         return;
2318     }
2320     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2321         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2322         return;
2323     }
2325     switch(mode) {
2326         case NODE_JOIN_ENDPOINTS:
2327             do_node_selected_join(nodepath, a, b);
2328             break;
2329         case NODE_JOIN_SEGMENT:
2330             do_node_selected_join_segment(nodepath, a, b);
2331             break;
2332     }
2335 /**
2336  *  Join two nodes by merging them into one.
2337  */
2338 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2340     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2343 /**
2344  *  Join two nodes by adding a segment between them.
2345  */
2346 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2348     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2351 /**
2352  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2353  */
2354 void sp_node_delete_preserve(GList *nodes_to_delete)
2356     GSList *nodepaths = NULL;
2358     while (nodes_to_delete) {
2359         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2360         Inkscape::NodePath::SubPath *sp = node->subpath;
2361         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2362         Inkscape::NodePath::Node *sample_cursor = NULL;
2363         Inkscape::NodePath::Node *sample_end = NULL;
2364         Inkscape::NodePath::Node *delete_cursor = node;
2365         bool just_delete = false;
2367         //find the start of this contiguous selection
2368         //move left to the first node that is not selected
2369         //or the start of the non-closed path
2370         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2371             delete_cursor = curr;
2372         }
2374         //just delete at the beginning of an open path
2375         if (!delete_cursor->p.other) {
2376             sample_cursor = delete_cursor;
2377             just_delete = true;
2378         } else {
2379             sample_cursor = delete_cursor->p.other;
2380         }
2382         //calculate points for each segment
2383         int rate = 5;
2384         float period = 1.0 / rate;
2385         std::vector<NR::Point> data;
2386         if (!just_delete) {
2387             data.push_back(sample_cursor->pos);
2388             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2389                 //just delete at the end of an open path
2390                 if (!sp->closed && curr == sp->last) {
2391                     just_delete = true;
2392                     break;
2393                 }
2395                 //sample points on the contiguous selected segment
2396                 NR::Point *bez;
2397                 bez = new NR::Point [4];
2398                 bez[0] = curr->pos;
2399                 bez[1] = curr->n.pos;
2400                 bez[2] = curr->n.other->p.pos;
2401                 bez[3] = curr->n.other->pos;
2402                 for (int i=1; i<rate; i++) {
2403                     gdouble t = i * period;
2404                     NR::Point p = bezier_pt(3, bez, t);
2405                     data.push_back(p);
2406                 }
2407                 data.push_back(curr->n.other->pos);
2409                 sample_end = curr->n.other;
2410                 //break if we've come full circle or hit the end of the selection
2411                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2412                     break;
2413                 }
2414             }
2415         }
2417         if (!just_delete) {
2418             //calculate the best fitting single segment and adjust the endpoints
2419             NR::Point *adata;
2420             adata = new NR::Point [data.size()];
2421             copy(data.begin(), data.end(), adata);
2423             NR::Point *bez;
2424             bez = new NR::Point [4];
2425             //would decreasing error create a better fitting approximation?
2426             gdouble error = 1.0;
2427             gint ret;
2428             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2430             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2431             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2432             //the resulting nodes behave as expected.
2433             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2434                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2435             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2436                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2438             //adjust endpoints
2439             sample_cursor->n.pos = bez[1];
2440             sample_end->p.pos = bez[2];
2441         }
2443         //destroy this contiguous selection
2444         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2445             Inkscape::NodePath::Node *temp = delete_cursor;
2446             if (delete_cursor->n.other == delete_cursor) {
2447                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2448                 delete_cursor = NULL;
2449             } else {
2450                 delete_cursor = delete_cursor->n.other;
2451             }
2452             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2453             sp_nodepath_node_destroy(temp);
2454         }
2456         sp_nodepath_update_handles(nodepath);
2458         if (!g_slist_find(nodepaths, nodepath))
2459             nodepaths = g_slist_prepend (nodepaths, nodepath);
2460     }
2462     for (GSList *i = nodepaths; i; i = i->next) {
2463         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2464         // different nodepaths will give us one undo event per nodepath
2465         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2467         // if the entire nodepath is removed, delete the selected object.
2468         if (nodepath->subpaths == NULL ||
2469             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2470             //at least 2
2471             sp_nodepath_get_node_count(nodepath) < 2) {
2472             SPDocument *document = sp_desktop_document (nodepath->desktop);
2473             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2474             //delete this nodepath's object, not the entire selection! (though at this time, this
2475             //does not matter)
2476             sp_selection_delete();
2477             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2478                               _("Delete nodes"));
2479         } else {
2480             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2481             sp_nodepath_update_statusbar(nodepath);
2482         }
2483     }
2485     g_slist_free (nodepaths);
2488 /**
2489  * Delete one or more selected nodes.
2490  */
2491 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2493     if (!nodepath) return;
2494     if (!nodepath->selected) return;
2496     /** \todo fixme: do it the right way */
2497     while (nodepath->selected) {
2498        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2499         sp_nodepath_node_destroy(node);
2500     }
2503     //clean up the nodepath (such as for trivial subpaths)
2504     sp_nodepath_cleanup(nodepath);
2506     sp_nodepath_update_handles(nodepath);
2508     // if the entire nodepath is removed, delete the selected object.
2509     if (nodepath->subpaths == NULL ||
2510         sp_nodepath_get_node_count(nodepath) < 2) {
2511         SPDocument *document = sp_desktop_document (nodepath->desktop);
2512         sp_selection_delete();
2513         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2514                           _("Delete nodes"));
2515         return;
2516     }
2518     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2520     sp_nodepath_update_statusbar(nodepath);
2523 /**
2524  * Delete one or more segments between two selected nodes.
2525  * This is the code for 'split'.
2526  */
2527 void
2528 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2530    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2531    Inkscape::NodePath::Node *curr, *next;     //Iterators
2533     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2535     if (g_list_length(nodepath->selected) != 2) {
2536         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2537                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2538         return;
2539     }
2541     //Selected nodes, not inclusive
2542    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2543    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2545     if ( ( a==b)                       ||  //same node
2546          (a->subpath  != b->subpath )  ||  //not the same path
2547          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2548          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2549     {
2550         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2551                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2552         return;
2553     }
2555     //###########################################
2556     //# BEGIN EDITS
2557     //###########################################
2558     //##################################
2559     //# CLOSED PATH
2560     //##################################
2561     if (a->subpath->closed) {
2564         gboolean reversed = FALSE;
2566         //Since we can go in a circle, we need to find the shorter distance.
2567         //  a->b or b->a
2568         start = end = NULL;
2569         int distance    = 0;
2570         int minDistance = 0;
2571         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2572             if (curr==b) {
2573                 //printf("a to b:%d\n", distance);
2574                 start = a;//go from a to b
2575                 end   = b;
2576                 minDistance = distance;
2577                 //printf("A to B :\n");
2578                 break;
2579             }
2580             distance++;
2581         }
2583         //try again, the other direction
2584         distance = 0;
2585         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2586             if (curr==a) {
2587                 //printf("b to a:%d\n", distance);
2588                 if (distance < minDistance) {
2589                     start    = b;  //we go from b to a
2590                     end      = a;
2591                     reversed = TRUE;
2592                     //printf("B to A\n");
2593                 }
2594                 break;
2595             }
2596             distance++;
2597         }
2600         //Copy everything from 'end' to 'start' to a new subpath
2601        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2602         for (curr=end ; curr ; curr=curr->n.other) {
2603             NRPathcode code = (NRPathcode) curr->code;
2604             if (curr == end)
2605                 code = NR_MOVETO;
2606             sp_nodepath_node_new(t, NULL,
2607                                  (Inkscape::NodePath::NodeType)curr->type, code,
2608                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2609             if (curr == start)
2610                 break;
2611         }
2612         sp_nodepath_subpath_destroy(a->subpath);
2615     }
2619     //##################################
2620     //# OPEN PATH
2621     //##################################
2622     else {
2624         //We need to get the direction of the list between A and B
2625         //Can we walk from a to b?
2626         start = end = NULL;
2627         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2628             if (curr==b) {
2629                 start = a;  //did it!  we go from a to b
2630                 end   = b;
2631                 //printf("A to B\n");
2632                 break;
2633             }
2634         }
2635         if (!start) {//didn't work?  let's try the other direction
2636             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2637                 if (curr==a) {
2638                     start = b;  //did it!  we go from b to a
2639                     end   = a;
2640                     //printf("B to A\n");
2641                     break;
2642                 }
2643             }
2644         }
2645         if (!start) {
2646             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2647                                                      _("Cannot find path between nodes."));
2648             return;
2649         }
2653         //Copy everything after 'end' to a new subpath
2654        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2655         for (curr=end ; curr ; curr=curr->n.other) {
2656             NRPathcode code = (NRPathcode) curr->code;
2657             if (curr == end)
2658                 code = NR_MOVETO;
2659             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2660                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2661         }
2663         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2664         for (curr = start->n.other ; curr  ; curr=next) {
2665             next = curr->n.other;
2666             sp_nodepath_node_destroy(curr);
2667         }
2669     }
2670     //###########################################
2671     //# END EDITS
2672     //###########################################
2674     //clean up the nodepath (such as for trivial subpaths)
2675     sp_nodepath_cleanup(nodepath);
2677     sp_nodepath_update_handles(nodepath);
2679     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2681     sp_nodepath_update_statusbar(nodepath);
2684 /**
2685  * Call sp_nodepath_set_line() for all selected segments.
2686  */
2687 void
2688 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2690     if (nodepath == NULL) return;
2692     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2693        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2694         g_assert(n->selected);
2695         if (n->p.other && n->p.other->selected) {
2696             sp_nodepath_set_line_type(n, code);
2697         }
2698     }
2700     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2703 /**
2704  * Call sp_nodepath_convert_node_type() for all selected nodes.
2705  */
2706 void
2707 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2709     if (nodepath == NULL) return;
2711     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2713     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2714         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2715     }
2717     sp_nodepath_update_repr(nodepath, _("Change node type"));
2720 /**
2721  * Change select status of node, update its own and neighbour handles.
2722  */
2723 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2725     node->selected = selected;
2727     if (selected) {
2728         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2729         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2730         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2731         sp_knot_update_ctrl(node->knot);
2732     } else {
2733         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2734         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2735         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2736         sp_knot_update_ctrl(node->knot);
2737     }
2739     sp_node_update_handles(node);
2740     if (node->n.other) sp_node_update_handles(node->n.other);
2741     if (node->p.other) sp_node_update_handles(node->p.other);
2744 /**
2745 \brief Select a node
2746 \param node     The node to select
2747 \param incremental   If true, add to selection, otherwise deselect others
2748 \param override   If true, always select this node, otherwise toggle selected status
2749 */
2750 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2752     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2754     if (incremental) {
2755         if (override) {
2756             if (!g_list_find(nodepath->selected, node)) {
2757                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2758             }
2759             sp_node_set_selected(node, TRUE);
2760         } else { // toggle
2761             if (node->selected) {
2762                 g_assert(g_list_find(nodepath->selected, node));
2763                 nodepath->selected = g_list_remove(nodepath->selected, node);
2764             } else {
2765                 g_assert(!g_list_find(nodepath->selected, node));
2766                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2767             }
2768             sp_node_set_selected(node, !node->selected);
2769         }
2770     } else {
2771         sp_nodepath_deselect(nodepath);
2772         nodepath->selected = g_list_prepend(nodepath->selected, node);
2773         sp_node_set_selected(node, TRUE);
2774     }
2776     sp_nodepath_update_statusbar(nodepath);
2780 /**
2781 \brief Deselect all nodes in the nodepath
2782 */
2783 void
2784 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2786     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2788     while (nodepath->selected) {
2789         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2790         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2791     }
2792     sp_nodepath_update_statusbar(nodepath);
2795 /**
2796 \brief Select or invert selection of all nodes in the nodepath
2797 */
2798 void
2799 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2801     if (!nodepath) return;
2803     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2804        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2805         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2806            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2807            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2808         }
2809     }
2812 /**
2813  * If nothing selected, does the same as sp_nodepath_select_all();
2814  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2815  * (i.e., similar to "select all in layer", with the "selected" subpaths
2816  * being treated as "layers" in the path).
2817  */
2818 void
2819 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2821     if (!nodepath) return;
2823     if (g_list_length (nodepath->selected) == 0) {
2824         sp_nodepath_select_all (nodepath, invert);
2825         return;
2826     }
2828     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2829     GSList *subpaths = NULL;
2831     for (GList *l = copy; l != NULL; l = l->next) {
2832         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2833         Inkscape::NodePath::SubPath *subpath = n->subpath;
2834         if (!g_slist_find (subpaths, subpath))
2835             subpaths = g_slist_prepend (subpaths, subpath);
2836     }
2838     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2839         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2840         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2841             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2842             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2843         }
2844     }
2846     g_slist_free (subpaths);
2847     g_list_free (copy);
2850 /**
2851  * \brief Select the node after the last selected; if none is selected,
2852  * select the first within path.
2853  */
2854 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2856     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2858    Inkscape::NodePath::Node *last = NULL;
2859     if (nodepath->selected) {
2860         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2861            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2862             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2863             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2864                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2865                 if (node->selected) {
2866                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2867                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2868                             if (spl->next) { // there's a next subpath
2869                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2870                                 last = subpath_next->first;
2871                             } else if (spl->prev) { // there's a previous subpath
2872                                 last = NULL; // to be set later to the first node of first subpath
2873                             } else {
2874                                 last = node->n.other;
2875                             }
2876                         } else {
2877                             last = node->n.other;
2878                         }
2879                     } else {
2880                         if (node->n.other) {
2881                             last = node->n.other;
2882                         } else {
2883                             if (spl->next) { // there's a next subpath
2884                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2885                                 last = subpath_next->first;
2886                             } else if (spl->prev) { // there's a previous subpath
2887                                 last = NULL; // to be set later to the first node of first subpath
2888                             } else {
2889                                 last = (Inkscape::NodePath::Node *) subpath->first;
2890                             }
2891                         }
2892                     }
2893                 }
2894             }
2895         }
2896         sp_nodepath_deselect(nodepath);
2897     }
2899     if (last) { // there's at least one more node after selected
2900         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2901     } else { // no more nodes, select the first one in first subpath
2902        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2903         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2904     }
2907 /**
2908  * \brief Select the node before the first selected; if none is selected,
2909  * select the last within path
2910  */
2911 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2913     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2915    Inkscape::NodePath::Node *last = NULL;
2916     if (nodepath->selected) {
2917         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2918            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2919             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2920                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2921                 if (node->selected) {
2922                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2923                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2924                             if (spl->prev) { // there's a prev subpath
2925                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2926                                 last = subpath_prev->last;
2927                             } else if (spl->next) { // there's a next subpath
2928                                 last = NULL; // to be set later to the last node of last subpath
2929                             } else {
2930                                 last = node->p.other;
2931                             }
2932                         } else {
2933                             last = node->p.other;
2934                         }
2935                     } else {
2936                         if (node->p.other) {
2937                             last = node->p.other;
2938                         } else {
2939                             if (spl->prev) { // there's a prev subpath
2940                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2941                                 last = subpath_prev->last;
2942                             } else if (spl->next) { // there's a next subpath
2943                                 last = NULL; // to be set later to the last node of last subpath
2944                             } else {
2945                                 last = (Inkscape::NodePath::Node *) subpath->last;
2946                             }
2947                         }
2948                     }
2949                 }
2950             }
2951         }
2952         sp_nodepath_deselect(nodepath);
2953     }
2955     if (last) { // there's at least one more node before selected
2956         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2957     } else { // no more nodes, select the last one in last subpath
2958         GList *spl = g_list_last(nodepath->subpaths);
2959        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2960         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2961     }
2964 /**
2965  * \brief Select all nodes that are within the rectangle.
2966  */
2967 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2969     if (!incremental) {
2970         sp_nodepath_deselect(nodepath);
2971     }
2973     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2974        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2975         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2976            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2978             if (b.contains(node->pos)) {
2979                 sp_nodepath_node_select(node, TRUE, TRUE);
2980             }
2981         }
2982     }
2986 void
2987 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2989     g_assert (n);
2990     g_assert (nodepath);
2991     g_assert (n->subpath->nodepath == nodepath);
2993     if (g_list_length (nodepath->selected) == 0) {
2994         if (grow > 0) {
2995             sp_nodepath_node_select(n, TRUE, TRUE);
2996         }
2997         return;
2998     }
3000     if (g_list_length (nodepath->selected) == 1) {
3001         if (grow < 0) {
3002             sp_nodepath_deselect (nodepath);
3003             return;
3004         }
3005     }
3007         double n_sel_range = 0, p_sel_range = 0;
3008             Inkscape::NodePath::Node *farthest_n_node = n;
3009             Inkscape::NodePath::Node *farthest_p_node = n;
3011         // Calculate ranges
3012         {
3013             double n_range = 0, p_range = 0;
3014             bool n_going = true, p_going = true;
3015             Inkscape::NodePath::Node *n_node = n;
3016             Inkscape::NodePath::Node *p_node = n;
3017             do {
3018                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3019                 if (n_node && n_going)
3020                     n_node = n_node->n.other;
3021                 if (n_node == NULL) {
3022                     n_going = false;
3023                 } else {
3024                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3025                     if (n_node->selected) {
3026                         n_sel_range = n_range;
3027                         farthest_n_node = n_node;
3028                     }
3029                     if (n_node == p_node) {
3030                         n_going = false;
3031                         p_going = false;
3032                     }
3033                 }
3034                 if (p_node && p_going)
3035                     p_node = p_node->p.other;
3036                 if (p_node == NULL) {
3037                     p_going = false;
3038                 } else {
3039                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3040                     if (p_node->selected) {
3041                         p_sel_range = p_range;
3042                         farthest_p_node = p_node;
3043                     }
3044                     if (p_node == n_node) {
3045                         n_going = false;
3046                         p_going = false;
3047                     }
3048                 }
3049             } while (n_going || p_going);
3050         }
3052     if (grow > 0) {
3053         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3054                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3055         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3056                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3057         }
3058     } else {
3059         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3060                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3061         } else if (farthest_p_node && farthest_p_node->selected) {
3062                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3063         }
3064     }
3067 void
3068 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3070     g_assert (n);
3071     g_assert (nodepath);
3072     g_assert (n->subpath->nodepath == nodepath);
3074     if (g_list_length (nodepath->selected) == 0) {
3075         if (grow > 0) {
3076             sp_nodepath_node_select(n, TRUE, TRUE);
3077         }
3078         return;
3079     }
3081     if (g_list_length (nodepath->selected) == 1) {
3082         if (grow < 0) {
3083             sp_nodepath_deselect (nodepath);
3084             return;
3085         }
3086     }
3088     Inkscape::NodePath::Node *farthest_selected = NULL;
3089     double farthest_dist = 0;
3091     Inkscape::NodePath::Node *closest_unselected = NULL;
3092     double closest_dist = NR_HUGE;
3094     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3095        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3096         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3097            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3098            if (node == n)
3099                continue;
3100            if (node->selected) {
3101                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3102                    farthest_dist = NR::L2(node->pos - n->pos);
3103                    farthest_selected = node;
3104                }
3105            } else {
3106                if (NR::L2(node->pos - n->pos) < closest_dist) {
3107                    closest_dist = NR::L2(node->pos - n->pos);
3108                    closest_unselected = node;
3109                }
3110            }
3111         }
3112     }
3114     if (grow > 0) {
3115         if (closest_unselected) {
3116             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3117         }
3118     } else {
3119         if (farthest_selected) {
3120             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3121         }
3122     }
3126 /**
3127 \brief  Saves all nodes' and handles' current positions in their origin members
3128 */
3129 void
3130 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3132     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3133        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3134         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3135            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3136            n->origin = n->pos;
3137            n->p.origin = n->p.pos;
3138            n->n.origin = n->n.pos;
3139         }
3140     }
3143 /**
3144 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3145 */
3146 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3148     if (!nodepath->selected) {
3149         return NULL;
3150     }
3152     GList *r = NULL;
3153     guint i = 0;
3154     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3155        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3156         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3157            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3158             i++;
3159             if (node->selected) {
3160                 r = g_list_append(r, GINT_TO_POINTER(i));
3161             }
3162         }
3163     }
3164     return r;
3167 /**
3168 \brief  Restores selection by selecting nodes whose positions are in the list
3169 */
3170 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3172     sp_nodepath_deselect(nodepath);
3174     guint i = 0;
3175     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3176        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3177         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3178            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3179             i++;
3180             if (g_list_find(r, GINT_TO_POINTER(i))) {
3181                 sp_nodepath_node_select(node, TRUE, TRUE);
3182             }
3183         }
3184     }
3188 /**
3189 \brief Adjusts handle according to node type and line code.
3190 */
3191 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3193     g_assert(node);
3195    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3196    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3198    // nothing to do if we are an end node
3199     if (me->other == NULL) return;
3200     if (other->other == NULL) return;
3202     // nothing to do if we are a cusp node
3203     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3205     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3206     NRPathcode mecode;
3207     if (which_adjust == 1) {
3208         mecode = (NRPathcode)me->other->code;
3209     } else {
3210         mecode = (NRPathcode)node->code;
3211     }
3212     if (mecode == NR_LINETO) return;
3214     if (sp_node_side_is_line(node, other)) {
3215         // other is a line, and we are either smooth or symm
3216        Inkscape::NodePath::Node *othernode = other->other;
3217         double len = NR::L2(me->pos - node->pos);
3218         NR::Point delta = node->pos - othernode->pos;
3219         double linelen = NR::L2(delta);
3220         if (linelen < 1e-18)
3221             return;
3222         me->pos = node->pos + (len / linelen)*delta;
3223         return;
3224     }
3226     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3227         // symmetrize 
3228         me->pos = 2 * node->pos - other->pos;
3229         return;
3230     } else {
3231         // smoothify
3232         double len = NR::L2(me->pos - node->pos);
3233         NR::Point delta = other->pos - node->pos;
3234         double otherlen = NR::L2(delta);
3235         if (otherlen < 1e-18) return;
3236         me->pos = node->pos - (len / otherlen) * delta;
3237     }
3240 /**
3241  \brief Adjusts both handles according to node type and line code
3242  */
3243 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3245     g_assert(node);
3247     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3249     /* we are either smooth or symm */
3251     if (node->p.other == NULL) return;
3252     if (node->n.other == NULL) return;
3254     if (sp_node_side_is_line(node, &node->p)) {
3255         sp_node_adjust_handle(node, 1);
3256         return;
3257     }
3259     if (sp_node_side_is_line(node, &node->n)) {
3260         sp_node_adjust_handle(node, -1);
3261         return;
3262     }
3264     /* both are curves */
3265     NR::Point const delta( node->n.pos - node->p.pos );
3267     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3268         node->p.pos = node->pos - delta / 2;
3269         node->n.pos = node->pos + delta / 2;
3270         return;
3271     }
3273     /* We are smooth */
3274     double plen = NR::L2(node->p.pos - node->pos);
3275     if (plen < 1e-18) return;
3276     double nlen = NR::L2(node->n.pos - node->pos);
3277     if (nlen < 1e-18) return;
3278     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3279     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3282 /**
3283  * Node event callback.
3284  */
3285 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3287     gboolean ret = FALSE;
3288     switch (event->type) {
3289         case GDK_ENTER_NOTIFY:
3290             Inkscape::NodePath::Path::active_node = n;
3291             break;
3292         case GDK_LEAVE_NOTIFY:
3293             Inkscape::NodePath::Path::active_node = NULL;
3294             break;
3295         case GDK_SCROLL:
3296             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3297                 switch (event->scroll.direction) {
3298                     case GDK_SCROLL_UP:
3299                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3300                         break;
3301                     case GDK_SCROLL_DOWN:
3302                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3303                         break;
3304                     default:
3305                         break;
3306                 }
3307                 ret = TRUE;
3308             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3309                 switch (event->scroll.direction) {
3310                     case GDK_SCROLL_UP:
3311                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3312                         break;
3313                     case GDK_SCROLL_DOWN:
3314                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3315                         break;
3316                     default:
3317                         break;
3318                 }
3319                 ret = TRUE;
3320             }
3321             break;
3322         case GDK_KEY_PRESS:
3323             switch (get_group0_keyval (&event->key)) {
3324                 case GDK_space:
3325                     if (event->key.state & GDK_BUTTON1_MASK) {
3326                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3327                         stamp_repr(nodepath);
3328                         ret = TRUE;
3329                     }
3330                     break;
3331                 case GDK_Page_Up:
3332                     if (event->key.state & GDK_CONTROL_MASK) {
3333                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3334                     } else {
3335                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3336                     }
3337                     break;
3338                 case GDK_Page_Down:
3339                     if (event->key.state & GDK_CONTROL_MASK) {
3340                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3341                     } else {
3342                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3343                     }
3344                     break;
3345                 default:
3346                     break;
3347             }
3348             break;
3349         default:
3350             break;
3351     }
3353     return ret;
3356 /**
3357  * Handle keypress on node; directly called.
3358  */
3359 gboolean node_key(GdkEvent *event)
3361     Inkscape::NodePath::Path *np;
3363     // there is no way to verify nodes so set active_node to nil when deleting!!
3364     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3366     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3367         gint ret = FALSE;
3368         switch (get_group0_keyval (&event->key)) {
3369             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3370             case GDK_BackSpace:
3371                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3372                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3373                 sp_nodepath_update_repr(np, _("Delete node"));
3374                 Inkscape::NodePath::Path::active_node = NULL;
3375                 ret = TRUE;
3376                 break;
3377             case GDK_c:
3378                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3379                 ret = TRUE;
3380                 break;
3381             case GDK_s:
3382                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3383                 ret = TRUE;
3384                 break;
3385             case GDK_y:
3386                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3387                 ret = TRUE;
3388                 break;
3389             case GDK_b:
3390                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3391                 ret = TRUE;
3392                 break;
3393         }
3394         return ret;
3395     }
3396     return FALSE;
3399 /**
3400  * Mouseclick on node callback.
3401  */
3402 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3404    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3406     if (state & GDK_CONTROL_MASK) {
3407         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3409         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3410             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3411                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3412             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3413                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3414             } else {
3415                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3416             }
3417             sp_nodepath_update_repr(nodepath, _("Change node type"));
3418             sp_nodepath_update_statusbar(nodepath);
3420         } else { //ctrl+alt+click: delete node
3421             GList *node_to_delete = NULL;
3422             node_to_delete = g_list_append(node_to_delete, n);
3423             sp_node_delete_preserve(node_to_delete);
3424         }
3426     } else {
3427         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3428     }
3431 /**
3432  * Mouse grabbed node callback.
3433  */
3434 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3436    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3438     if (!n->selected) {
3439         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3440     }
3442     n->is_dragging = true;
3443     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3445     sp_nodepath_remember_origins (n->subpath->nodepath);
3448 /**
3449  * Mouse ungrabbed node callback.
3450  */
3451 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3453    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3455    n->dragging_out = NULL;
3456    n->is_dragging = false;
3457    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3459    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3462 /**
3463  * The point on a line, given by its angle, closest to the given point.
3464  * \param p  A point.
3465  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3466  * \param closest  Pointer to the point struct where the result is stored.
3467  * \todo FIXME: use dot product perhaps?
3468  */
3469 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3471     if (a == HUGE_VAL) { // vertical
3472         *closest = NR::Point(0, (*p)[NR::Y]);
3473     } else {
3474         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3475         (*closest)[NR::Y] = a * (*closest)[NR::X];
3476     }
3479 /**
3480  * Distance from the point to a line given by its angle.
3481  * \param p  A point.
3482  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3483  */
3484 static double point_line_distance(NR::Point *p, double a)
3486     NR::Point c;
3487     point_line_closest(p, a, &c);
3488     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]));
3491 /**
3492  * Callback for node "request" signal.
3493  * \todo fixme: This goes to "moved" event? (lauris)
3494  */
3495 static gboolean
3496 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3498     double yn, xn, yp, xp;
3499     double an, ap, na, pa;
3500     double d_an, d_ap, d_na, d_pa;
3501     gboolean collinear = FALSE;
3502     NR::Point c;
3503     NR::Point pr;
3505     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3507     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3509     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3510     if ( (!n->subpath->nodepath->straight_path) &&
3511          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3512            || n->dragging_out ) )
3513     {
3514        NR::Point mouse = (*p);
3516        if (!n->dragging_out) {
3517            // This is the first drag-out event; find out which handle to drag out
3518            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3519            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3521            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3522                return FALSE;
3524            Inkscape::NodePath::NodeSide *opposite;
3525            if (appr_p > appr_n) { // closer to p
3526                n->dragging_out = &n->p;
3527                opposite = &n->n;
3528                n->code = NR_CURVETO;
3529            } else if (appr_p < appr_n) { // closer to n
3530                n->dragging_out = &n->n;
3531                opposite = &n->p;
3532                n->n.other->code = NR_CURVETO;
3533            } else { // p and n nodes are the same
3534                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3535                    n->dragging_out = &n->p;
3536                    opposite = &n->n;
3537                    n->code = NR_CURVETO;
3538                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3539                    n->dragging_out = &n->n;
3540                    opposite = &n->p;
3541                    n->n.other->code = NR_CURVETO;
3542                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3543                    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);
3544                    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);
3545                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3546                        n->dragging_out = &n->n;
3547                        opposite = &n->p;
3548                        n->n.other->code = NR_CURVETO;
3549                    } else { // closer to other's n handle
3550                        n->dragging_out = &n->p;
3551                        opposite = &n->n;
3552                        n->code = NR_CURVETO;
3553                    }
3554                }
3555            }
3557            // if there's another handle, make sure the one we drag out starts parallel to it
3558            if (opposite->pos != n->pos) {
3559                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3560            }
3562            // knots might not be created yet!
3563            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3564            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3565        }
3567        // pass this on to the handle-moved callback
3568        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3569        sp_node_update_handles(n);
3570        return TRUE;
3571    }
3573     if (state & GDK_CONTROL_MASK) { // constrained motion
3575         // calculate relative distances of handles
3576         // n handle:
3577         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3578         xn = n->n.pos[NR::X] - n->pos[NR::X];
3579         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3580         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3581             if (n->n.other) { // if there is the next point
3582                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3583                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3584                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3585             }
3586         }
3587         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3588         if (yn < 0) { xn = -xn; yn = -yn; }
3590         // p handle:
3591         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3592         xp = n->p.pos[NR::X] - n->pos[NR::X];
3593         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3594         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3595             if (n->p.other) {
3596                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3597                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3598                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3599             }
3600         }
3601         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3602         if (yp < 0) { xp = -xp; yp = -yp; }
3604         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3605             // sliding on handles, only if at least one of the handles is non-vertical
3606             // (otherwise it's the same as ctrl+drag anyway)
3608             // calculate angles of the handles
3609             if (xn == 0) {
3610                 if (yn == 0) { // no handle, consider it the continuation of the other one
3611                     an = 0;
3612                     collinear = TRUE;
3613                 }
3614                 else an = 0; // vertical; set the angle to horizontal
3615             } else an = yn/xn;
3617             if (xp == 0) {
3618                 if (yp == 0) { // no handle, consider it the continuation of the other one
3619                     ap = an;
3620                 }
3621                 else ap = 0; // vertical; set the angle to horizontal
3622             } else  ap = yp/xp;
3624             if (collinear) an = ap;
3626             // angles of the perpendiculars; HUGE_VAL means vertical
3627             if (an == 0) na = HUGE_VAL; else na = -1/an;
3628             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3630             // mouse point relative to the node's original pos
3631             pr = (*p) - n->origin;
3633             // distances to the four lines (two handles and two perpendiculars)
3634             d_an = point_line_distance(&pr, an);
3635             d_na = point_line_distance(&pr, na);
3636             d_ap = point_line_distance(&pr, ap);
3637             d_pa = point_line_distance(&pr, pa);
3639             // find out which line is the closest, save its closest point in c
3640             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3641                 point_line_closest(&pr, an, &c);
3642             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3643                 point_line_closest(&pr, ap, &c);
3644             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3645                 point_line_closest(&pr, na, &c);
3646             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3647                 point_line_closest(&pr, pa, &c);
3648             }
3650             // move the node to the closest point
3651             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3652                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3653                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3654                                             true);
3656         } else {  // constraining to hor/vert
3658             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3659                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3660                                                 (*p)[NR::X] - n->pos[NR::X], 
3661                                                 n->origin[NR::Y] - n->pos[NR::Y],
3662                                                 true, 
3663                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3664             } else { // snap to vert
3665                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3666                                                 n->origin[NR::X] - n->pos[NR::X],
3667                                                 (*p)[NR::Y] - n->pos[NR::Y],
3668                                                 true,
3669                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3670             }
3671         }
3672     } else { // move freely
3673         if (n->is_dragging) {
3674             if (state & GDK_MOD1_MASK) { // sculpt
3675                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3676             } else {
3677                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3678                                             (*p)[NR::X] - n->pos[NR::X],
3679                                             (*p)[NR::Y] - n->pos[NR::Y],
3680                                             (state & GDK_SHIFT_MASK) == 0);
3681             }
3682         }
3683     }
3685     n->subpath->nodepath->desktop->scroll_to_point(p);
3687     return TRUE;
3690 /**
3691  * Node handle clicked callback.
3692  */
3693 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3695    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3697     if (state & GDK_CONTROL_MASK) { // "delete" handle
3698         if (n->p.knot == knot) {
3699             n->p.pos = n->pos;
3700         } else if (n->n.knot == knot) {
3701             n->n.pos = n->pos;
3702         }
3703         sp_node_update_handles(n);
3704         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3705         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3706         sp_nodepath_update_statusbar(nodepath);
3708     } else { // just select or add to selection, depending in Shift
3709         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3710     }
3713 /**
3714  * Node handle grabbed callback.
3715  */
3716 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3718    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3720     if (!n->selected) {
3721         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3722     }
3724     // remember the origin point of the handle
3725     if (n->p.knot == knot) {
3726         n->p.origin_radial = n->p.pos - n->pos;
3727     } else if (n->n.knot == knot) {
3728         n->n.origin_radial = n->n.pos - n->pos;
3729     } else {
3730         g_assert_not_reached();
3731     }
3733     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3736 /**
3737  * Node handle ungrabbed callback.
3738  */
3739 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3741    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3743     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3744     if (n->p.knot == knot) {
3745         n->p.origin_radial.a = 0;
3746         sp_knot_set_position(knot, n->p.pos, state);
3747     } else if (n->n.knot == knot) {
3748         n->n.origin_radial.a = 0;
3749         sp_knot_set_position(knot, n->n.pos, state);
3750     } else {
3751         g_assert_not_reached();
3752     }
3754     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3757 /**
3758  * Node handle "request" signal callback.
3759  */
3760 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3762     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3764     Inkscape::NodePath::NodeSide *me, *opposite;
3765     gint which;
3766     if (n->p.knot == knot) {
3767         me = &n->p;
3768         opposite = &n->n;
3769         which = -1;
3770     } else if (n->n.knot == knot) {
3771         me = &n->n;
3772         opposite = &n->p;
3773         which = 1;
3774     } else {
3775         me = opposite = NULL;
3776         which = 0;
3777         g_assert_not_reached();
3778     }
3780     SPDesktop *desktop = n->subpath->nodepath->desktop;
3781     SnapManager &m = desktop->namedview->snap_manager;
3782     m.setup(desktop, n->subpath->nodepath->item);
3783     Inkscape::SnappedPoint s;
3784     
3785     if ((state & GDK_SHIFT_MASK) != 0) {
3786         // We will not try to snap when the shift-key is pressed
3787         // so remove the old snap indicator and don't wait for it to time-out  
3788         desktop->snapindicator->remove_snappoint();     
3789     }
3791     Inkscape::NodePath::Node *othernode = opposite->other;
3792     if (othernode) {
3793         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3794             /* We are smooth node adjacent with line */
3795             NR::Point const delta = *p - n->pos;
3796             NR::Coord const len = NR::L2(delta);
3797             Inkscape::NodePath::Node *othernode = opposite->other;
3798             NR::Point const ndelta = n->pos - othernode->pos;
3799             NR::Coord const linelen = NR::L2(ndelta);
3800             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3801                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3802                 (*p) = n->pos + (scal / linelen) * ndelta;
3803             }
3804             if ((state & GDK_SHIFT_MASK) == 0) {
3805                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3806             }
3807         } else {
3808                 if ((state & GDK_SHIFT_MASK) == 0) {
3809                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3810                 }
3811         }
3812     } else {
3813         if ((state & GDK_SHIFT_MASK) == 0) {
3814                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3815         }
3816     }
3817     
3818     s.getPoint(*p);
3819     
3820     sp_node_adjust_handle(n, -which);
3822     return FALSE;
3825 /**
3826  * Node handle moved callback.
3827  */
3828 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3830    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3832    Inkscape::NodePath::NodeSide *me;
3833    Inkscape::NodePath::NodeSide *other;
3834     if (n->p.knot == knot) {
3835         me = &n->p;
3836         other = &n->n;
3837     } else if (n->n.knot == knot) {
3838         me = &n->n;
3839         other = &n->p;
3840     } else {
3841         me = NULL;
3842         other = NULL;
3843         g_assert_not_reached();
3844     }
3846     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3847     Radial rme(me->pos - n->pos);
3848     Radial rother(other->pos - n->pos);
3849     Radial rnew(*p - n->pos);
3851     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3852         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3853         /* 0 interpreted as "no snapping". */
3855         // 1. Snap to the closest PI/snaps angle, starting from zero.
3856         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3858         // 2. Snap to the original angle, its opposite and perpendiculars
3859         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3860             /* The closest PI/2 angle, starting from original angle */
3861             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3863             // Snap to the closest.
3864             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3865                        ? a_snapped
3866                        : a_ortho );
3867         }
3869         // 3. Snap to the angle of the opposite line, if any
3870         Inkscape::NodePath::Node *othernode = other->other;
3871         if (othernode) {
3872             NR::Point other_to_snap(0,0);
3873             if (sp_node_side_is_line(n, other)) {
3874                 other_to_snap = othernode->pos - n->pos;
3875             } else {
3876                 other_to_snap = other->pos - n->pos;
3877             }
3878             if (NR::L2(other_to_snap) > 1e-3) {
3879                 Radial rother_to_snap(other_to_snap);
3880                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3881                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3883                 // Snap to the closest.
3884                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3885                        ? a_snapped
3886                        : a_oppo );
3887             }
3888         }
3890         rnew.a = a_snapped;
3891     }
3893     if (state & GDK_MOD1_MASK) {
3894         // lock handle length
3895         rnew.r = me->origin_radial.r;
3896     }
3898     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3899         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3900         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3901         rother.a += rnew.a - rme.a;
3902         other->pos = NR::Point(rother) + n->pos;
3903         if (other->knot) {
3904             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3905             sp_knot_moveto(other->knot, other->pos);
3906         }
3907     }
3909     me->pos = NR::Point(rnew) + n->pos;
3910     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3912     // move knot, but without emitting the signal:
3913     // we cannot emit a "moved" signal because we're now processing it
3914     sp_knot_moveto(me->knot, me->pos);
3916     update_object(n->subpath->nodepath);
3918     /* status text */
3919     SPDesktop *desktop = n->subpath->nodepath->desktop;
3920     if (!desktop) return;
3921     SPEventContext *ec = desktop->event_context;
3922     if (!ec) return;
3923     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3924     if (!mc) return;
3926     double degrees = 180 / M_PI * rnew.a;
3927     if (degrees > 180) degrees -= 360;
3928     if (degrees < -180) degrees += 360;
3929     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3930         degrees = angle_to_compass (degrees);
3932     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3934     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3935          _("<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);
3937     g_string_free(length, TRUE);
3940 /**
3941  * Node handle event callback.
3942  */
3943 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3945     gboolean ret = FALSE;
3946     switch (event->type) {
3947         case GDK_KEY_PRESS:
3948             switch (get_group0_keyval (&event->key)) {
3949                 case GDK_space:
3950                     if (event->key.state & GDK_BUTTON1_MASK) {
3951                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3952                         stamp_repr(nodepath);
3953                         ret = TRUE;
3954                     }
3955                     break;
3956                 default:
3957                     break;
3958             }
3959             break;
3960         case GDK_ENTER_NOTIFY:
3961             // we use an experimentally determined threshold that seems to work fine
3962             if (NR::L2(n->pos - knot->pos) < 0.75)
3963                 Inkscape::NodePath::Path::active_node = n;
3964             break;
3965         case GDK_LEAVE_NOTIFY:
3966             // we use an experimentally determined threshold that seems to work fine
3967             if (NR::L2(n->pos - knot->pos) < 0.75)
3968                 Inkscape::NodePath::Path::active_node = NULL;
3969             break;
3970         default:
3971             break;
3972     }
3974     return ret;
3977 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3978                                  Radial &rme, Radial &rother, gboolean const both)
3980     rme.a += angle;
3981     if ( both
3982          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3983          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3984     {
3985         rother.a += angle;
3986     }
3989 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3990                                         Radial &rme, Radial &rother, gboolean const both)
3992     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3994     gdouble r;
3995     if ( both
3996          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3997          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3998     {
3999         r = MAX(rme.r, rother.r);
4000     } else {
4001         r = rme.r;
4002     }
4004     gdouble const weird_angle = atan2(norm_angle, r);
4005 /* Bulia says norm_angle is just the visible distance that the
4006  * object's end must travel on the screen.  Left as 'angle' for want of
4007  * a better name.*/
4009     rme.a += weird_angle;
4010     if ( both
4011          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4012          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4013     {
4014         rother.a += weird_angle;
4015     }
4018 /**
4019  * Rotate one node.
4020  */
4021 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4023     Inkscape::NodePath::NodeSide *me, *other;
4024     bool both = false;
4026     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4027     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4029     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4030         me = &(n->p);
4031         other = &(n->n);
4032     } else if (!n->p.other) {
4033         me = &(n->n);
4034         other = &(n->p);
4035     } else {
4036         if (which > 0) { // right handle
4037             if (xn > xp) {
4038                 me = &(n->n);
4039                 other = &(n->p);
4040             } else {
4041                 me = &(n->p);
4042                 other = &(n->n);
4043             }
4044         } else if (which < 0){ // left handle
4045             if (xn <= xp) {
4046                 me = &(n->n);
4047                 other = &(n->p);
4048             } else {
4049                 me = &(n->p);
4050                 other = &(n->n);
4051             }
4052         } else { // both handles
4053             me = &(n->n);
4054             other = &(n->p);
4055             both = true;
4056         }
4057     }
4059     Radial rme(me->pos - n->pos);
4060     Radial rother(other->pos - n->pos);
4062     if (screen) {
4063         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4064     } else {
4065         node_rotate_one_internal (*n, angle, rme, rother, both);
4066     }
4068     me->pos = n->pos + NR::Point(rme);
4070     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4071         other->pos =  n->pos + NR::Point(rother);
4072     }
4074     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4075     // so here we just move all the knots without emitting move signals, for speed
4076     sp_node_update_handles(n, false);
4079 /**
4080  * Rotate selected nodes.
4081  */
4082 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4084     if (!nodepath || !nodepath->selected) return;
4086     if (g_list_length(nodepath->selected) == 1) {
4087        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4088         node_rotate_one (n, angle, which, screen);
4089     } else {
4090        // rotate as an object:
4092         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4093         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4094         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4095             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4096             box.expandTo (n->pos); // contain all selected nodes
4097         }
4099         gdouble rot;
4100         if (screen) {
4101             gdouble const zoom = nodepath->desktop->current_zoom();
4102             gdouble const zmove = angle / zoom;
4103             gdouble const r = NR::L2(box.max() - box.midpoint());
4104             rot = atan2(zmove, r);
4105         } else {
4106             rot = angle;
4107         }
4109         NR::Point rot_center;
4110         if (Inkscape::NodePath::Path::active_node == NULL)
4111             rot_center = box.midpoint();
4112         else
4113             rot_center = Inkscape::NodePath::Path::active_node->pos;
4115         NR::Matrix t =
4116             NR::Matrix (NR::translate(-rot_center)) *
4117             NR::Matrix (NR::rotate(rot)) *
4118             NR::Matrix (NR::translate(rot_center));
4120         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4121             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4122             n->pos *= t;
4123             n->n.pos *= t;
4124             n->p.pos *= t;
4125             sp_node_update_handles(n, false);
4126         }
4127     }
4129     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4132 /**
4133  * Scale one node.
4134  */
4135 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4137     bool both = false;
4138     Inkscape::NodePath::NodeSide *me, *other;
4140     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4141     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4143     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4144         me = &(n->p);
4145         other = &(n->n);
4146         n->code = NR_CURVETO;
4147     } else if (!n->p.other) {
4148         me = &(n->n);
4149         other = &(n->p);
4150         if (n->n.other)
4151             n->n.other->code = NR_CURVETO;
4152     } else {
4153         if (which > 0) { // right handle
4154             if (xn > xp) {
4155                 me = &(n->n);
4156                 other = &(n->p);
4157                 if (n->n.other)
4158                     n->n.other->code = NR_CURVETO;
4159             } else {
4160                 me = &(n->p);
4161                 other = &(n->n);
4162                 n->code = NR_CURVETO;
4163             }
4164         } else if (which < 0){ // left handle
4165             if (xn <= xp) {
4166                 me = &(n->n);
4167                 other = &(n->p);
4168                 if (n->n.other)
4169                     n->n.other->code = NR_CURVETO;
4170             } else {
4171                 me = &(n->p);
4172                 other = &(n->n);
4173                 n->code = NR_CURVETO;
4174             }
4175         } else { // both handles
4176             me = &(n->n);
4177             other = &(n->p);
4178             both = true;
4179             n->code = NR_CURVETO;
4180             if (n->n.other)
4181                 n->n.other->code = NR_CURVETO;
4182         }
4183     }
4185     Radial rme(me->pos - n->pos);
4186     Radial rother(other->pos - n->pos);
4188     rme.r += grow;
4189     if (rme.r < 0) rme.r = 0;
4190     if (rme.a == HUGE_VAL) {
4191         if (me->other) { // if direction is unknown, initialize it towards the next node
4192             Radial rme_next(me->other->pos - n->pos);
4193             rme.a = rme_next.a;
4194         } else { // if there's no next, initialize to 0
4195             rme.a = 0;
4196         }
4197     }
4198     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4199         rother.r += grow;
4200         if (rother.r < 0) rother.r = 0;
4201         if (rother.a == HUGE_VAL) {
4202             rother.a = rme.a + M_PI;
4203         }
4204     }
4206     me->pos = n->pos + NR::Point(rme);
4208     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4209         other->pos = n->pos + NR::Point(rother);
4210     }
4212     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4213     // so here we just move all the knots without emitting move signals, for speed
4214     sp_node_update_handles(n, false);
4217 /**
4218  * Scale selected nodes.
4219  */
4220 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4222     if (!nodepath || !nodepath->selected) return;
4224     if (g_list_length(nodepath->selected) == 1) {
4225         // scale handles of the single selected node
4226         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4227         node_scale_one (n, grow, which);
4228     } else {
4229         // scale nodes as an "object":
4231         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4232         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4233         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4234             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4235             box.expandTo (n->pos); // contain all selected nodes
4236         }
4238         double scale = (box.maxExtent() + grow)/box.maxExtent();
4240         NR::Point scale_center;
4241         if (Inkscape::NodePath::Path::active_node == NULL)
4242             scale_center = box.midpoint();
4243         else
4244             scale_center = Inkscape::NodePath::Path::active_node->pos;
4246         NR::Matrix t =
4247             NR::Matrix (NR::translate(-scale_center)) *
4248             NR::Matrix (NR::scale(scale, scale)) *
4249             NR::Matrix (NR::translate(scale_center));
4251         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4252             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4253             n->pos *= t;
4254             n->n.pos *= t;
4255             n->p.pos *= t;
4256             sp_node_update_handles(n, false);
4257         }
4258     }
4260     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4263 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4265     if (!nodepath) return;
4266     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4269 /**
4270  * Flip selected nodes horizontally/vertically.
4271  */
4272 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4274     if (!nodepath || !nodepath->selected) return;
4276     if (g_list_length(nodepath->selected) == 1 && !center) {
4277         // flip handles of the single selected node
4278         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4279         double temp = n->p.pos[axis];
4280         n->p.pos[axis] = n->n.pos[axis];
4281         n->n.pos[axis] = temp;
4282         sp_node_update_handles(n, false);
4283     } else {
4284         // scale nodes as an "object":
4286         NR::Rect box = sp_node_selected_bbox (nodepath);
4287         if (!center) {
4288             center = box.midpoint();
4289         }
4290         NR::Matrix t =
4291             NR::Matrix (NR::translate(- *center)) *
4292             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4293             NR::Matrix (NR::translate(*center));
4295         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4296             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4297             n->pos *= t;
4298             n->n.pos *= t;
4299             n->p.pos *= t;
4300             sp_node_update_handles(n, false);
4301         }
4302     }
4304     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4307 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4309     g_assert (nodepath->selected);
4311     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4312     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4313     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4314         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4315         box.expandTo (n->pos); // contain all selected nodes
4316     }
4317     return box;
4320 //-----------------------------------------------
4321 /**
4322  * Return new subpath under given nodepath.
4323  */
4324 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4326     g_assert(nodepath);
4327     g_assert(nodepath->desktop);
4329    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4331     s->nodepath = nodepath;
4332     s->closed = FALSE;
4333     s->nodes = NULL;
4334     s->first = NULL;
4335     s->last = NULL;
4337     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4338     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4339     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4341     return s;
4344 /**
4345  * Destroy nodes in subpath, then subpath itself.
4346  */
4347 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4349     g_assert(subpath);
4350     g_assert(subpath->nodepath);
4351     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4353     while (subpath->nodes) {
4354         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4355     }
4357     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4359     g_free(subpath);
4362 /**
4363  * Link head to tail in subpath.
4364  */
4365 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4367     g_assert(!sp->closed);
4368     g_assert(sp->last != sp->first);
4369     g_assert(sp->first->code == NR_MOVETO);
4371     sp->closed = TRUE;
4373     //Link the head to the tail
4374     sp->first->p.other = sp->last;
4375     sp->last->n.other  = sp->first;
4376     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4377     sp->first          = sp->last;
4379     //Remove the extra end node
4380     sp_nodepath_node_destroy(sp->last->n.other);
4383 /**
4384  * Open closed (loopy) subpath at node.
4385  */
4386 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4388     g_assert(sp->closed);
4389     g_assert(n->subpath == sp);
4390     g_assert(sp->first == sp->last);
4392     /* We create new startpoint, current node will become last one */
4394    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4395                                                 &n->pos, &n->pos, &n->n.pos);
4398     sp->closed        = FALSE;
4400     //Unlink to make a head and tail
4401     sp->first         = new_path;
4402     sp->last          = n;
4403     n->n.other        = NULL;
4404     new_path->p.other = NULL;
4407 /**
4408  * Return new node in subpath with given properties.
4409  * \param pos Position of node.
4410  * \param ppos Handle position in previous direction
4411  * \param npos Handle position in previous direction
4412  */
4413 Inkscape::NodePath::Node *
4414 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)
4416     g_assert(sp);
4417     g_assert(sp->nodepath);
4418     g_assert(sp->nodepath->desktop);
4420     if (nodechunk == NULL)
4421         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4423     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4425     n->subpath  = sp;
4427     if (type != Inkscape::NodePath::NODE_NONE) {
4428         // use the type from sodipodi:nodetypes
4429         n->type = type;
4430     } else {
4431         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4432             // points are (almost) collinear
4433             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4434                 // endnode, or a node with a retracted handle
4435                 n->type = Inkscape::NodePath::NODE_CUSP;
4436             } else {
4437                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4438             }
4439         } else {
4440             n->type = Inkscape::NodePath::NODE_CUSP;
4441         }
4442     }
4444     n->code     = code;
4445     n->selected = FALSE;
4446     n->pos      = *pos;
4447     n->p.pos    = *ppos;
4448     n->n.pos    = *npos;
4450     n->dragging_out = NULL;
4452     Inkscape::NodePath::Node *prev;
4453     if (next) {
4454         //g_assert(g_list_find(sp->nodes, next));
4455         prev = next->p.other;
4456     } else {
4457         prev = sp->last;
4458     }
4460     if (prev)
4461         prev->n.other = n;
4462     else
4463         sp->first = n;
4465     if (next)
4466         next->p.other = n;
4467     else
4468         sp->last = n;
4470     n->p.other = prev;
4471     n->n.other = next;
4473     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"));
4474     sp_knot_set_position(n->knot, *pos, 0);
4476     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4477     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4478     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4479     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4480     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4481     sp_knot_update_ctrl(n->knot);
4483     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4484     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4485     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4486     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4487     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4488     sp_knot_show(n->knot);
4490     // We only create handle knots and lines on demand
4491     n->p.knot = NULL;
4492     n->p.line = NULL;
4493     n->n.knot = NULL;
4494     n->n.line = NULL;
4496     sp->nodes = g_list_prepend(sp->nodes, n);
4498     return n;
4501 /**
4502  * Destroy node and its knots, link neighbors in subpath.
4503  */
4504 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4506     g_assert(node);
4507     g_assert(node->subpath);
4508     g_assert(SP_IS_KNOT(node->knot));
4510    Inkscape::NodePath::SubPath *sp = node->subpath;
4512     if (node->selected) { // first, deselect
4513         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4514         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4515     }
4517     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4519     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4520     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4521     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4522     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4523     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4524     g_object_unref(G_OBJECT(node->knot));
4526     if (node->p.knot) {
4527         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4528         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4529         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4530         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4531         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4532         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4533         g_object_unref(G_OBJECT(node->p.knot));
4534         node->p.knot = NULL;
4535     }
4537     if (node->n.knot) {
4538         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4539         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4540         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4541         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4542         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4543         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4544         g_object_unref(G_OBJECT(node->n.knot));
4545         node->n.knot = NULL;
4546     }
4548     if (node->p.line)
4549         gtk_object_destroy(GTK_OBJECT(node->p.line));
4550     if (node->n.line)
4551         gtk_object_destroy(GTK_OBJECT(node->n.line));
4553     if (sp->nodes) { // there are others nodes on the subpath
4554         if (sp->closed) {
4555             if (sp->first == node) {
4556                 g_assert(sp->last == node);
4557                 sp->first = node->n.other;
4558                 sp->last = sp->first;
4559             }
4560             node->p.other->n.other = node->n.other;
4561             node->n.other->p.other = node->p.other;
4562         } else {
4563             if (sp->first == node) {
4564                 sp->first = node->n.other;
4565                 sp->first->code = NR_MOVETO;
4566             }
4567             if (sp->last == node) sp->last = node->p.other;
4568             if (node->p.other) node->p.other->n.other = node->n.other;
4569             if (node->n.other) node->n.other->p.other = node->p.other;
4570         }
4571     } else { // this was the last node on subpath
4572         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4573     }
4575     g_mem_chunk_free(nodechunk, node);
4578 /**
4579  * Returns one of the node's two sides.
4580  * \param which Indicates which side.
4581  * \return Pointer to previous node side if which==-1, next if which==1.
4582  */
4583 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4585     g_assert(node);
4587     switch (which) {
4588         case -1:
4589             return &node->p;
4590         case 1:
4591             return &node->n;
4592         default:
4593             break;
4594     }
4596     g_assert_not_reached();
4598     return NULL;
4601 /**
4602  * Return the other side of the node, given one of its sides.
4603  */
4604 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4606     g_assert(node);
4608     if (me == &node->p) return &node->n;
4609     if (me == &node->n) return &node->p;
4611     g_assert_not_reached();
4613     return NULL;
4616 /**
4617  * Return NRPathcode on the given side of the node.
4618  */
4619 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4621     g_assert(node);
4623     if (me == &node->p) {
4624         if (node->p.other) return (NRPathcode)node->code;
4625         return NR_MOVETO;
4626     }
4628     if (me == &node->n) {
4629         if (node->n.other) return (NRPathcode)node->n.other->code;
4630         return NR_MOVETO;
4631     }
4633     g_assert_not_reached();
4635     return NR_END;
4638 /**
4639  * Return node with the given index
4640  */
4641 Inkscape::NodePath::Node *
4642 sp_nodepath_get_node_by_index(int index)
4644     Inkscape::NodePath::Node *e = NULL;
4646     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4647     if (!nodepath) {
4648         return e;
4649     }
4651     //find segment
4652     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4654         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4655         int n = g_list_length(sp->nodes);
4656         if (sp->closed) {
4657             n++;
4658         }
4660         //if the piece belongs to this subpath grab it
4661         //otherwise move onto the next subpath
4662         if (index < n) {
4663             e = sp->first;
4664             for (int i = 0; i < index; ++i) {
4665                 e = e->n.other;
4666             }
4667             break;
4668         } else {
4669             if (sp->closed) {
4670                 index -= (n+1);
4671             } else {
4672                 index -= n;
4673             }
4674         }
4675     }
4677     return e;
4680 /**
4681  * Returns plain text meaning of node type.
4682  */
4683 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4685     unsigned retracted = 0;
4686     bool endnode = false;
4688     for (int which = -1; which <= 1; which += 2) {
4689         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4690         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4691             retracted ++;
4692         if (!side->other)
4693             endnode = true;
4694     }
4696     if (retracted == 0) {
4697         if (endnode) {
4698                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4699                 return _("end node");
4700         } else {
4701             switch (node->type) {
4702                 case Inkscape::NodePath::NODE_CUSP:
4703                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4704                     return _("cusp");
4705                 case Inkscape::NodePath::NODE_SMOOTH:
4706                     // TRANSLATORS: "smooth" is an adjective here
4707                     return _("smooth");
4708                 case Inkscape::NodePath::NODE_SYMM:
4709                     return _("symmetric");
4710             }
4711         }
4712     } else if (retracted == 1) {
4713         if (endnode) {
4714             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4715             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4716         } else {
4717             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4718         }
4719     } else {
4720         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4721     }
4723     return NULL;
4726 /**
4727  * Handles content of statusbar as long as node tool is active.
4728  */
4729 void
4730 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4732     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");
4733     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4735     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4736     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4737     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4738     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4740     SPDesktop *desktop = NULL;
4741     if (nodepath) {
4742         desktop = nodepath->desktop;
4743     } else {
4744         desktop = SP_ACTIVE_DESKTOP;
4745     }
4747     SPEventContext *ec = desktop->event_context;
4748     if (!ec) return;
4749     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4750     if (!mc) return;
4752     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4754     if (selected_nodes == 0) {
4755         Inkscape::Selection *sel = desktop->selection;
4756         if (!sel || sel->isEmpty()) {
4757             mc->setF(Inkscape::NORMAL_MESSAGE,
4758                      _("Select a single object to edit its nodes or handles."));
4759         } else {
4760             if (nodepath) {
4761             mc->setF(Inkscape::NORMAL_MESSAGE,
4762                      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.",
4763                               "<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.",
4764                               total_nodes),
4765                      total_nodes);
4766             } else {
4767                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4768                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4769                 } else {
4770                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4771                 }
4772             }
4773         }
4774     } else if (nodepath && selected_nodes == 1) {
4775         mc->setF(Inkscape::NORMAL_MESSAGE,
4776                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4777                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4778                           total_nodes),
4779                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4780     } else {
4781         if (selected_subpaths > 1) {
4782             mc->setF(Inkscape::NORMAL_MESSAGE,
4783                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4784                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4785                               total_nodes),
4786                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4787         } else {
4788             mc->setF(Inkscape::NORMAL_MESSAGE,
4789                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4790                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4791                               total_nodes),
4792                      selected_nodes, total_nodes, when_selected);
4793         }
4794     }
4797 /*
4798  * returns a *copy* of the curve of that object.
4799  */
4800 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4801     if (!object)
4802         return NULL;
4804     SPCurve *curve = NULL;
4805     if (SP_IS_PATH(object)) {
4806         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4807         curve = curve_new->copy();
4808     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4809         const gchar *svgd = object->repr->attribute(key);
4810         if (svgd) {
4811             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4812             SPCurve *curve_new = new SPCurve(pv);
4813             if (curve_new) {
4814                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4815             }
4816         }
4817     }
4819     return curve;
4822 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4823     if (!np || !np->object || !curve)
4824         return;
4826     if (SP_IS_PATH(np->object)) {
4827         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4828             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4829         } else {
4830             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4831         }
4832     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4833         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4834         if (pathparam) {
4835             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4836             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4837         }
4838     }
4841 /**
4842 SPCanvasItem *
4843 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4844     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4846 **/
4848 /**
4849 SPCanvasItem *
4850 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4851     SPCurve *flash_curve = curve->copy();
4852     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4853     flash_curve->transform(i2d);
4854     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4855     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4856     // unless we also flash the nodes...
4857     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4858     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4859     sp_canvas_item_show(canvasitem);
4860     flash_curve->unref();
4861     return canvasitem;
4864 SPCanvasItem *
4865 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4866     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4867                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4869 **/
4871 SPCanvasItem *
4872 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4873     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4874     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4875     flash_curve->transform(i2d);
4876     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4877     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4878     // unless we also flash the nodes...
4879     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4880     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4881     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4882     sp_canvas_item_show(canvasitem);
4883     flash_curve->unref();
4884     return canvasitem;
4887 // TODO: Merge this with sp_nodepath_make_helper_item()!
4888 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4889     np->show_helperpath = show;
4891     if (show) {
4892         SPCurve *helper_curve = np->curve->copy();
4893         helper_curve->transform(to_2geom(np->i2d));
4894         if (!np->helper_path) {
4895             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4897             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4898             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);
4899             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4900             sp_canvas_item_move_to_z(np->helper_path, 0);
4901             sp_canvas_item_show(np->helper_path);
4902         } else {
4903             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4904         }
4905         helper_curve->unref();
4906     } else {
4907         if (np->helper_path) {
4908             GtkObject *temp = np->helper_path;
4909             np->helper_path = NULL;
4910             gtk_object_destroy(temp);
4911         }
4912     }
4915 /* sp_nodepath_make_straight_path:
4916  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4917  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4918  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4919  */
4920 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4921     np->straight_path = true;
4922     np->show_handles = false;
4923     g_message("add code to make the path straight.");
4924     // do sp_nodepath_convert_node_type on all nodes?
4925     // coding tip: search for this text : "Make selected segments lines"
4928 /* convert all curve types to LineSegment or CubicBezier, because nodepath cannot handle other segment types */
4929 Geom::PathVector sp_nodepath_sanitize_path(Geom::PathVector const &pathv_in) {
4930     Geom::PathVector pathv;
4932     for (Geom::PathVector::const_iterator pit = pathv_in.begin(); pit != pathv_in.end(); ++pit) {
4933         pathv.push_back( Geom::Path() );
4934         Geom::Path &newpath = pathv.back();
4935         newpath.start(pit->initialPoint());
4936         newpath.close(pit->closed());
4938         for (Geom::Path::const_iterator c = pit->begin(); c != pit->end_open(); ++c) {
4939             if( dynamic_cast<Geom::CubicBezier const*>(&*c) ||
4940                 dynamic_cast<Geom::LineSegment const*>(&*c) ||
4941                 dynamic_cast<Geom::HLineSegment const*>(&*c) ||
4942                 dynamic_cast<Geom::VLineSegment const*>(&*c) )
4943             {
4944                 newpath.append(*c);
4945             }
4946             else {
4947                 //this case handles sbasis as well as all other curve types
4948                 Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c->toSBasis(), 0.1);
4949                 newpath.append(sbasis_path);
4950             }
4951         }
4952     }
4954     return pathv;
4957 /*
4958   Local Variables:
4959   mode:c++
4960   c-file-style:"stroustrup"
4961   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4962   indent-tabs-mode:nil
4963   fill-column:99
4964   End:
4965 */
4966 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :