Code

convert path to only linear and cubic segments when nodeediting. prevents crashing...
[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 void add_curve_to_subpath( Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c,
104                                   Inkscape::NodePath::NodeType const *t, guint & i, NR::Point & ppos, NRPathcode & pcode  );
105 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
106 Geom::PathVector sp_nodepath_sanitize_path(Geom::PathVector const &pathv_in);
108 /* Object updating */
110 static void stamp_repr(Inkscape::NodePath::Path *np);
111 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
112 static gchar *create_typestr(Inkscape::NodePath::Path *np);
114 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
116 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
118 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
120 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
122 /* Adjust handle placement, if the node or the other handle is moved */
123 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
124 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
126 /* Node event callbacks */
127 static void node_clicked(SPKnot *knot, guint state, gpointer data);
128 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
129 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
130 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
132 /* Handle event callbacks */
133 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
135 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
136 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
137 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
138 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
140 /* Constructors and destructors */
142 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
143 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
144 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
145 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
146 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
147                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
148 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
150 /* Helpers */
152 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
153 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
154 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
156 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
157 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
159 // active_node indicates mouseover node
160 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
162 static SPCanvasItem *
163 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
164     SPCurve *helper_curve = curve->copy();
165     helper_curve->transform(to_2geom(np->i2d));
166     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
167     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
168     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
169     sp_canvas_item_move_to_z(helper_path, 0);
170     if (show) {
171         sp_canvas_item_show(helper_path);
172     }
173     helper_curve->unref();
174     return helper_path;
177 static SPCanvasItem *
178 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
179     SPCurve *helper_curve = new SPCurve(pathv);
180     return sp_nodepath_make_helper_item(np, helper_curve, show);
183 static void
184 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
185     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
186     if (!SP_IS_LPE_ITEM(np->item)) {
187         g_print ("Only LPEItems can have helperpaths!\n");
188         return;
189     }
191     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
192     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
193     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
194         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
195         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
196         // create new canvas items from the effect's helper paths
197         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
198         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
199             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
200         }
201     }
204 void
205 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
206     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
207     if (!SP_IS_LPE_ITEM(np->item)) {
208         g_print ("Only LPEItems can have helperpaths!\n");
209         return;
210     }
212     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
213     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
214     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
215         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
216         /* update canvas items from the effect's helper paths; note that this code relies on the
217          * fact that getHelperPaths() will always return the same number of helperpaths in the same
218          * order as during their creation in sp_nodepath_create_helperpaths
219          */
220         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
221         for (unsigned int j = 0; j < hpaths.size(); ++j) {
222             SPCurve *curve = new SPCurve(hpaths[j]);
223             curve->transform(to_2geom(np->i2d));
224             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
225             curve = curve->unref();
226         }
227     }
230 static void
231 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
232     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
233         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
234             GtkObject *temp = *j;
235             *j = NULL;
236             gtk_object_destroy(temp);
237         }
238     }
242 /**
243  * \brief Creates new nodepath from item
244  */
245 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
247     Inkscape::XML::Node *repr = object->repr;
249     /** \todo
250      * FIXME: remove this. We don't want to edit paths inside flowtext.
251      * Instead we will build our flowtext with cloned paths, so that the
252      * real paths are outside the flowtext and thus editable as usual.
253      */
254     if (SP_IS_FLOWTEXT(object)) {
255         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
256             if SP_IS_FLOWREGION(child) {
257                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
258                 if (grandchild && SP_IS_PATH(grandchild)) {
259                     object = SP_ITEM(grandchild);
260                     break;
261                 }
262             }
263         }
264     }
266     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
268     if (curve == NULL)
269         return NULL;
271     if (curve->get_segment_count() < 1) {
272         curve->unref();
273         return NULL; // prevent crash for one-node paths
274     }
276     //Create new nodepath
277     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
278     if (!np) {
279         curve->unref();
280         return NULL;
281     }
283     // Set defaults
284     np->desktop     = desktop;
285     np->object      = object;
286     np->subpaths    = NULL;
287     np->selected    = NULL;
288     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
289     np->local_change = 0;
290     np->show_handles = show_handles;
291     np->helper_path = NULL;
292     np->helper_path_vec = new HelperPathList;
293     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
294     np->helperpath_width = 1.0;
295     np->curve = curve->copy();
296     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
297     if (SP_IS_LPE_ITEM(object)) {
298         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
299         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
300             np->show_helperpath = true;
301         }            
302     }
303     np->straight_path = false;
304     if (IS_LIVEPATHEFFECT(object) && item) {
305         np->item = item;
306     } else {
307         np->item = SP_ITEM(object);
308     }
310     // we need to update item's transform from the repr here,
311     // because they may be out of sync when we respond
312     // to a change in repr by regenerating nodepath     --bb
313     sp_object_read_attr(SP_OBJECT(np->item), "transform");
315     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
316     np->d2i  = np->i2d.inverse();
318     np->repr = repr;
319     if (repr_key_in) { // apparantly the object is an LPEObject
320         np->repr_key = g_strdup(repr_key_in);
321         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
322         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
323         if (lpeparam) {
324             lpeparam->param_setup_nodepath(np);
325         }
326     } else {
327         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
328         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
329             np->repr_key = g_strdup("inkscape:original-d");
331             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
332             if (lpe) {
333                 lpe->setup_nodepath(np);
334             }
335         } else {
336             np->repr_key = g_strdup("d");
337         }
338     }
340     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
341      * So for example a closed rectangle has a nodetypestring of length 5.
342      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
343     Geom::PathVector pathv_sanitized = sp_nodepath_sanitize_path(np->curve->get_pathvector());
344     np->curve->set_pathvector(pathv_sanitized);
345     guint length = np->curve->get_segment_count();
346     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
347         length += pit->empty() ? 0 : 1;
348     }
350     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
351     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
353     // create the subpath(s) from the bpath
354     subpaths_from_pathvector(np, pathv_sanitized, typestr);
356     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
357     np->subpaths = g_list_reverse(np->subpaths);
359     delete[] typestr;
360     curve->unref();
362     // Draw helper curve
363     if (np->show_helperpath) {
364         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
365     }
367     sp_nodepath_create_helperpaths(np);
369     return np;
372 /**
373  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
374  */
375 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
377     if (!np)  //soft fail, like delete
378         return;
380     while (np->subpaths) {
381         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
382     }
384     //Inform the ShapeEditor that made me, if any, that I am gone.
385     if (np->shape_editor)
386         np->shape_editor->nodepath_destroyed();
388     g_assert(!np->selected);
390     if (np->helper_path) {
391         GtkObject *temp = np->helper_path;
392         np->helper_path = NULL;
393         gtk_object_destroy(temp);
394     }
395     if (np->curve) {
396         np->curve->unref();
397         np->curve = NULL;
398     }
400     if (np->repr_key) {
401         g_free(np->repr_key);
402         np->repr_key = NULL;
403     }
404     if (np->repr_nodetypes_key) {
405         g_free(np->repr_nodetypes_key);
406         np->repr_nodetypes_key = NULL;
407     }
409     sp_nodepath_destroy_helperpaths(np);
410     delete np->helper_path_vec;
411     np->helper_path_vec = NULL;
413     np->desktop = NULL;
415     g_free(np);
418 /**
419  *  Return the node count of a given NodeSubPath.
420  */
421 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
423     if (!subpath)
424         return 0;
425     gint nodeCount = g_list_length(subpath->nodes);
426     return nodeCount;
429 /**
430  *  Return the node count of a given NodePath.
431  */
432 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
434     if (!np)
435         return 0;
436     gint nodeCount = 0;
437     for (GList *item = np->subpaths ; item ; item=item->next) {
438        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
439         nodeCount += g_list_length(subpath->nodes);
440     }
441     return nodeCount;
444 /**
445  *  Return the subpath count of a given NodePath.
446  */
447 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
449     if (!np)
450         return 0;
451     return g_list_length (np->subpaths);
454 /**
455  *  Return the selected node count of a given NodePath.
456  */
457 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
459     if (!np)
460         return 0;
461     return g_list_length (np->selected);
464 /**
465  *  Return the number of subpaths where nodes are selected in a given NodePath.
466  */
467 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
469     if (!np)
470         return 0;
471     if (!np->selected)
472         return 0;
473     if (!np->selected->next)
474         return 1;
475     gint count = 0;
476     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
477         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
478         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
479             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
480             if (node->selected) {
481                 count ++;
482                 break;
483             }
484         }
485     }
486     return count;
489 /**
490  * Clean up a nodepath after editing.
491  *
492  * Currently we are deleting trivial subpaths.
493  */
494 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
496     GList *badSubPaths = NULL;
498     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
499     for (GList *l = nodepath->subpaths; l ; l=l->next) {
500        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
501        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
502             badSubPaths = g_list_append(badSubPaths, sp);
503     }
505     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
506     //also removes the subpath from nodepath->subpaths
507     for (GList *l = badSubPaths; l ; l=l->next) {
508        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
509         sp_nodepath_subpath_destroy(sp);
510     }
512     g_list_free(badSubPaths);
515 /**
516  * Create new nodepaths from pathvector, make it subpaths of np.
517  * \param t The node type array.
518  */
519 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
521     guint i = 0;  // index into node type array
522     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
523         if (pit->empty())
524             continue;  // don't add single knot paths
526         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
528         NR::Point ppos = from_2geom(pit->initialPoint()) * np->i2d;
529         NRPathcode pcode = NR_MOVETO;
531         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
532             add_curve_to_subpath(np, sp, *cit, t, i, ppos, pcode);
533         }
535         if (pit->closed()) {
536             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
537             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
538              * If the length is zero, don't add it to the nodepath. */
539             Geom::Curve const &closing_seg = pit->back_closed();
540             if ( ! closing_seg.isDegenerate() ) {
541                 NR::Point pos = from_2geom(closing_seg.finalPoint()) * np->i2d;
542                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
543             }
545             sp_nodepath_subpath_close(sp);
546         }
547     }
549 // should add initial point of curve with type of previous curve:
550 static void add_curve_to_subpath(Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c, Inkscape::NodePath::NodeType const *t, guint & i,
551                                  NR::Point & ppos, NRPathcode & pcode)
553     if( dynamic_cast<Geom::LineSegment const*>(&c) ||
554         dynamic_cast<Geom::HLineSegment const*>(&c) ||
555         dynamic_cast<Geom::VLineSegment const*>(&c) )
556     {
557         NR::Point pos = from_2geom(c.initialPoint()) * np->i2d;
558         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &pos, &pos, &pos);
559         ppos = from_2geom(c.finalPoint());
560         pcode = NR_LINETO;
561     }
562     else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) {
563         std::vector<Geom::Point> points = cubic_bezier->points();
564         NR::Point pos = from_2geom(points[0]) * np->i2d;
565         NR::Point npos = from_2geom(points[1]) * np->i2d;
566         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
567         ppos = from_2geom(points[2]) * np->i2d;
568         pcode = NR_CURVETO;
569     }
570     else {
571         //this case handles sbasis as well as all other curve types
572         Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1);
574         for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
575             add_curve_to_subpath(np, sp, *iter, t, i, ppos, pcode);
576         }
577     }
581 /**
582  * Convert from sodipodi:nodetypes to new style type array.
583  */
584 static
585 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
587     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
589     guint pos = 0;
591     if (types) {
592         for (guint i = 0; types[i] && ( i < length ); i++) {
593             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
594             if (types[i] != '\0') {
595                 switch (types[i]) {
596                     case 's':
597                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
598                         break;
599                     case 'z':
600                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
601                         break;
602                     case 'c':
603                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
604                         break;
605                     default:
606                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
607                         break;
608                 }
609             }
610         }
611     }
613     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
615     return typestr;
618 /**
619  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
620  * updated but repr is not (for speed). Used during curve and node drag.
621  */
622 static void update_object(Inkscape::NodePath::Path *np)
624     g_assert(np);
626     np->curve->unref();
627     np->curve = create_curve(np);
629     sp_nodepath_set_curve(np, np->curve);
631     if (np->show_helperpath) {
632         SPCurve * helper_curve = np->curve->copy();
633         helper_curve->transform(to_2geom(np->i2d));
634         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
635         helper_curve->unref();
636     }
638     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
639     //sp_nodepath_update_helperpaths(np);
641     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
642     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
643     np->shape_editor->update_knotholder();
646 /**
647  * Update XML path node with data from path object.
648  */
649 static void update_repr_internal(Inkscape::NodePath::Path *np)
651     g_assert(np);
653     Inkscape::XML::Node *repr = np->object->repr;
655     np->curve->unref();
656     np->curve = create_curve(np);
658     gchar *typestr = create_typestr(np);
659     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
661     // determine if path has an effect applied and write to correct "d" attribute.
662     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
663         np->local_change++;
664         repr->setAttribute(np->repr_key, svgpath);
665     }
667     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
668         np->local_change++;
669         repr->setAttribute(np->repr_nodetypes_key, typestr);
670     }
672     g_free(svgpath);
673     g_free(typestr);
675     if (np->show_helperpath) {
676         SPCurve * helper_curve = np->curve->copy();
677         helper_curve->transform(to_2geom(np->i2d));
678         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
679         helper_curve->unref();
680     }
682     // TODO: do we need this call here? after all, update_object() should have been called just before
683     //sp_nodepath_update_helperpaths(np);
686 /**
687  * Update XML path node with data from path object, commit changes forever.
688  */
689 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
691     //fixme: np can be NULL, so check before proceeding
692     g_return_if_fail(np != NULL);
694     update_repr_internal(np);
695     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
697     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
698                      annotation);
701 /**
702  * Update XML path node with data from path object, commit changes with undo.
703  */
704 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
706     update_repr_internal(np);
707     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
708                            annotation);
711 /**
712  * Make duplicate of path, replace corresponding XML node in tree, commit.
713  */
714 static void stamp_repr(Inkscape::NodePath::Path *np)
716     g_assert(np);
718     Inkscape::XML::Node *old_repr = np->object->repr;
719     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
721     // remember the position of the item
722     gint pos = old_repr->position();
723     // remember parent
724     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
726     SPCurve *curve = create_curve(np);
727     gchar *typestr = create_typestr(np);
729     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
731     new_repr->setAttribute(np->repr_key, svgpath);
732     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
734     // add the new repr to the parent
735     parent->appendChild(new_repr);
736     // move to the saved position
737     new_repr->setPosition(pos > 0 ? pos : 0);
739     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
740                      _("Stamp"));
742     Inkscape::GC::release(new_repr);
743     g_free(svgpath);
744     g_free(typestr);
745     curve->unref();
748 /**
749  * Create curve from path.
750  */
751 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
753     SPCurve *curve = new SPCurve();
755     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
756        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
757         curve->moveto(sp->first->pos * np->d2i);
758        Inkscape::NodePath::Node *n = sp->first->n.other;
759         while (n) {
760             NR::Point const end_pt = n->pos * np->d2i;
761             switch (n->code) {
762                 case NR_LINETO:
763                     curve->lineto(end_pt);
764                     break;
765                 case NR_CURVETO:
766                     curve->curveto(n->p.other->n.pos * np->d2i,
767                                      n->p.pos * np->d2i,
768                                      end_pt);
769                     break;
770                 default:
771                     g_assert_not_reached();
772                     break;
773             }
774             if (n != sp->last) {
775                 n = n->n.other;
776             } else {
777                 n = NULL;
778             }
779         }
780         if (sp->closed) {
781             curve->closepath();
782         }
783     }
785     return curve;
788 /**
789  * Convert path type string to sodipodi:nodetypes style.
790  */
791 static gchar *create_typestr(Inkscape::NodePath::Path *np)
793     gchar *typestr = g_new(gchar, 32);
794     gint len = 32;
795     gint pos = 0;
797     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
798        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
800         if (pos >= len) {
801             typestr = g_renew(gchar, typestr, len + 32);
802             len += 32;
803         }
805         typestr[pos++] = 'c';
807        Inkscape::NodePath::Node *n;
808         n = sp->first->n.other;
809         while (n) {
810             gchar code;
812             switch (n->type) {
813                 case Inkscape::NodePath::NODE_CUSP:
814                     code = 'c';
815                     break;
816                 case Inkscape::NodePath::NODE_SMOOTH:
817                     code = 's';
818                     break;
819                 case Inkscape::NodePath::NODE_SYMM:
820                     code = 'z';
821                     break;
822                 default:
823                     g_assert_not_reached();
824                     code = '\0';
825                     break;
826             }
828             if (pos >= len) {
829                 typestr = g_renew(gchar, typestr, len + 32);
830                 len += 32;
831             }
833             typestr[pos++] = code;
835             if (n != sp->last) {
836                 n = n->n.other;
837             } else {
838                 n = NULL;
839             }
840         }
841     }
843     if (pos >= len) {
844         typestr = g_renew(gchar, typestr, len + 1);
845         len += 1;
846     }
848     typestr[pos++] = '\0';
850     return typestr;
853 /**
854  * Returns current path in context. // later eliminate this function at all!
855  */
856 static Inkscape::NodePath::Path *sp_nodepath_current()
858     if (!SP_ACTIVE_DESKTOP) {
859         return NULL;
860     }
862     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
864     if (!SP_IS_NODE_CONTEXT(event_context)) {
865         return NULL;
866     }
868     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
873 /**
874  \brief Fills node and handle positions for three nodes, splitting line
875   marked by end at distance t.
876  */
877 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
879     g_assert(new_path != NULL);
880     g_assert(end      != NULL);
882     g_assert(end->p.other == new_path);
883    Inkscape::NodePath::Node *start = new_path->p.other;
884     g_assert(start);
886     if (end->code == NR_LINETO) {
887         new_path->type =Inkscape::NodePath::NODE_CUSP;
888         new_path->code = NR_LINETO;
889         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
890     } else {
891         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
892         new_path->code = NR_CURVETO;
893         gdouble s      = 1 - t;
894         for (int dim = 0; dim < 2; dim++) {
895             NR::Coord const f000 = start->pos[dim];
896             NR::Coord const f001 = start->n.pos[dim];
897             NR::Coord const f011 = end->p.pos[dim];
898             NR::Coord const f111 = end->pos[dim];
899             NR::Coord const f00t = s * f000 + t * f001;
900             NR::Coord const f01t = s * f001 + t * f011;
901             NR::Coord const f11t = s * f011 + t * f111;
902             NR::Coord const f0tt = s * f00t + t * f01t;
903             NR::Coord const f1tt = s * f01t + t * f11t;
904             NR::Coord const fttt = s * f0tt + t * f1tt;
905             start->n.pos[dim]    = f00t;
906             new_path->p.pos[dim] = f0tt;
907             new_path->pos[dim]   = fttt;
908             new_path->n.pos[dim] = f1tt;
909             end->p.pos[dim]      = f11t;
910         }
911     }
914 /**
915  * Adds new node on direct line between two nodes, activates handles of all
916  * three nodes.
917  */
918 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
920     g_assert(end);
921     g_assert(end->subpath);
922     g_assert(g_list_find(end->subpath->nodes, end));
924    Inkscape::NodePath::Node *start = end->p.other;
925     g_assert( start->n.other == end );
926    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
927                                                end,
928                                                (NRPathcode)end->code == NR_LINETO?
929                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
930                                                (NRPathcode)end->code,
931                                                &start->pos, &start->pos, &start->n.pos);
932     sp_nodepath_line_midpoint(newnode, end, t);
934     sp_node_adjust_handles(start);
935     sp_node_update_handles(start);
936     sp_node_update_handles(newnode);
937     sp_node_adjust_handles(end);
938     sp_node_update_handles(end);
940     return newnode;
943 /**
944 \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
945 */
946 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
948     g_assert(node);
949     g_assert(node->subpath);
950     g_assert(g_list_find(node->subpath->nodes, node));
952    Inkscape::NodePath::SubPath *sp = node->subpath;
953     Inkscape::NodePath::Path *np    = sp->nodepath;
955     if (sp->closed) {
956         sp_nodepath_subpath_open(sp, node);
957         return sp->first;
958     } else {
959         // no break for end nodes
960         if (node == sp->first) return NULL;
961         if (node == sp->last ) return NULL;
963         // create a new subpath
964        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
966         // duplicate the break node as start of the new subpath
967         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
969         // attach rest of curve to new node
970         g_assert(node->n.other);
971         newnode->n.other = node->n.other; node->n.other = NULL;
972         newnode->n.other->p.other = newnode;
973         newsubpath->last = sp->last;
974         sp->last = node;
975         node = newnode;
976         while (node->n.other) {
977             node = node->n.other;
978             node->subpath = newsubpath;
979             sp->nodes = g_list_remove(sp->nodes, node);
980             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
981         }
984         return newnode;
985     }
988 /**
989  * Duplicate node and connect to neighbours.
990  */
991 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
993     g_assert(node);
994     g_assert(node->subpath);
995     g_assert(g_list_find(node->subpath->nodes, node));
997    Inkscape::NodePath::SubPath *sp = node->subpath;
999     NRPathcode code = (NRPathcode) node->code;
1000     if (code == NR_MOVETO) { // if node is the endnode,
1001         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1002     }
1004     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1006     if (!node->n.other || !node->p.other) // if node is an endnode, select it
1007         return node;
1008     else
1009         return newnode; // otherwise select the newly created node
1012 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1014     node->p.pos = (node->pos + (node->pos - node->n.pos));
1017 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1019     node->n.pos = (node->pos + (node->pos - node->p.pos));
1022 /**
1023  * Change line type at node, with side effects on neighbours.
1024  */
1025 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1027     g_assert(end);
1028     g_assert(end->subpath);
1029     g_assert(end->p.other);
1031     if (end->code == static_cast< guint > ( code ) )
1032         return;
1034    Inkscape::NodePath::Node *start = end->p.other;
1036     end->code = code;
1038     if (code == NR_LINETO) {
1039         if (start->code == NR_LINETO) {
1040             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
1041         }
1042         if (end->n.other) {
1043             if (end->n.other->code == NR_LINETO) {
1044                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
1045             }
1046         }
1047     } else {
1048         NR::Point delta = end->pos - start->pos;
1049         start->n.pos = start->pos + delta / 3;
1050         end->p.pos = end->pos - delta / 3;
1051         sp_node_adjust_handle(start, 1);
1052         sp_node_adjust_handle(end, -1);
1053     }
1055     sp_node_update_handles(start);
1056     sp_node_update_handles(end);
1059 /**
1060  * Change node type, and its handles accordingly.
1061  */
1062 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1064     g_assert(node);
1065     g_assert(node->subpath);
1067     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1068         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1069             type =Inkscape::NodePath::NODE_CUSP;
1070         }
1071     }
1073     node->type = type;
1075     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1076         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1077         node->knot->setSize (node->selected? 11 : 9);
1078         sp_knot_update_ctrl(node->knot);
1079     } else {
1080         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1081         node->knot->setSize (node->selected? 9 : 7);
1082         sp_knot_update_ctrl(node->knot);
1083     }
1085     // if one of handles is mouseovered, preserve its position
1086     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1087         sp_node_adjust_handle(node, 1);
1088     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1089         sp_node_adjust_handle(node, -1);
1090     } else {
1091         sp_node_adjust_handles(node);
1092     }
1094     sp_node_update_handles(node);
1096     sp_nodepath_update_statusbar(node->subpath->nodepath);
1098     return node;
1101 bool
1102 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1104         Inkscape::NodePath::Node *othernode = side->other;
1105         if (!othernode)
1106             return false;
1107         NRPathcode const code = sp_node_path_code_from_side(node, side);
1108         if (code == NR_LINETO)
1109             return true;
1110         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1111         if (&node->p == side) {
1112             other_to_me = &othernode->n;
1113         } else if (&node->n == side) {
1114             other_to_me = &othernode->p;
1115         } 
1116         if (!other_to_me)
1117             return false;
1118         bool is_line = 
1119              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1120               NR::L2(node->pos - side->pos) < 1e-6);
1121         return is_line;
1124 /**
1125  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1126  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1127  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1128  * If already cusp and set to cusp, retracts handles.
1129 */
1130 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1132     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1134 /* 
1135   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1136  
1137         if (two_handles) {
1138             // do nothing, adjust_handles called via set_node_type will line them up
1139         } else if (one_handle) {
1140             if (opposite_to_handle_is_line) {
1141                 if (lined_up) {
1142                     // already half-smooth; pull opposite handle too making it fully smooth
1143                 } else {
1144                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1145                 }
1146             } else {
1147                 // pull opposite handle in line with the existing one
1148             }
1149         } else if (no_handles) {
1150             if (both_segments_are_lines OR both_segments_are_curves) {
1151                 //pull both handles
1152             } else {
1153                 // pull the handle opposite to line segment, making node half-smooth
1154             }
1155         }
1156 */
1157         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1158         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1159         bool p_is_line = sp_node_side_is_line(node, &node->p);
1160         bool n_is_line = sp_node_side_is_line(node, &node->n);
1162         if (p_has_handle && n_has_handle) {
1163             // do nothing, adjust_handles will line them up
1164         } else if (p_has_handle || n_has_handle) {
1165             if (p_has_handle && n_is_line) {
1166                 Radial line (node->n.other->pos - node->pos);
1167                 Radial handle (node->pos - node->p.pos);
1168                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1169                     // already half-smooth; pull opposite handle too making it fully smooth
1170                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1171                 } else {
1172                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1173                 }
1174             } else if (n_has_handle && p_is_line) {
1175                 Radial line (node->p.other->pos - node->pos);
1176                 Radial handle (node->pos - node->n.pos);
1177                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1178                     // already half-smooth; pull opposite handle too making it fully smooth
1179                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1180                 } else {
1181                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1182                 }
1183             } else if (p_has_handle && node->n.other) {
1184                 // pull n handle
1185                 node->n.other->code = NR_CURVETO;
1186                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1187                     NR::L2(node->p.pos - node->pos) :
1188                     NR::L2(node->n.other->pos - node->pos) / 3;
1189                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1190             } else if (n_has_handle && node->p.other) {
1191                 // pull p handle
1192                 node->code = NR_CURVETO;
1193                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1194                     NR::L2(node->n.pos - node->pos) :
1195                     NR::L2(node->p.other->pos - node->pos) / 3;
1196                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1197             }
1198         } else if (!p_has_handle && !n_has_handle) {
1199             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1200                 // no handles, but both segments are either lnes or curves:
1201                 //pull both handles
1203                 // convert both to curves:
1204                 node->code = NR_CURVETO;
1205                 node->n.other->code = NR_CURVETO;
1207                 NR::Point leg_prev = node->pos - node->p.other->pos;
1208                 NR::Point leg_next = node->pos - node->n.other->pos;
1210                 double norm_leg_prev = L2(leg_prev);
1211                 double norm_leg_next = L2(leg_next);
1213                 NR::Point delta;
1214                 if (norm_leg_next > 0.0) {
1215                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1216                     (&delta)->normalize();
1217                 }
1219                 if (type == Inkscape::NodePath::NODE_SYMM) {
1220                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1221                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1222                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1223                 } else {
1224                     // length of handle is proportional to distance to adjacent node
1225                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1226                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1227                 }
1229             } else {
1230                 // pull the handle opposite to line segment, making it half-smooth
1231                 if (p_is_line && node->n.other) {
1232                     if (type != Inkscape::NodePath::NODE_SYMM) {
1233                         // pull n handle
1234                         node->n.other->code = NR_CURVETO;
1235                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1236                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1237                     }
1238                 } else if (n_is_line && node->p.other) {
1239                     if (type != Inkscape::NodePath::NODE_SYMM) {
1240                         // pull p handle
1241                         node->code = NR_CURVETO;
1242                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1243                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1244                     }
1245                 }
1246             }
1247         }
1248     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1249         // cusping a cusp: retract nodes
1250         node->p.pos = node->pos;
1251         node->n.pos = node->pos;
1252     }
1254     sp_nodepath_set_node_type (node, type);
1257 /**
1258  * Move node to point, and adjust its and neighbouring handles.
1259  */
1260 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1262     NR::Point delta = p - node->pos;
1263     node->pos = p;
1265     node->p.pos += delta;
1266     node->n.pos += delta;
1268     Inkscape::NodePath::Node *node_p = NULL;
1269     Inkscape::NodePath::Node *node_n = NULL;
1271     if (node->p.other) {
1272         if (node->code == NR_LINETO) {
1273             sp_node_adjust_handle(node, 1);
1274             sp_node_adjust_handle(node->p.other, -1);
1275             node_p = node->p.other;
1276         }
1277     }
1278     if (node->n.other) {
1279         if (node->n.other->code == NR_LINETO) {
1280             sp_node_adjust_handle(node, -1);
1281             sp_node_adjust_handle(node->n.other, 1);
1282             node_n = node->n.other;
1283         }
1284     }
1286     // this function is only called from batch movers that will update display at the end
1287     // themselves, so here we just move all the knots without emitting move signals, for speed
1288     sp_node_update_handles(node, false);
1289     if (node_n) {
1290         sp_node_update_handles(node_n, false);
1291     }
1292     if (node_p) {
1293         sp_node_update_handles(node_p, false);
1294     }
1297 /**
1298  * Call sp_node_moveto() for node selection and handle possible snapping.
1299  */
1300 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1301                                             bool const snap, bool constrained = false, 
1302                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1304     NR::Coord best = NR_HUGE;
1305     NR::Point delta(dx, dy);
1306     NR::Point best_pt = delta;
1307     Inkscape::SnappedPoint best_abs;
1308     
1309     if (snap) {    
1310         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1311          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1312          * must provide that information. */
1313           
1314         // Build a list of the unselected nodes to which the snapper should snap 
1315         std::vector<NR::Point> unselected_nodes;
1316         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1317             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1318             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1319                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1320                 if (!node->selected) {
1321                     unselected_nodes.push_back(node->pos);
1322                 }    
1323             }
1324         }        
1325         
1326         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1327         
1328         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1329             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1330             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1331             Inkscape::SnappedPoint s;
1332             if (constrained) {
1333                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1334                 dedicated_constraint.setPoint(n->pos);
1335                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1336             } else {
1337                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1338             }            
1339             if (s.getSnapped() && (s.getDistance() < best)) {
1340                 best = s.getDistance();
1341                 best_abs = s;
1342                 best_pt = s.getPoint() - n->pos;
1343             }
1344         }
1345                         
1346         if (best_abs.getSnapped()) {
1347             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1348         } else {
1349             nodepath->desktop->snapindicator->remove_snappoint();    
1350         }
1351     }
1353     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1354         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1355         sp_node_moveto(n, n->pos + best_pt);
1356     }
1358     // do not update repr here so that node dragging is acceptably fast
1359     update_object(nodepath);
1362 /**
1363 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1364 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1365 near x = 0.
1366  */
1367 double
1368 sculpt_profile (double x, double alpha, guint profile)
1370     if (x >= 1)
1371         return 0;
1372     if (x <= 0)
1373         return 1;
1375     switch (profile) {
1376         case SCULPT_PROFILE_LINEAR:
1377         return 1 - x;
1378         case SCULPT_PROFILE_BELL:
1379         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1380         case SCULPT_PROFILE_ELLIPTIC:
1381         return sqrt(1 - x*x);
1382     }
1384     return 1;
1387 double
1388 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1390     // extremely primitive for now, don't have time to look for the real one
1391     double lower = NR::L2(b - a);
1392     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1393     return (lower + upper)/2;
1396 void
1397 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1399     n->pos = n->origin + delta;
1400     n->n.pos = n->n.origin + delta_n;
1401     n->p.pos = n->p.origin + delta_p;
1402     sp_node_adjust_handles(n);
1403     sp_node_update_handles(n, false);
1406 /**
1407  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1408  * on how far they are from the dragged node n.
1409  */
1410 static void
1411 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1413     g_assert (n);
1414     g_assert (nodepath);
1415     g_assert (n->subpath->nodepath == nodepath);
1417     double pressure = n->knot->pressure;
1418     if (pressure == 0)
1419         pressure = 0.5; // default
1420     pressure = CLAMP (pressure, 0.2, 0.8);
1422     // map pressure to alpha = 1/5 ... 5
1423     double alpha = 1 - 2 * fabs(pressure - 0.5);
1424     if (pressure > 0.5)
1425         alpha = 1/alpha;
1427     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1429     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1430         // Only one subpath has selected nodes:
1431         // use linear mode, where the distance from n to node being dragged is calculated along the path
1433         double n_sel_range = 0, p_sel_range = 0;
1434         guint n_nodes = 0, p_nodes = 0;
1435         guint n_sel_nodes = 0, p_sel_nodes = 0;
1437         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1438         {
1439             double n_range = 0, p_range = 0;
1440             bool n_going = true, p_going = true;
1441             Inkscape::NodePath::Node *n_node = n;
1442             Inkscape::NodePath::Node *p_node = n;
1443             do {
1444                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1445                 if (n_node && n_going)
1446                     n_node = n_node->n.other;
1447                 if (n_node == NULL) {
1448                     n_going = false;
1449                 } else {
1450                     n_nodes ++;
1451                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1452                     if (n_node->selected) {
1453                         n_sel_nodes ++;
1454                         n_sel_range = n_range;
1455                     }
1456                     if (n_node == p_node) {
1457                         n_going = false;
1458                         p_going = false;
1459                     }
1460                 }
1461                 if (p_node && p_going)
1462                     p_node = p_node->p.other;
1463                 if (p_node == NULL) {
1464                     p_going = false;
1465                 } else {
1466                     p_nodes ++;
1467                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1468                     if (p_node->selected) {
1469                         p_sel_nodes ++;
1470                         p_sel_range = p_range;
1471                     }
1472                     if (p_node == n_node) {
1473                         n_going = false;
1474                         p_going = false;
1475                     }
1476                 }
1477             } while (n_going || p_going);
1478         }
1480         // Second pass: actually move nodes in this subpath
1481         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1482         {
1483             double n_range = 0, p_range = 0;
1484             bool n_going = true, p_going = true;
1485             Inkscape::NodePath::Node *n_node = n;
1486             Inkscape::NodePath::Node *p_node = n;
1487             do {
1488                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1489                 if (n_node && n_going)
1490                     n_node = n_node->n.other;
1491                 if (n_node == NULL) {
1492                     n_going = false;
1493                 } else {
1494                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1495                     if (n_node->selected) {
1496                         sp_nodepath_move_node_and_handles (n_node,
1497                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1498                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1499                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1500                     }
1501                     if (n_node == p_node) {
1502                         n_going = false;
1503                         p_going = false;
1504                     }
1505                 }
1506                 if (p_node && p_going)
1507                     p_node = p_node->p.other;
1508                 if (p_node == NULL) {
1509                     p_going = false;
1510                 } else {
1511                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1512                     if (p_node->selected) {
1513                         sp_nodepath_move_node_and_handles (p_node,
1514                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1515                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1516                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1517                     }
1518                     if (p_node == n_node) {
1519                         n_going = false;
1520                         p_going = false;
1521                     }
1522                 }
1523             } while (n_going || p_going);
1524         }
1526     } else {
1527         // Multiple subpaths have selected nodes:
1528         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1529         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1530         // fix the pear-like shape when sculpting e.g. a ring
1532         // First pass: calculate range
1533         gdouble direct_range = 0;
1534         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1535             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1536             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1537                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1538                 if (node->selected) {
1539                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1540                 }
1541             }
1542         }
1544         // Second pass: actually move nodes
1545         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1546             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1547             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1548                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1549                 if (node->selected) {
1550                     if (direct_range > 1e-6) {
1551                         sp_nodepath_move_node_and_handles (node,
1552                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1553                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1554                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1555                     } else {
1556                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1557                     }
1559                 }
1560             }
1561         }
1562     }
1564     // do not update repr here so that node dragging is acceptably fast
1565     update_object(nodepath);
1569 /**
1570  * Move node selection to point, adjust its and neighbouring handles,
1571  * handle possible snapping, and commit the change with possible undo.
1572  */
1573 void
1574 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1576     if (!nodepath) return;
1578     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1580     if (dx == 0) {
1581         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1582     } else if (dy == 0) {
1583         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1584     } else {
1585         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1586     }
1589 /**
1590  * Move node selection off screen and commit the change.
1591  */
1592 void
1593 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1595     // borrowed from sp_selection_move_screen in selection-chemistry.c
1596     // we find out the current zoom factor and divide deltas by it
1597     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1599     gdouble zoom = desktop->current_zoom();
1600     gdouble zdx = dx / zoom;
1601     gdouble zdy = dy / zoom;
1603     if (!nodepath) return;
1605     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1607     if (dx == 0) {
1608         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1609     } else if (dy == 0) {
1610         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1611     } else {
1612         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1613     }
1616 /**
1617  * Move selected nodes to the absolute position given
1618  */
1619 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1621     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1622         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1623         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1624         sp_node_moveto(n, npos);
1625     }
1627     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1630 /**
1631  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1632  */
1633 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1635     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1636     g_return_val_if_fail(nodepath->selected, no_coord);
1638     // determine coordinate of first selected node
1639     GList *nsel = nodepath->selected;
1640     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1641     NR::Coord coord = n->pos[axis];
1642     bool coincide = true;
1644     // compare it to the coordinates of all the other selected nodes
1645     for (GList *l = nsel->next; l != NULL; l = l->next) {
1646         n = (Inkscape::NodePath::Node *) l->data;
1647         if (n->pos[axis] != coord) {
1648             coincide = false;
1649         }
1650     }
1651     if (coincide) {
1652         return coord;
1653     } else {
1654         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1655         // currently we return the coordinate of the bounding box midpoint because I don't know how
1656         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1657         return bbox.midpoint()[axis];
1658     }
1661 /** If they don't yet exist, creates knot and line for the given side of the node */
1662 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1664     if (!side->knot) {
1665         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"));
1667         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1668         side->knot->setSize (7);
1669         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1670         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1671         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1672         sp_knot_update_ctrl(side->knot);
1674         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1675         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1676         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1677         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1678         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1679         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1680     }
1682     if (!side->line) {
1683         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1684                                         SP_TYPE_CTRLLINE, NULL);
1685     }
1688 /**
1689  * Ensure the given handle of the node is visible/invisible, update its screen position
1690  */
1691 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1693     g_assert(node != NULL);
1695    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1696     NRPathcode code = sp_node_path_code_from_side(node, side);
1698     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1700     if (show_handle) {
1701         if (!side->knot) { // No handle knot at all
1702             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1703             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1704             side->knot->pos = side->pos;
1705             if (side->knot->item)
1706                 SP_CTRL(side->knot->item)->moveto(side->pos);
1707             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1708             sp_knot_show(side->knot);
1709         } else {
1710             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1711                 if (fire_move_signals) {
1712                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1713                 } else {
1714                     sp_knot_moveto(side->knot, side->pos);
1715                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1716                 }
1717             }
1718             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1719                 sp_knot_show(side->knot);
1720             }
1721         }
1722         sp_canvas_item_show(side->line);
1723     } else {
1724         if (side->knot) {
1725             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1726                 sp_knot_hide(side->knot);
1727             }
1728         }
1729         if (side->line) {
1730             sp_canvas_item_hide(side->line);
1731         }
1732     }
1735 /**
1736  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1737  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1738  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1739  * updated; otherwise, just move the knots silently (used in batch moves).
1740  */
1741 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1743     g_assert(node != NULL);
1745     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1746         sp_knot_show(node->knot);
1747     }
1749     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1750         if (fire_move_signals)
1751             sp_knot_set_position(node->knot, node->pos, 0);
1752         else
1753             sp_knot_moveto(node->knot, node->pos);
1754     }
1756     gboolean show_handles = node->selected;
1757     if (node->p.other != NULL) {
1758         if (node->p.other->selected) show_handles = TRUE;
1759     }
1760     if (node->n.other != NULL) {
1761         if (node->n.other->selected) show_handles = TRUE;
1762     }
1764     if (node->subpath->nodepath->show_handles == false)
1765         show_handles = FALSE;
1767     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1768     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1771 /**
1772  * Call sp_node_update_handles() for all nodes on subpath.
1773  */
1774 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1776     g_assert(subpath != NULL);
1778     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1779         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1780     }
1783 /**
1784  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1785  */
1786 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1788     g_assert(nodepath != NULL);
1790     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1791         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1792     }
1795 void
1796 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1798     if (nodepath == NULL) return;
1800     nodepath->show_handles = show;
1801     sp_nodepath_update_handles(nodepath);
1804 /**
1805  * Adds all selected nodes in nodepath to list.
1806  */
1807 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1809     StlConv<Node *>::list(l, selected);
1810 /// \todo this adds a copying, rework when the selection becomes a stl list
1813 /**
1814  * Align selected nodes on the specified axis.
1815  */
1816 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1818     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1819         return;
1820     }
1822     if ( !nodepath->selected->next ) { // only one node selected
1823         return;
1824     }
1825    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1826     NR::Point dest(pNode->pos);
1827     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1828         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1829         if (pNode) {
1830             dest[axis] = pNode->pos[axis];
1831             sp_node_moveto(pNode, dest);
1832         }
1833     }
1835     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1838 /// Helper struct.
1839 struct NodeSort
1841    Inkscape::NodePath::Node *_node;
1842     NR::Coord _coord;
1843     /// \todo use vectorof pointers instead of calling copy ctor
1844     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1845         _node(node), _coord(node->pos[axis])
1846     {}
1848 };
1850 static bool operator<(NodeSort const &a, NodeSort const &b)
1852     return (a._coord < b._coord);
1855 /**
1856  * Distribute selected nodes on the specified axis.
1857  */
1858 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1860     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1861         return;
1862     }
1864     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1865         return;
1866     }
1868    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1869     std::vector<NodeSort> sorted;
1870     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1871         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1872         if (pNode) {
1873             NodeSort n(pNode, axis);
1874             sorted.push_back(n);
1875             //dest[axis] = pNode->pos[axis];
1876             //sp_node_moveto(pNode, dest);
1877         }
1878     }
1879     std::sort(sorted.begin(), sorted.end());
1880     unsigned int len = sorted.size();
1881     //overall bboxes span
1882     float dist = (sorted.back()._coord -
1883                   sorted.front()._coord);
1884     //new distance between each bbox
1885     float step = (dist) / (len - 1);
1886     float pos = sorted.front()._coord;
1887     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1888           it < sorted.end();
1889           it ++ )
1890     {
1891         NR::Point dest((*it)._node->pos);
1892         dest[axis] = pos;
1893         sp_node_moveto((*it)._node, dest);
1894         pos += step;
1895     }
1897     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1901 /**
1902  * Call sp_nodepath_line_add_node() for all selected segments.
1903  */
1904 void
1905 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1907     if (!nodepath) {
1908         return;
1909     }
1911     GList *nl = NULL;
1913     int n_added = 0;
1915     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1916        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1917         g_assert(t->selected);
1918         if (t->p.other && t->p.other->selected) {
1919             nl = g_list_prepend(nl, t);
1920         }
1921     }
1923     while (nl) {
1924        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1925        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1926        sp_nodepath_node_select(n, TRUE, FALSE);
1927        n_added ++;
1928        nl = g_list_remove(nl, t);
1929     }
1931     /** \todo fixme: adjust ? */
1932     sp_nodepath_update_handles(nodepath);
1934     if (n_added > 1) {
1935         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1936     } else if (n_added > 0) {
1937         sp_nodepath_update_repr(nodepath, _("Add node"));
1938     }
1940     sp_nodepath_update_statusbar(nodepath);
1943 /**
1944  * Select segment nearest to point
1945  */
1946 void
1947 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1949     if (!nodepath) {
1950         return;
1951     }
1953     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1954     Geom::PathVector const &pathv = curve->get_pathvector();
1955     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1957     // calculate index for nodepath's representation.
1958     unsigned int segment_index = floor(pvpos.t) + 1;
1959     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1960         segment_index += pathv[i].size() + 1;
1961         if (pathv[i].closed()) {
1962             segment_index += 1;
1963         }
1964     }
1966     curve->unref();
1968     //find segment to segment
1969     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
1971     //fixme: this can return NULL, so check before proceeding.
1972     g_return_if_fail(e != NULL);
1974     gboolean force = FALSE;
1975     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1976         force = TRUE;
1977     }
1978     sp_nodepath_node_select(e, (gboolean) toggle, force);
1979     if (e->p.other)
1980         sp_nodepath_node_select(e->p.other, TRUE, force);
1982     sp_nodepath_update_handles(nodepath);
1984     sp_nodepath_update_statusbar(nodepath);
1987 /**
1988  * Add a node nearest to point
1989  */
1990 void
1991 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1993     if (!nodepath) {
1994         return;
1995     }
1997     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1998     Geom::PathVector const &pathv = curve->get_pathvector();
1999     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
2001     // calculate index for nodepath's representation.
2002     double int_part;
2003     double t = std::modf(pvpos.t, &int_part);
2004     unsigned int segment_index = (unsigned int)int_part + 1;
2005     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
2006         segment_index += pathv[i].size() + 1;
2007         if (pathv[i].closed()) {
2008             segment_index += 1;
2009         }
2010     }
2012     curve->unref();
2014     //find segment to split
2015     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
2017     //don't know why but t seems to flip for lines
2018     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2019         t = 1.0 - t;
2020     }
2022     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2023     sp_nodepath_node_select(n, FALSE, TRUE);
2025     /* fixme: adjust ? */
2026     sp_nodepath_update_handles(nodepath);
2028     sp_nodepath_update_repr(nodepath, _("Add node"));
2030     sp_nodepath_update_statusbar(nodepath);
2033 /*
2034  * Adjusts a segment so that t moves by a certain delta for dragging
2035  * converts lines to curves
2036  *
2037  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2038  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2039  */
2040 void
2041 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
2043     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
2045     //fixme: e and e->p can be NULL, so check for those before proceeding
2046     g_return_if_fail(e != NULL);
2047     g_return_if_fail(&e->p != NULL);
2049     /* feel good is an arbitrary parameter that distributes the delta between handles
2050      * if t of the drag point is less than 1/6 distance form the endpoint only
2051      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2052      */
2053     double feel_good;
2054     if (t <= 1.0 / 6.0)
2055         feel_good = 0;
2056     else if (t <= 0.5)
2057         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2058     else if (t <= 5.0 / 6.0)
2059         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2060     else
2061         feel_good = 1;
2063     //if we're dragging a line convert it to a curve
2064     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2065         sp_nodepath_set_line_type(e, NR_CURVETO);
2066     }
2068     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2069     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2070     e->p.other->n.pos += offsetcoord0;
2071     e->p.pos += offsetcoord1;
2073     // adjust handles of adjacent nodes where necessary
2074     sp_node_adjust_handle(e,1);
2075     sp_node_adjust_handle(e->p.other,-1);
2077     sp_nodepath_update_handles(e->subpath->nodepath);
2079     update_object(e->subpath->nodepath);
2081     sp_nodepath_update_statusbar(e->subpath->nodepath);
2085 /**
2086  * Call sp_nodepath_break() for all selected segments.
2087  */
2088 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2090     if (!nodepath) return;
2092     GList *tempin = g_list_copy(nodepath->selected);
2093     GList *temp = NULL;
2094     for (GList *l = tempin; l != NULL; l = l->next) {
2095        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2096        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2097         if (nn == NULL) continue; // no break, no new node
2098         temp = g_list_prepend(temp, nn);
2099     }
2100     g_list_free(tempin);
2102     if (temp) {
2103         sp_nodepath_deselect(nodepath);
2104     }
2105     for (GList *l = temp; l != NULL; l = l->next) {
2106         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2107     }
2109     sp_nodepath_update_handles(nodepath);
2111     sp_nodepath_update_repr(nodepath, _("Break path"));
2114 /**
2115  * Duplicate the selected node(s).
2116  */
2117 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2119     if (!nodepath) {
2120         return;
2121     }
2123     GList *temp = NULL;
2124     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2125        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2126        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2127         if (nn == NULL) continue; // could not duplicate
2128         temp = g_list_prepend(temp, nn);
2129     }
2131     if (temp) {
2132         sp_nodepath_deselect(nodepath);
2133     }
2134     for (GList *l = temp; l != NULL; l = l->next) {
2135         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2136     }
2138     sp_nodepath_update_handles(nodepath);
2140     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2143 /**
2144  *  Internal function to join two nodes by merging them into one.
2145  */
2146 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2148     /* a and b are endpoints */
2150     // if one of the two nodes is mouseovered, fix its position
2151     NR::Point c;
2152     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2153         c = a->pos;
2154     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2155         c = b->pos;
2156     } else {
2157         // otherwise, move joined node to the midpoint
2158         c = (a->pos + b->pos) / 2;
2159     }
2161     if (a->subpath == b->subpath) {
2162        Inkscape::NodePath::SubPath *sp = a->subpath;
2163         sp_nodepath_subpath_close(sp);
2164         sp_node_moveto (sp->first, c);
2166         sp_nodepath_update_handles(sp->nodepath);
2167         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2168         return;
2169     }
2171     /* a and b are separate subpaths */
2172     Inkscape::NodePath::SubPath *sa = a->subpath;
2173     Inkscape::NodePath::SubPath *sb = b->subpath;
2174     NR::Point p;
2175     Inkscape::NodePath::Node *n;
2176     NRPathcode code;
2177     if (a == sa->first) {
2178         // we will now reverse sa, so that a is its last node, not first, and drop that node
2179         p = sa->first->n.pos;
2180         code = (NRPathcode)sa->first->n.other->code;
2181         // create new subpath
2182        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2183        // create a first moveto node on it
2184         n = sa->last;
2185         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2186         n = n->p.other;
2187         if (n == sa->first) n = NULL;
2188         while (n) {
2189             // copy the rest of the nodes from sa to t, going backwards
2190             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2191             n = n->p.other;
2192             if (n == sa->first) n = NULL;
2193         }
2194         // replace sa with t
2195         sp_nodepath_subpath_destroy(sa);
2196         sa = t;
2197     } else if (a == sa->last) {
2198         // a is already last, just drop it
2199         p = sa->last->p.pos;
2200         code = (NRPathcode)sa->last->code;
2201         sp_nodepath_node_destroy(sa->last);
2202     } else {
2203         code = NR_END;
2204         g_assert_not_reached();
2205     }
2207     if (b == sb->first) {
2208         // copy all nodes from b to a, forward 
2209         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2210         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2211             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2212         }
2213     } else if (b == sb->last) {
2214         // copy all nodes from b to a, backward 
2215         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2216         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2217             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2218         }
2219     } else {
2220         g_assert_not_reached();
2221     }
2222     /* and now destroy sb */
2224     sp_nodepath_subpath_destroy(sb);
2226     sp_nodepath_update_handles(sa->nodepath);
2228     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2230     sp_nodepath_update_statusbar(nodepath);
2233 /**
2234  *  Internal function to join two nodes by adding a segment between them.
2235  */
2236 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2238     if (a->subpath == b->subpath) {
2239        Inkscape::NodePath::SubPath *sp = a->subpath;
2241         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2242         sp->closed = TRUE;
2244         sp->first->p.other = sp->last;
2245         sp->last->n.other  = sp->first;
2247         sp_node_handle_mirror_p_to_n(sp->last);
2248         sp_node_handle_mirror_n_to_p(sp->first);
2250         sp->first->code = sp->last->code;
2251         sp->first       = sp->last;
2253         sp_nodepath_update_handles(sp->nodepath);
2255         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2257         return;
2258     }
2260     /* a and b are separate subpaths */
2261     Inkscape::NodePath::SubPath *sa = a->subpath;
2262     Inkscape::NodePath::SubPath *sb = b->subpath;
2264     Inkscape::NodePath::Node *n;
2265     NR::Point p;
2266     NRPathcode code;
2267     if (a == sa->first) {
2268         code = (NRPathcode) sa->first->n.other->code;
2269        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2270         n = sa->last;
2271         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2272         for (n = n->p.other; n != NULL; n = n->p.other) {
2273             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2274         }
2275         sp_nodepath_subpath_destroy(sa);
2276         sa = t;
2277     } else if (a == sa->last) {
2278         code = (NRPathcode)sa->last->code;
2279     } else {
2280         code = NR_END;
2281         g_assert_not_reached();
2282     }
2284     if (b == sb->first) {
2285         n = sb->first;
2286         sp_node_handle_mirror_p_to_n(sa->last);
2287         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2288         sp_node_handle_mirror_n_to_p(sa->last);
2289         for (n = n->n.other; n != NULL; n = n->n.other) {
2290             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2291         }
2292     } else if (b == sb->last) {
2293         n = sb->last;
2294         sp_node_handle_mirror_p_to_n(sa->last);
2295         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2296         sp_node_handle_mirror_n_to_p(sa->last);
2297         for (n = n->p.other; n != NULL; n = n->p.other) {
2298             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2299         }
2300     } else {
2301         g_assert_not_reached();
2302     }
2303     /* and now destroy sb */
2305     sp_nodepath_subpath_destroy(sb);
2307     sp_nodepath_update_handles(sa->nodepath);
2309     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2312 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2314 /**
2315  * Internal function to handle joining two nodes.
2316  */
2317 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2319     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2321     if (g_list_length(nodepath->selected) != 2) {
2322         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2323         return;
2324     }
2326     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2327     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2329     g_assert(a != b);
2330     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2331         // someone tried to join an orphan node (i.e. a single-node subpath).
2332         // this is not worth an error message, just fail silently.
2333         return;
2334     }
2336     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2337         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2338         return;
2339     }
2341     switch(mode) {
2342         case NODE_JOIN_ENDPOINTS:
2343             do_node_selected_join(nodepath, a, b);
2344             break;
2345         case NODE_JOIN_SEGMENT:
2346             do_node_selected_join_segment(nodepath, a, b);
2347             break;
2348     }
2351 /**
2352  *  Join two nodes by merging them into one.
2353  */
2354 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2356     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2359 /**
2360  *  Join two nodes by adding a segment between them.
2361  */
2362 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2364     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2367 /**
2368  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2369  */
2370 void sp_node_delete_preserve(GList *nodes_to_delete)
2372     GSList *nodepaths = NULL;
2374     while (nodes_to_delete) {
2375         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2376         Inkscape::NodePath::SubPath *sp = node->subpath;
2377         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2378         Inkscape::NodePath::Node *sample_cursor = NULL;
2379         Inkscape::NodePath::Node *sample_end = NULL;
2380         Inkscape::NodePath::Node *delete_cursor = node;
2381         bool just_delete = false;
2383         //find the start of this contiguous selection
2384         //move left to the first node that is not selected
2385         //or the start of the non-closed path
2386         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2387             delete_cursor = curr;
2388         }
2390         //just delete at the beginning of an open path
2391         if (!delete_cursor->p.other) {
2392             sample_cursor = delete_cursor;
2393             just_delete = true;
2394         } else {
2395             sample_cursor = delete_cursor->p.other;
2396         }
2398         //calculate points for each segment
2399         int rate = 5;
2400         float period = 1.0 / rate;
2401         std::vector<NR::Point> data;
2402         if (!just_delete) {
2403             data.push_back(sample_cursor->pos);
2404             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2405                 //just delete at the end of an open path
2406                 if (!sp->closed && curr == sp->last) {
2407                     just_delete = true;
2408                     break;
2409                 }
2411                 //sample points on the contiguous selected segment
2412                 NR::Point *bez;
2413                 bez = new NR::Point [4];
2414                 bez[0] = curr->pos;
2415                 bez[1] = curr->n.pos;
2416                 bez[2] = curr->n.other->p.pos;
2417                 bez[3] = curr->n.other->pos;
2418                 for (int i=1; i<rate; i++) {
2419                     gdouble t = i * period;
2420                     NR::Point p = bezier_pt(3, bez, t);
2421                     data.push_back(p);
2422                 }
2423                 data.push_back(curr->n.other->pos);
2425                 sample_end = curr->n.other;
2426                 //break if we've come full circle or hit the end of the selection
2427                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2428                     break;
2429                 }
2430             }
2431         }
2433         if (!just_delete) {
2434             //calculate the best fitting single segment and adjust the endpoints
2435             NR::Point *adata;
2436             adata = new NR::Point [data.size()];
2437             copy(data.begin(), data.end(), adata);
2439             NR::Point *bez;
2440             bez = new NR::Point [4];
2441             //would decreasing error create a better fitting approximation?
2442             gdouble error = 1.0;
2443             gint ret;
2444             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2446             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2447             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2448             //the resulting nodes behave as expected.
2449             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2450                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2451             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2452                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2454             //adjust endpoints
2455             sample_cursor->n.pos = bez[1];
2456             sample_end->p.pos = bez[2];
2457         }
2459         //destroy this contiguous selection
2460         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2461             Inkscape::NodePath::Node *temp = delete_cursor;
2462             if (delete_cursor->n.other == delete_cursor) {
2463                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2464                 delete_cursor = NULL;
2465             } else {
2466                 delete_cursor = delete_cursor->n.other;
2467             }
2468             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2469             sp_nodepath_node_destroy(temp);
2470         }
2472         sp_nodepath_update_handles(nodepath);
2474         if (!g_slist_find(nodepaths, nodepath))
2475             nodepaths = g_slist_prepend (nodepaths, nodepath);
2476     }
2478     for (GSList *i = nodepaths; i; i = i->next) {
2479         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2480         // different nodepaths will give us one undo event per nodepath
2481         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2483         // if the entire nodepath is removed, delete the selected object.
2484         if (nodepath->subpaths == NULL ||
2485             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2486             //at least 2
2487             sp_nodepath_get_node_count(nodepath) < 2) {
2488             SPDocument *document = sp_desktop_document (nodepath->desktop);
2489             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2490             //delete this nodepath's object, not the entire selection! (though at this time, this
2491             //does not matter)
2492             sp_selection_delete();
2493             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2494                               _("Delete nodes"));
2495         } else {
2496             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2497             sp_nodepath_update_statusbar(nodepath);
2498         }
2499     }
2501     g_slist_free (nodepaths);
2504 /**
2505  * Delete one or more selected nodes.
2506  */
2507 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2509     if (!nodepath) return;
2510     if (!nodepath->selected) return;
2512     /** \todo fixme: do it the right way */
2513     while (nodepath->selected) {
2514        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2515         sp_nodepath_node_destroy(node);
2516     }
2519     //clean up the nodepath (such as for trivial subpaths)
2520     sp_nodepath_cleanup(nodepath);
2522     sp_nodepath_update_handles(nodepath);
2524     // if the entire nodepath is removed, delete the selected object.
2525     if (nodepath->subpaths == NULL ||
2526         sp_nodepath_get_node_count(nodepath) < 2) {
2527         SPDocument *document = sp_desktop_document (nodepath->desktop);
2528         sp_selection_delete();
2529         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2530                           _("Delete nodes"));
2531         return;
2532     }
2534     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2536     sp_nodepath_update_statusbar(nodepath);
2539 /**
2540  * Delete one or more segments between two selected nodes.
2541  * This is the code for 'split'.
2542  */
2543 void
2544 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2546    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2547    Inkscape::NodePath::Node *curr, *next;     //Iterators
2549     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2551     if (g_list_length(nodepath->selected) != 2) {
2552         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2553                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2554         return;
2555     }
2557     //Selected nodes, not inclusive
2558    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2559    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2561     if ( ( a==b)                       ||  //same node
2562          (a->subpath  != b->subpath )  ||  //not the same path
2563          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2564          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2565     {
2566         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2567                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2568         return;
2569     }
2571     //###########################################
2572     //# BEGIN EDITS
2573     //###########################################
2574     //##################################
2575     //# CLOSED PATH
2576     //##################################
2577     if (a->subpath->closed) {
2580         gboolean reversed = FALSE;
2582         //Since we can go in a circle, we need to find the shorter distance.
2583         //  a->b or b->a
2584         start = end = NULL;
2585         int distance    = 0;
2586         int minDistance = 0;
2587         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2588             if (curr==b) {
2589                 //printf("a to b:%d\n", distance);
2590                 start = a;//go from a to b
2591                 end   = b;
2592                 minDistance = distance;
2593                 //printf("A to B :\n");
2594                 break;
2595             }
2596             distance++;
2597         }
2599         //try again, the other direction
2600         distance = 0;
2601         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2602             if (curr==a) {
2603                 //printf("b to a:%d\n", distance);
2604                 if (distance < minDistance) {
2605                     start    = b;  //we go from b to a
2606                     end      = a;
2607                     reversed = TRUE;
2608                     //printf("B to A\n");
2609                 }
2610                 break;
2611             }
2612             distance++;
2613         }
2616         //Copy everything from 'end' to 'start' to a new subpath
2617        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2618         for (curr=end ; curr ; curr=curr->n.other) {
2619             NRPathcode code = (NRPathcode) curr->code;
2620             if (curr == end)
2621                 code = NR_MOVETO;
2622             sp_nodepath_node_new(t, NULL,
2623                                  (Inkscape::NodePath::NodeType)curr->type, code,
2624                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2625             if (curr == start)
2626                 break;
2627         }
2628         sp_nodepath_subpath_destroy(a->subpath);
2631     }
2635     //##################################
2636     //# OPEN PATH
2637     //##################################
2638     else {
2640         //We need to get the direction of the list between A and B
2641         //Can we walk from a to b?
2642         start = end = NULL;
2643         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2644             if (curr==b) {
2645                 start = a;  //did it!  we go from a to b
2646                 end   = b;
2647                 //printf("A to B\n");
2648                 break;
2649             }
2650         }
2651         if (!start) {//didn't work?  let's try the other direction
2652             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2653                 if (curr==a) {
2654                     start = b;  //did it!  we go from b to a
2655                     end   = a;
2656                     //printf("B to A\n");
2657                     break;
2658                 }
2659             }
2660         }
2661         if (!start) {
2662             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2663                                                      _("Cannot find path between nodes."));
2664             return;
2665         }
2669         //Copy everything after 'end' to a new subpath
2670        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2671         for (curr=end ; curr ; curr=curr->n.other) {
2672             NRPathcode code = (NRPathcode) curr->code;
2673             if (curr == end)
2674                 code = NR_MOVETO;
2675             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2676                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2677         }
2679         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2680         for (curr = start->n.other ; curr  ; curr=next) {
2681             next = curr->n.other;
2682             sp_nodepath_node_destroy(curr);
2683         }
2685     }
2686     //###########################################
2687     //# END EDITS
2688     //###########################################
2690     //clean up the nodepath (such as for trivial subpaths)
2691     sp_nodepath_cleanup(nodepath);
2693     sp_nodepath_update_handles(nodepath);
2695     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2697     sp_nodepath_update_statusbar(nodepath);
2700 /**
2701  * Call sp_nodepath_set_line() for all selected segments.
2702  */
2703 void
2704 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2706     if (nodepath == NULL) return;
2708     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2709        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2710         g_assert(n->selected);
2711         if (n->p.other && n->p.other->selected) {
2712             sp_nodepath_set_line_type(n, code);
2713         }
2714     }
2716     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2719 /**
2720  * Call sp_nodepath_convert_node_type() for all selected nodes.
2721  */
2722 void
2723 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2725     if (nodepath == NULL) return;
2727     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2729     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2730         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2731     }
2733     sp_nodepath_update_repr(nodepath, _("Change node type"));
2736 /**
2737  * Change select status of node, update its own and neighbour handles.
2738  */
2739 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2741     node->selected = selected;
2743     if (selected) {
2744         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2745         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2746         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2747         sp_knot_update_ctrl(node->knot);
2748     } else {
2749         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2750         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2751         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2752         sp_knot_update_ctrl(node->knot);
2753     }
2755     sp_node_update_handles(node);
2756     if (node->n.other) sp_node_update_handles(node->n.other);
2757     if (node->p.other) sp_node_update_handles(node->p.other);
2760 /**
2761 \brief Select a node
2762 \param node     The node to select
2763 \param incremental   If true, add to selection, otherwise deselect others
2764 \param override   If true, always select this node, otherwise toggle selected status
2765 */
2766 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2768     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2770     if (incremental) {
2771         if (override) {
2772             if (!g_list_find(nodepath->selected, node)) {
2773                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2774             }
2775             sp_node_set_selected(node, TRUE);
2776         } else { // toggle
2777             if (node->selected) {
2778                 g_assert(g_list_find(nodepath->selected, node));
2779                 nodepath->selected = g_list_remove(nodepath->selected, node);
2780             } else {
2781                 g_assert(!g_list_find(nodepath->selected, node));
2782                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2783             }
2784             sp_node_set_selected(node, !node->selected);
2785         }
2786     } else {
2787         sp_nodepath_deselect(nodepath);
2788         nodepath->selected = g_list_prepend(nodepath->selected, node);
2789         sp_node_set_selected(node, TRUE);
2790     }
2792     sp_nodepath_update_statusbar(nodepath);
2796 /**
2797 \brief Deselect all nodes in the nodepath
2798 */
2799 void
2800 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2802     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2804     while (nodepath->selected) {
2805         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2806         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2807     }
2808     sp_nodepath_update_statusbar(nodepath);
2811 /**
2812 \brief Select or invert selection of all nodes in the nodepath
2813 */
2814 void
2815 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2817     if (!nodepath) return;
2819     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2820        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2821         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2822            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2823            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2824         }
2825     }
2828 /**
2829  * If nothing selected, does the same as sp_nodepath_select_all();
2830  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2831  * (i.e., similar to "select all in layer", with the "selected" subpaths
2832  * being treated as "layers" in the path).
2833  */
2834 void
2835 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2837     if (!nodepath) return;
2839     if (g_list_length (nodepath->selected) == 0) {
2840         sp_nodepath_select_all (nodepath, invert);
2841         return;
2842     }
2844     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2845     GSList *subpaths = NULL;
2847     for (GList *l = copy; l != NULL; l = l->next) {
2848         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2849         Inkscape::NodePath::SubPath *subpath = n->subpath;
2850         if (!g_slist_find (subpaths, subpath))
2851             subpaths = g_slist_prepend (subpaths, subpath);
2852     }
2854     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2855         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2856         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2857             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2858             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2859         }
2860     }
2862     g_slist_free (subpaths);
2863     g_list_free (copy);
2866 /**
2867  * \brief Select the node after the last selected; if none is selected,
2868  * select the first within path.
2869  */
2870 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2872     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2874    Inkscape::NodePath::Node *last = NULL;
2875     if (nodepath->selected) {
2876         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2877            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2878             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2879             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2880                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2881                 if (node->selected) {
2882                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2883                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2884                             if (spl->next) { // there's a next subpath
2885                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2886                                 last = subpath_next->first;
2887                             } else if (spl->prev) { // there's a previous subpath
2888                                 last = NULL; // to be set later to the first node of first subpath
2889                             } else {
2890                                 last = node->n.other;
2891                             }
2892                         } else {
2893                             last = node->n.other;
2894                         }
2895                     } else {
2896                         if (node->n.other) {
2897                             last = node->n.other;
2898                         } else {
2899                             if (spl->next) { // there's a next subpath
2900                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2901                                 last = subpath_next->first;
2902                             } else if (spl->prev) { // there's a previous subpath
2903                                 last = NULL; // to be set later to the first node of first subpath
2904                             } else {
2905                                 last = (Inkscape::NodePath::Node *) subpath->first;
2906                             }
2907                         }
2908                     }
2909                 }
2910             }
2911         }
2912         sp_nodepath_deselect(nodepath);
2913     }
2915     if (last) { // there's at least one more node after selected
2916         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2917     } else { // no more nodes, select the first one in first subpath
2918        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2919         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2920     }
2923 /**
2924  * \brief Select the node before the first selected; if none is selected,
2925  * select the last within path
2926  */
2927 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2929     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2931    Inkscape::NodePath::Node *last = NULL;
2932     if (nodepath->selected) {
2933         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2934            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2935             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2936                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2937                 if (node->selected) {
2938                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2939                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2940                             if (spl->prev) { // there's a prev subpath
2941                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2942                                 last = subpath_prev->last;
2943                             } else if (spl->next) { // there's a next subpath
2944                                 last = NULL; // to be set later to the last node of last subpath
2945                             } else {
2946                                 last = node->p.other;
2947                             }
2948                         } else {
2949                             last = node->p.other;
2950                         }
2951                     } else {
2952                         if (node->p.other) {
2953                             last = node->p.other;
2954                         } else {
2955                             if (spl->prev) { // there's a prev subpath
2956                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2957                                 last = subpath_prev->last;
2958                             } else if (spl->next) { // there's a next subpath
2959                                 last = NULL; // to be set later to the last node of last subpath
2960                             } else {
2961                                 last = (Inkscape::NodePath::Node *) subpath->last;
2962                             }
2963                         }
2964                     }
2965                 }
2966             }
2967         }
2968         sp_nodepath_deselect(nodepath);
2969     }
2971     if (last) { // there's at least one more node before selected
2972         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2973     } else { // no more nodes, select the last one in last subpath
2974         GList *spl = g_list_last(nodepath->subpaths);
2975        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2976         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2977     }
2980 /**
2981  * \brief Select all nodes that are within the rectangle.
2982  */
2983 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2985     if (!incremental) {
2986         sp_nodepath_deselect(nodepath);
2987     }
2989     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2990        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2991         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2992            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2994             if (b.contains(node->pos)) {
2995                 sp_nodepath_node_select(node, TRUE, TRUE);
2996             }
2997         }
2998     }
3002 void
3003 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3005     g_assert (n);
3006     g_assert (nodepath);
3007     g_assert (n->subpath->nodepath == nodepath);
3009     if (g_list_length (nodepath->selected) == 0) {
3010         if (grow > 0) {
3011             sp_nodepath_node_select(n, TRUE, TRUE);
3012         }
3013         return;
3014     }
3016     if (g_list_length (nodepath->selected) == 1) {
3017         if (grow < 0) {
3018             sp_nodepath_deselect (nodepath);
3019             return;
3020         }
3021     }
3023         double n_sel_range = 0, p_sel_range = 0;
3024             Inkscape::NodePath::Node *farthest_n_node = n;
3025             Inkscape::NodePath::Node *farthest_p_node = n;
3027         // Calculate ranges
3028         {
3029             double n_range = 0, p_range = 0;
3030             bool n_going = true, p_going = true;
3031             Inkscape::NodePath::Node *n_node = n;
3032             Inkscape::NodePath::Node *p_node = n;
3033             do {
3034                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3035                 if (n_node && n_going)
3036                     n_node = n_node->n.other;
3037                 if (n_node == NULL) {
3038                     n_going = false;
3039                 } else {
3040                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3041                     if (n_node->selected) {
3042                         n_sel_range = n_range;
3043                         farthest_n_node = n_node;
3044                     }
3045                     if (n_node == p_node) {
3046                         n_going = false;
3047                         p_going = false;
3048                     }
3049                 }
3050                 if (p_node && p_going)
3051                     p_node = p_node->p.other;
3052                 if (p_node == NULL) {
3053                     p_going = false;
3054                 } else {
3055                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3056                     if (p_node->selected) {
3057                         p_sel_range = p_range;
3058                         farthest_p_node = p_node;
3059                     }
3060                     if (p_node == n_node) {
3061                         n_going = false;
3062                         p_going = false;
3063                     }
3064                 }
3065             } while (n_going || p_going);
3066         }
3068     if (grow > 0) {
3069         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3070                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3071         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3072                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3073         }
3074     } else {
3075         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3076                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3077         } else if (farthest_p_node && farthest_p_node->selected) {
3078                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3079         }
3080     }
3083 void
3084 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3086     g_assert (n);
3087     g_assert (nodepath);
3088     g_assert (n->subpath->nodepath == nodepath);
3090     if (g_list_length (nodepath->selected) == 0) {
3091         if (grow > 0) {
3092             sp_nodepath_node_select(n, TRUE, TRUE);
3093         }
3094         return;
3095     }
3097     if (g_list_length (nodepath->selected) == 1) {
3098         if (grow < 0) {
3099             sp_nodepath_deselect (nodepath);
3100             return;
3101         }
3102     }
3104     Inkscape::NodePath::Node *farthest_selected = NULL;
3105     double farthest_dist = 0;
3107     Inkscape::NodePath::Node *closest_unselected = NULL;
3108     double closest_dist = NR_HUGE;
3110     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3111        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3112         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3113            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3114            if (node == n)
3115                continue;
3116            if (node->selected) {
3117                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3118                    farthest_dist = NR::L2(node->pos - n->pos);
3119                    farthest_selected = node;
3120                }
3121            } else {
3122                if (NR::L2(node->pos - n->pos) < closest_dist) {
3123                    closest_dist = NR::L2(node->pos - n->pos);
3124                    closest_unselected = node;
3125                }
3126            }
3127         }
3128     }
3130     if (grow > 0) {
3131         if (closest_unselected) {
3132             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3133         }
3134     } else {
3135         if (farthest_selected) {
3136             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3137         }
3138     }
3142 /**
3143 \brief  Saves all nodes' and handles' current positions in their origin members
3144 */
3145 void
3146 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3148     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3149        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3150         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3151            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3152            n->origin = n->pos;
3153            n->p.origin = n->p.pos;
3154            n->n.origin = n->n.pos;
3155         }
3156     }
3159 /**
3160 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3161 */
3162 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3164     if (!nodepath->selected) {
3165         return NULL;
3166     }
3168     GList *r = NULL;
3169     guint i = 0;
3170     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3171        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3172         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3173            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3174             i++;
3175             if (node->selected) {
3176                 r = g_list_append(r, GINT_TO_POINTER(i));
3177             }
3178         }
3179     }
3180     return r;
3183 /**
3184 \brief  Restores selection by selecting nodes whose positions are in the list
3185 */
3186 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3188     sp_nodepath_deselect(nodepath);
3190     guint i = 0;
3191     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3192        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3193         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3194            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3195             i++;
3196             if (g_list_find(r, GINT_TO_POINTER(i))) {
3197                 sp_nodepath_node_select(node, TRUE, TRUE);
3198             }
3199         }
3200     }
3204 /**
3205 \brief Adjusts handle according to node type and line code.
3206 */
3207 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3209     g_assert(node);
3211    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3212    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3214    // nothing to do if we are an end node
3215     if (me->other == NULL) return;
3216     if (other->other == NULL) return;
3218     // nothing to do if we are a cusp node
3219     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3221     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3222     NRPathcode mecode;
3223     if (which_adjust == 1) {
3224         mecode = (NRPathcode)me->other->code;
3225     } else {
3226         mecode = (NRPathcode)node->code;
3227     }
3228     if (mecode == NR_LINETO) return;
3230     if (sp_node_side_is_line(node, other)) {
3231         // other is a line, and we are either smooth or symm
3232        Inkscape::NodePath::Node *othernode = other->other;
3233         double len = NR::L2(me->pos - node->pos);
3234         NR::Point delta = node->pos - othernode->pos;
3235         double linelen = NR::L2(delta);
3236         if (linelen < 1e-18)
3237             return;
3238         me->pos = node->pos + (len / linelen)*delta;
3239         return;
3240     }
3242     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3243         // symmetrize 
3244         me->pos = 2 * node->pos - other->pos;
3245         return;
3246     } else {
3247         // smoothify
3248         double len = NR::L2(me->pos - node->pos);
3249         NR::Point delta = other->pos - node->pos;
3250         double otherlen = NR::L2(delta);
3251         if (otherlen < 1e-18) return;
3252         me->pos = node->pos - (len / otherlen) * delta;
3253     }
3256 /**
3257  \brief Adjusts both handles according to node type and line code
3258  */
3259 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3261     g_assert(node);
3263     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3265     /* we are either smooth or symm */
3267     if (node->p.other == NULL) return;
3268     if (node->n.other == NULL) return;
3270     if (sp_node_side_is_line(node, &node->p)) {
3271         sp_node_adjust_handle(node, 1);
3272         return;
3273     }
3275     if (sp_node_side_is_line(node, &node->n)) {
3276         sp_node_adjust_handle(node, -1);
3277         return;
3278     }
3280     /* both are curves */
3281     NR::Point const delta( node->n.pos - node->p.pos );
3283     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3284         node->p.pos = node->pos - delta / 2;
3285         node->n.pos = node->pos + delta / 2;
3286         return;
3287     }
3289     /* We are smooth */
3290     double plen = NR::L2(node->p.pos - node->pos);
3291     if (plen < 1e-18) return;
3292     double nlen = NR::L2(node->n.pos - node->pos);
3293     if (nlen < 1e-18) return;
3294     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3295     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3298 /**
3299  * Node event callback.
3300  */
3301 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3303     gboolean ret = FALSE;
3304     switch (event->type) {
3305         case GDK_ENTER_NOTIFY:
3306             Inkscape::NodePath::Path::active_node = n;
3307             break;
3308         case GDK_LEAVE_NOTIFY:
3309             Inkscape::NodePath::Path::active_node = NULL;
3310             break;
3311         case GDK_SCROLL:
3312             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3313                 switch (event->scroll.direction) {
3314                     case GDK_SCROLL_UP:
3315                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3316                         break;
3317                     case GDK_SCROLL_DOWN:
3318                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3319                         break;
3320                     default:
3321                         break;
3322                 }
3323                 ret = TRUE;
3324             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3325                 switch (event->scroll.direction) {
3326                     case GDK_SCROLL_UP:
3327                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3328                         break;
3329                     case GDK_SCROLL_DOWN:
3330                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3331                         break;
3332                     default:
3333                         break;
3334                 }
3335                 ret = TRUE;
3336             }
3337             break;
3338         case GDK_KEY_PRESS:
3339             switch (get_group0_keyval (&event->key)) {
3340                 case GDK_space:
3341                     if (event->key.state & GDK_BUTTON1_MASK) {
3342                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3343                         stamp_repr(nodepath);
3344                         ret = TRUE;
3345                     }
3346                     break;
3347                 case GDK_Page_Up:
3348                     if (event->key.state & GDK_CONTROL_MASK) {
3349                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3350                     } else {
3351                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3352                     }
3353                     break;
3354                 case GDK_Page_Down:
3355                     if (event->key.state & GDK_CONTROL_MASK) {
3356                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3357                     } else {
3358                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3359                     }
3360                     break;
3361                 default:
3362                     break;
3363             }
3364             break;
3365         default:
3366             break;
3367     }
3369     return ret;
3372 /**
3373  * Handle keypress on node; directly called.
3374  */
3375 gboolean node_key(GdkEvent *event)
3377     Inkscape::NodePath::Path *np;
3379     // there is no way to verify nodes so set active_node to nil when deleting!!
3380     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3382     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3383         gint ret = FALSE;
3384         switch (get_group0_keyval (&event->key)) {
3385             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3386             case GDK_BackSpace:
3387                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3388                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3389                 sp_nodepath_update_repr(np, _("Delete node"));
3390                 Inkscape::NodePath::Path::active_node = NULL;
3391                 ret = TRUE;
3392                 break;
3393             case GDK_c:
3394                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3395                 ret = TRUE;
3396                 break;
3397             case GDK_s:
3398                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3399                 ret = TRUE;
3400                 break;
3401             case GDK_y:
3402                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3403                 ret = TRUE;
3404                 break;
3405             case GDK_b:
3406                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3407                 ret = TRUE;
3408                 break;
3409         }
3410         return ret;
3411     }
3412     return FALSE;
3415 /**
3416  * Mouseclick on node callback.
3417  */
3418 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3420    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3422     if (state & GDK_CONTROL_MASK) {
3423         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3425         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3426             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3427                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3428             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3429                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3430             } else {
3431                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3432             }
3433             sp_nodepath_update_repr(nodepath, _("Change node type"));
3434             sp_nodepath_update_statusbar(nodepath);
3436         } else { //ctrl+alt+click: delete node
3437             GList *node_to_delete = NULL;
3438             node_to_delete = g_list_append(node_to_delete, n);
3439             sp_node_delete_preserve(node_to_delete);
3440         }
3442     } else {
3443         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3444     }
3447 /**
3448  * Mouse grabbed node callback.
3449  */
3450 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3452    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3454     if (!n->selected) {
3455         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3456     }
3458     n->is_dragging = true;
3459     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3461     sp_nodepath_remember_origins (n->subpath->nodepath);
3464 /**
3465  * Mouse ungrabbed node callback.
3466  */
3467 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3469    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3471    n->dragging_out = NULL;
3472    n->is_dragging = false;
3473    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3475    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3478 /**
3479  * The point on a line, given by its angle, closest to the given point.
3480  * \param p  A point.
3481  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3482  * \param closest  Pointer to the point struct where the result is stored.
3483  * \todo FIXME: use dot product perhaps?
3484  */
3485 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3487     if (a == HUGE_VAL) { // vertical
3488         *closest = NR::Point(0, (*p)[NR::Y]);
3489     } else {
3490         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3491         (*closest)[NR::Y] = a * (*closest)[NR::X];
3492     }
3495 /**
3496  * Distance from the point to a line given by its angle.
3497  * \param p  A point.
3498  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3499  */
3500 static double point_line_distance(NR::Point *p, double a)
3502     NR::Point c;
3503     point_line_closest(p, a, &c);
3504     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]));
3507 /**
3508  * Callback for node "request" signal.
3509  * \todo fixme: This goes to "moved" event? (lauris)
3510  */
3511 static gboolean
3512 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3514     double yn, xn, yp, xp;
3515     double an, ap, na, pa;
3516     double d_an, d_ap, d_na, d_pa;
3517     gboolean collinear = FALSE;
3518     NR::Point c;
3519     NR::Point pr;
3521     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3523     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3525     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3526     if ( (!n->subpath->nodepath->straight_path) &&
3527          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3528            || n->dragging_out ) )
3529     {
3530        NR::Point mouse = (*p);
3532        if (!n->dragging_out) {
3533            // This is the first drag-out event; find out which handle to drag out
3534            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3535            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3537            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3538                return FALSE;
3540            Inkscape::NodePath::NodeSide *opposite;
3541            if (appr_p > appr_n) { // closer to p
3542                n->dragging_out = &n->p;
3543                opposite = &n->n;
3544                n->code = NR_CURVETO;
3545            } else if (appr_p < appr_n) { // closer to n
3546                n->dragging_out = &n->n;
3547                opposite = &n->p;
3548                n->n.other->code = NR_CURVETO;
3549            } else { // p and n nodes are the same
3550                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3551                    n->dragging_out = &n->p;
3552                    opposite = &n->n;
3553                    n->code = NR_CURVETO;
3554                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3555                    n->dragging_out = &n->n;
3556                    opposite = &n->p;
3557                    n->n.other->code = NR_CURVETO;
3558                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3559                    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);
3560                    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);
3561                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3562                        n->dragging_out = &n->n;
3563                        opposite = &n->p;
3564                        n->n.other->code = NR_CURVETO;
3565                    } else { // closer to other's n handle
3566                        n->dragging_out = &n->p;
3567                        opposite = &n->n;
3568                        n->code = NR_CURVETO;
3569                    }
3570                }
3571            }
3573            // if there's another handle, make sure the one we drag out starts parallel to it
3574            if (opposite->pos != n->pos) {
3575                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3576            }
3578            // knots might not be created yet!
3579            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3580            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3581        }
3583        // pass this on to the handle-moved callback
3584        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3585        sp_node_update_handles(n);
3586        return TRUE;
3587    }
3589     if (state & GDK_CONTROL_MASK) { // constrained motion
3591         // calculate relative distances of handles
3592         // n handle:
3593         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3594         xn = n->n.pos[NR::X] - n->pos[NR::X];
3595         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3596         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3597             if (n->n.other) { // if there is the next point
3598                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3599                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3600                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3601             }
3602         }
3603         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3604         if (yn < 0) { xn = -xn; yn = -yn; }
3606         // p handle:
3607         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3608         xp = n->p.pos[NR::X] - n->pos[NR::X];
3609         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3610         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3611             if (n->p.other) {
3612                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3613                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3614                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3615             }
3616         }
3617         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3618         if (yp < 0) { xp = -xp; yp = -yp; }
3620         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3621             // sliding on handles, only if at least one of the handles is non-vertical
3622             // (otherwise it's the same as ctrl+drag anyway)
3624             // calculate angles of the handles
3625             if (xn == 0) {
3626                 if (yn == 0) { // no handle, consider it the continuation of the other one
3627                     an = 0;
3628                     collinear = TRUE;
3629                 }
3630                 else an = 0; // vertical; set the angle to horizontal
3631             } else an = yn/xn;
3633             if (xp == 0) {
3634                 if (yp == 0) { // no handle, consider it the continuation of the other one
3635                     ap = an;
3636                 }
3637                 else ap = 0; // vertical; set the angle to horizontal
3638             } else  ap = yp/xp;
3640             if (collinear) an = ap;
3642             // angles of the perpendiculars; HUGE_VAL means vertical
3643             if (an == 0) na = HUGE_VAL; else na = -1/an;
3644             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3646             // mouse point relative to the node's original pos
3647             pr = (*p) - n->origin;
3649             // distances to the four lines (two handles and two perpendiculars)
3650             d_an = point_line_distance(&pr, an);
3651             d_na = point_line_distance(&pr, na);
3652             d_ap = point_line_distance(&pr, ap);
3653             d_pa = point_line_distance(&pr, pa);
3655             // find out which line is the closest, save its closest point in c
3656             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3657                 point_line_closest(&pr, an, &c);
3658             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3659                 point_line_closest(&pr, ap, &c);
3660             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3661                 point_line_closest(&pr, na, &c);
3662             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3663                 point_line_closest(&pr, pa, &c);
3664             }
3666             // move the node to the closest point
3667             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3668                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3669                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3670                                             true);
3672         } else {  // constraining to hor/vert
3674             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3675                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3676                                                 (*p)[NR::X] - n->pos[NR::X], 
3677                                                 n->origin[NR::Y] - n->pos[NR::Y],
3678                                                 true, 
3679                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3680             } else { // snap to vert
3681                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3682                                                 n->origin[NR::X] - n->pos[NR::X],
3683                                                 (*p)[NR::Y] - n->pos[NR::Y],
3684                                                 true,
3685                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3686             }
3687         }
3688     } else { // move freely
3689         if (n->is_dragging) {
3690             if (state & GDK_MOD1_MASK) { // sculpt
3691                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3692             } else {
3693                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3694                                             (*p)[NR::X] - n->pos[NR::X],
3695                                             (*p)[NR::Y] - n->pos[NR::Y],
3696                                             (state & GDK_SHIFT_MASK) == 0);
3697             }
3698         }
3699     }
3701     n->subpath->nodepath->desktop->scroll_to_point(p);
3703     return TRUE;
3706 /**
3707  * Node handle clicked callback.
3708  */
3709 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3711    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3713     if (state & GDK_CONTROL_MASK) { // "delete" handle
3714         if (n->p.knot == knot) {
3715             n->p.pos = n->pos;
3716         } else if (n->n.knot == knot) {
3717             n->n.pos = n->pos;
3718         }
3719         sp_node_update_handles(n);
3720         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3721         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3722         sp_nodepath_update_statusbar(nodepath);
3724     } else { // just select or add to selection, depending in Shift
3725         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3726     }
3729 /**
3730  * Node handle grabbed callback.
3731  */
3732 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3734    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3736     if (!n->selected) {
3737         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3738     }
3740     // remember the origin point of the handle
3741     if (n->p.knot == knot) {
3742         n->p.origin_radial = n->p.pos - n->pos;
3743     } else if (n->n.knot == knot) {
3744         n->n.origin_radial = n->n.pos - n->pos;
3745     } else {
3746         g_assert_not_reached();
3747     }
3749     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3752 /**
3753  * Node handle ungrabbed callback.
3754  */
3755 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3757    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3759     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3760     if (n->p.knot == knot) {
3761         n->p.origin_radial.a = 0;
3762         sp_knot_set_position(knot, n->p.pos, state);
3763     } else if (n->n.knot == knot) {
3764         n->n.origin_radial.a = 0;
3765         sp_knot_set_position(knot, n->n.pos, state);
3766     } else {
3767         g_assert_not_reached();
3768     }
3770     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3773 /**
3774  * Node handle "request" signal callback.
3775  */
3776 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3778     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3780     Inkscape::NodePath::NodeSide *me, *opposite;
3781     gint which;
3782     if (n->p.knot == knot) {
3783         me = &n->p;
3784         opposite = &n->n;
3785         which = -1;
3786     } else if (n->n.knot == knot) {
3787         me = &n->n;
3788         opposite = &n->p;
3789         which = 1;
3790     } else {
3791         me = opposite = NULL;
3792         which = 0;
3793         g_assert_not_reached();
3794     }
3796     SPDesktop *desktop = n->subpath->nodepath->desktop;
3797     SnapManager &m = desktop->namedview->snap_manager;
3798     m.setup(desktop, n->subpath->nodepath->item);
3799     Inkscape::SnappedPoint s;
3800     
3801     if ((state & GDK_SHIFT_MASK) != 0) {
3802         // We will not try to snap when the shift-key is pressed
3803         // so remove the old snap indicator and don't wait for it to time-out  
3804         desktop->snapindicator->remove_snappoint();     
3805     }
3807     Inkscape::NodePath::Node *othernode = opposite->other;
3808     if (othernode) {
3809         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3810             /* We are smooth node adjacent with line */
3811             NR::Point const delta = *p - n->pos;
3812             NR::Coord const len = NR::L2(delta);
3813             Inkscape::NodePath::Node *othernode = opposite->other;
3814             NR::Point const ndelta = n->pos - othernode->pos;
3815             NR::Coord const linelen = NR::L2(ndelta);
3816             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3817                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3818                 (*p) = n->pos + (scal / linelen) * ndelta;
3819             }
3820             if ((state & GDK_SHIFT_MASK) == 0) {
3821                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3822             }
3823         } else {
3824                 if ((state & GDK_SHIFT_MASK) == 0) {
3825                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3826                 }
3827         }
3828     } else {
3829         if ((state & GDK_SHIFT_MASK) == 0) {
3830                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3831         }
3832     }
3833     
3834     s.getPoint(*p);
3835     
3836     sp_node_adjust_handle(n, -which);
3838     return FALSE;
3841 /**
3842  * Node handle moved callback.
3843  */
3844 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3846    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3848    Inkscape::NodePath::NodeSide *me;
3849    Inkscape::NodePath::NodeSide *other;
3850     if (n->p.knot == knot) {
3851         me = &n->p;
3852         other = &n->n;
3853     } else if (n->n.knot == knot) {
3854         me = &n->n;
3855         other = &n->p;
3856     } else {
3857         me = NULL;
3858         other = NULL;
3859         g_assert_not_reached();
3860     }
3862     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3863     Radial rme(me->pos - n->pos);
3864     Radial rother(other->pos - n->pos);
3865     Radial rnew(*p - n->pos);
3867     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3868         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3869         /* 0 interpreted as "no snapping". */
3871         // 1. Snap to the closest PI/snaps angle, starting from zero.
3872         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3874         // 2. Snap to the original angle, its opposite and perpendiculars
3875         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3876             /* The closest PI/2 angle, starting from original angle */
3877             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3879             // Snap to the closest.
3880             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3881                        ? a_snapped
3882                        : a_ortho );
3883         }
3885         // 3. Snap to the angle of the opposite line, if any
3886         Inkscape::NodePath::Node *othernode = other->other;
3887         if (othernode) {
3888             NR::Point other_to_snap(0,0);
3889             if (sp_node_side_is_line(n, other)) {
3890                 other_to_snap = othernode->pos - n->pos;
3891             } else {
3892                 other_to_snap = other->pos - n->pos;
3893             }
3894             if (NR::L2(other_to_snap) > 1e-3) {
3895                 Radial rother_to_snap(other_to_snap);
3896                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3897                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3899                 // Snap to the closest.
3900                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3901                        ? a_snapped
3902                        : a_oppo );
3903             }
3904         }
3906         rnew.a = a_snapped;
3907     }
3909     if (state & GDK_MOD1_MASK) {
3910         // lock handle length
3911         rnew.r = me->origin_radial.r;
3912     }
3914     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3915         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3916         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3917         rother.a += rnew.a - rme.a;
3918         other->pos = NR::Point(rother) + n->pos;
3919         if (other->knot) {
3920             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3921             sp_knot_moveto(other->knot, other->pos);
3922         }
3923     }
3925     me->pos = NR::Point(rnew) + n->pos;
3926     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3928     // move knot, but without emitting the signal:
3929     // we cannot emit a "moved" signal because we're now processing it
3930     sp_knot_moveto(me->knot, me->pos);
3932     update_object(n->subpath->nodepath);
3934     /* status text */
3935     SPDesktop *desktop = n->subpath->nodepath->desktop;
3936     if (!desktop) return;
3937     SPEventContext *ec = desktop->event_context;
3938     if (!ec) return;
3939     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3940     if (!mc) return;
3942     double degrees = 180 / M_PI * rnew.a;
3943     if (degrees > 180) degrees -= 360;
3944     if (degrees < -180) degrees += 360;
3945     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3946         degrees = angle_to_compass (degrees);
3948     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3950     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3951          _("<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);
3953     g_string_free(length, TRUE);
3956 /**
3957  * Node handle event callback.
3958  */
3959 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3961     gboolean ret = FALSE;
3962     switch (event->type) {
3963         case GDK_KEY_PRESS:
3964             switch (get_group0_keyval (&event->key)) {
3965                 case GDK_space:
3966                     if (event->key.state & GDK_BUTTON1_MASK) {
3967                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3968                         stamp_repr(nodepath);
3969                         ret = TRUE;
3970                     }
3971                     break;
3972                 default:
3973                     break;
3974             }
3975             break;
3976         case GDK_ENTER_NOTIFY:
3977             // we use an experimentally determined threshold that seems to work fine
3978             if (NR::L2(n->pos - knot->pos) < 0.75)
3979                 Inkscape::NodePath::Path::active_node = n;
3980             break;
3981         case GDK_LEAVE_NOTIFY:
3982             // we use an experimentally determined threshold that seems to work fine
3983             if (NR::L2(n->pos - knot->pos) < 0.75)
3984                 Inkscape::NodePath::Path::active_node = NULL;
3985             break;
3986         default:
3987             break;
3988     }
3990     return ret;
3993 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3994                                  Radial &rme, Radial &rother, gboolean const both)
3996     rme.a += angle;
3997     if ( both
3998          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3999          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4000     {
4001         rother.a += angle;
4002     }
4005 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4006                                         Radial &rme, Radial &rother, gboolean const both)
4008     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4010     gdouble r;
4011     if ( both
4012          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4013          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4014     {
4015         r = MAX(rme.r, rother.r);
4016     } else {
4017         r = rme.r;
4018     }
4020     gdouble const weird_angle = atan2(norm_angle, r);
4021 /* Bulia says norm_angle is just the visible distance that the
4022  * object's end must travel on the screen.  Left as 'angle' for want of
4023  * a better name.*/
4025     rme.a += weird_angle;
4026     if ( both
4027          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4028          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4029     {
4030         rother.a += weird_angle;
4031     }
4034 /**
4035  * Rotate one node.
4036  */
4037 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4039     Inkscape::NodePath::NodeSide *me, *other;
4040     bool both = false;
4042     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4043     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4045     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4046         me = &(n->p);
4047         other = &(n->n);
4048     } else if (!n->p.other) {
4049         me = &(n->n);
4050         other = &(n->p);
4051     } else {
4052         if (which > 0) { // right handle
4053             if (xn > xp) {
4054                 me = &(n->n);
4055                 other = &(n->p);
4056             } else {
4057                 me = &(n->p);
4058                 other = &(n->n);
4059             }
4060         } else if (which < 0){ // left handle
4061             if (xn <= xp) {
4062                 me = &(n->n);
4063                 other = &(n->p);
4064             } else {
4065                 me = &(n->p);
4066                 other = &(n->n);
4067             }
4068         } else { // both handles
4069             me = &(n->n);
4070             other = &(n->p);
4071             both = true;
4072         }
4073     }
4075     Radial rme(me->pos - n->pos);
4076     Radial rother(other->pos - n->pos);
4078     if (screen) {
4079         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4080     } else {
4081         node_rotate_one_internal (*n, angle, rme, rother, both);
4082     }
4084     me->pos = n->pos + NR::Point(rme);
4086     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4087         other->pos =  n->pos + NR::Point(rother);
4088     }
4090     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4091     // so here we just move all the knots without emitting move signals, for speed
4092     sp_node_update_handles(n, false);
4095 /**
4096  * Rotate selected nodes.
4097  */
4098 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4100     if (!nodepath || !nodepath->selected) return;
4102     if (g_list_length(nodepath->selected) == 1) {
4103        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4104         node_rotate_one (n, angle, which, screen);
4105     } else {
4106        // rotate as an object:
4108         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4109         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4110         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4111             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4112             box.expandTo (n->pos); // contain all selected nodes
4113         }
4115         gdouble rot;
4116         if (screen) {
4117             gdouble const zoom = nodepath->desktop->current_zoom();
4118             gdouble const zmove = angle / zoom;
4119             gdouble const r = NR::L2(box.max() - box.midpoint());
4120             rot = atan2(zmove, r);
4121         } else {
4122             rot = angle;
4123         }
4125         NR::Point rot_center;
4126         if (Inkscape::NodePath::Path::active_node == NULL)
4127             rot_center = box.midpoint();
4128         else
4129             rot_center = Inkscape::NodePath::Path::active_node->pos;
4131         NR::Matrix t =
4132             NR::Matrix (NR::translate(-rot_center)) *
4133             NR::Matrix (NR::rotate(rot)) *
4134             NR::Matrix (NR::translate(rot_center));
4136         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4137             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4138             n->pos *= t;
4139             n->n.pos *= t;
4140             n->p.pos *= t;
4141             sp_node_update_handles(n, false);
4142         }
4143     }
4145     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4148 /**
4149  * Scale one node.
4150  */
4151 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4153     bool both = false;
4154     Inkscape::NodePath::NodeSide *me, *other;
4156     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4157     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4159     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4160         me = &(n->p);
4161         other = &(n->n);
4162         n->code = NR_CURVETO;
4163     } else if (!n->p.other) {
4164         me = &(n->n);
4165         other = &(n->p);
4166         if (n->n.other)
4167             n->n.other->code = NR_CURVETO;
4168     } else {
4169         if (which > 0) { // right handle
4170             if (xn > xp) {
4171                 me = &(n->n);
4172                 other = &(n->p);
4173                 if (n->n.other)
4174                     n->n.other->code = NR_CURVETO;
4175             } else {
4176                 me = &(n->p);
4177                 other = &(n->n);
4178                 n->code = NR_CURVETO;
4179             }
4180         } else if (which < 0){ // left handle
4181             if (xn <= xp) {
4182                 me = &(n->n);
4183                 other = &(n->p);
4184                 if (n->n.other)
4185                     n->n.other->code = NR_CURVETO;
4186             } else {
4187                 me = &(n->p);
4188                 other = &(n->n);
4189                 n->code = NR_CURVETO;
4190             }
4191         } else { // both handles
4192             me = &(n->n);
4193             other = &(n->p);
4194             both = true;
4195             n->code = NR_CURVETO;
4196             if (n->n.other)
4197                 n->n.other->code = NR_CURVETO;
4198         }
4199     }
4201     Radial rme(me->pos - n->pos);
4202     Radial rother(other->pos - n->pos);
4204     rme.r += grow;
4205     if (rme.r < 0) rme.r = 0;
4206     if (rme.a == HUGE_VAL) {
4207         if (me->other) { // if direction is unknown, initialize it towards the next node
4208             Radial rme_next(me->other->pos - n->pos);
4209             rme.a = rme_next.a;
4210         } else { // if there's no next, initialize to 0
4211             rme.a = 0;
4212         }
4213     }
4214     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4215         rother.r += grow;
4216         if (rother.r < 0) rother.r = 0;
4217         if (rother.a == HUGE_VAL) {
4218             rother.a = rme.a + M_PI;
4219         }
4220     }
4222     me->pos = n->pos + NR::Point(rme);
4224     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4225         other->pos = n->pos + NR::Point(rother);
4226     }
4228     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4229     // so here we just move all the knots without emitting move signals, for speed
4230     sp_node_update_handles(n, false);
4233 /**
4234  * Scale selected nodes.
4235  */
4236 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4238     if (!nodepath || !nodepath->selected) return;
4240     if (g_list_length(nodepath->selected) == 1) {
4241         // scale handles of the single selected node
4242         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4243         node_scale_one (n, grow, which);
4244     } else {
4245         // scale nodes as an "object":
4247         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4248         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4249         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4250             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4251             box.expandTo (n->pos); // contain all selected nodes
4252         }
4254         double scale = (box.maxExtent() + grow)/box.maxExtent();
4256         NR::Point scale_center;
4257         if (Inkscape::NodePath::Path::active_node == NULL)
4258             scale_center = box.midpoint();
4259         else
4260             scale_center = Inkscape::NodePath::Path::active_node->pos;
4262         NR::Matrix t =
4263             NR::Matrix (NR::translate(-scale_center)) *
4264             NR::Matrix (NR::scale(scale, scale)) *
4265             NR::Matrix (NR::translate(scale_center));
4267         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4268             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4269             n->pos *= t;
4270             n->n.pos *= t;
4271             n->p.pos *= t;
4272             sp_node_update_handles(n, false);
4273         }
4274     }
4276     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4279 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4281     if (!nodepath) return;
4282     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4285 /**
4286  * Flip selected nodes horizontally/vertically.
4287  */
4288 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4290     if (!nodepath || !nodepath->selected) return;
4292     if (g_list_length(nodepath->selected) == 1 && !center) {
4293         // flip handles of the single selected node
4294         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4295         double temp = n->p.pos[axis];
4296         n->p.pos[axis] = n->n.pos[axis];
4297         n->n.pos[axis] = temp;
4298         sp_node_update_handles(n, false);
4299     } else {
4300         // scale nodes as an "object":
4302         NR::Rect box = sp_node_selected_bbox (nodepath);
4303         if (!center) {
4304             center = box.midpoint();
4305         }
4306         NR::Matrix t =
4307             NR::Matrix (NR::translate(- *center)) *
4308             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4309             NR::Matrix (NR::translate(*center));
4311         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4312             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4313             n->pos *= t;
4314             n->n.pos *= t;
4315             n->p.pos *= t;
4316             sp_node_update_handles(n, false);
4317         }
4318     }
4320     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4323 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4325     g_assert (nodepath->selected);
4327     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4328     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4329     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4330         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4331         box.expandTo (n->pos); // contain all selected nodes
4332     }
4333     return box;
4336 //-----------------------------------------------
4337 /**
4338  * Return new subpath under given nodepath.
4339  */
4340 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4342     g_assert(nodepath);
4343     g_assert(nodepath->desktop);
4345    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4347     s->nodepath = nodepath;
4348     s->closed = FALSE;
4349     s->nodes = NULL;
4350     s->first = NULL;
4351     s->last = NULL;
4353     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4354     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4355     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4357     return s;
4360 /**
4361  * Destroy nodes in subpath, then subpath itself.
4362  */
4363 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4365     g_assert(subpath);
4366     g_assert(subpath->nodepath);
4367     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4369     while (subpath->nodes) {
4370         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4371     }
4373     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4375     g_free(subpath);
4378 /**
4379  * Link head to tail in subpath.
4380  */
4381 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4383     g_assert(!sp->closed);
4384     g_assert(sp->last != sp->first);
4385     g_assert(sp->first->code == NR_MOVETO);
4387     sp->closed = TRUE;
4389     //Link the head to the tail
4390     sp->first->p.other = sp->last;
4391     sp->last->n.other  = sp->first;
4392     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4393     sp->first          = sp->last;
4395     //Remove the extra end node
4396     sp_nodepath_node_destroy(sp->last->n.other);
4399 /**
4400  * Open closed (loopy) subpath at node.
4401  */
4402 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4404     g_assert(sp->closed);
4405     g_assert(n->subpath == sp);
4406     g_assert(sp->first == sp->last);
4408     /* We create new startpoint, current node will become last one */
4410    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4411                                                 &n->pos, &n->pos, &n->n.pos);
4414     sp->closed        = FALSE;
4416     //Unlink to make a head and tail
4417     sp->first         = new_path;
4418     sp->last          = n;
4419     n->n.other        = NULL;
4420     new_path->p.other = NULL;
4423 /**
4424  * Return new node in subpath with given properties.
4425  * \param pos Position of node.
4426  * \param ppos Handle position in previous direction
4427  * \param npos Handle position in previous direction
4428  */
4429 Inkscape::NodePath::Node *
4430 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)
4432     g_assert(sp);
4433     g_assert(sp->nodepath);
4434     g_assert(sp->nodepath->desktop);
4436     if (nodechunk == NULL)
4437         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4439     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4441     n->subpath  = sp;
4443     if (type != Inkscape::NodePath::NODE_NONE) {
4444         // use the type from sodipodi:nodetypes
4445         n->type = type;
4446     } else {
4447         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4448             // points are (almost) collinear
4449             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4450                 // endnode, or a node with a retracted handle
4451                 n->type = Inkscape::NodePath::NODE_CUSP;
4452             } else {
4453                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4454             }
4455         } else {
4456             n->type = Inkscape::NodePath::NODE_CUSP;
4457         }
4458     }
4460     n->code     = code;
4461     n->selected = FALSE;
4462     n->pos      = *pos;
4463     n->p.pos    = *ppos;
4464     n->n.pos    = *npos;
4466     n->dragging_out = NULL;
4468     Inkscape::NodePath::Node *prev;
4469     if (next) {
4470         //g_assert(g_list_find(sp->nodes, next));
4471         prev = next->p.other;
4472     } else {
4473         prev = sp->last;
4474     }
4476     if (prev)
4477         prev->n.other = n;
4478     else
4479         sp->first = n;
4481     if (next)
4482         next->p.other = n;
4483     else
4484         sp->last = n;
4486     n->p.other = prev;
4487     n->n.other = next;
4489     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"));
4490     sp_knot_set_position(n->knot, *pos, 0);
4492     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4493     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4494     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4495     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4496     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4497     sp_knot_update_ctrl(n->knot);
4499     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4500     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4501     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4502     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4503     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4504     sp_knot_show(n->knot);
4506     // We only create handle knots and lines on demand
4507     n->p.knot = NULL;
4508     n->p.line = NULL;
4509     n->n.knot = NULL;
4510     n->n.line = NULL;
4512     sp->nodes = g_list_prepend(sp->nodes, n);
4514     return n;
4517 /**
4518  * Destroy node and its knots, link neighbors in subpath.
4519  */
4520 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4522     g_assert(node);
4523     g_assert(node->subpath);
4524     g_assert(SP_IS_KNOT(node->knot));
4526    Inkscape::NodePath::SubPath *sp = node->subpath;
4528     if (node->selected) { // first, deselect
4529         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4530         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4531     }
4533     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4535     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4536     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4537     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4538     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4539     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4540     g_object_unref(G_OBJECT(node->knot));
4542     if (node->p.knot) {
4543         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4544         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4545         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4546         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4547         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4548         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4549         g_object_unref(G_OBJECT(node->p.knot));
4550         node->p.knot = NULL;
4551     }
4553     if (node->n.knot) {
4554         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4555         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4556         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4557         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4558         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4559         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4560         g_object_unref(G_OBJECT(node->n.knot));
4561         node->n.knot = NULL;
4562     }
4564     if (node->p.line)
4565         gtk_object_destroy(GTK_OBJECT(node->p.line));
4566     if (node->n.line)
4567         gtk_object_destroy(GTK_OBJECT(node->n.line));
4569     if (sp->nodes) { // there are others nodes on the subpath
4570         if (sp->closed) {
4571             if (sp->first == node) {
4572                 g_assert(sp->last == node);
4573                 sp->first = node->n.other;
4574                 sp->last = sp->first;
4575             }
4576             node->p.other->n.other = node->n.other;
4577             node->n.other->p.other = node->p.other;
4578         } else {
4579             if (sp->first == node) {
4580                 sp->first = node->n.other;
4581                 sp->first->code = NR_MOVETO;
4582             }
4583             if (sp->last == node) sp->last = node->p.other;
4584             if (node->p.other) node->p.other->n.other = node->n.other;
4585             if (node->n.other) node->n.other->p.other = node->p.other;
4586         }
4587     } else { // this was the last node on subpath
4588         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4589     }
4591     g_mem_chunk_free(nodechunk, node);
4594 /**
4595  * Returns one of the node's two sides.
4596  * \param which Indicates which side.
4597  * \return Pointer to previous node side if which==-1, next if which==1.
4598  */
4599 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4601     g_assert(node);
4603     switch (which) {
4604         case -1:
4605             return &node->p;
4606         case 1:
4607             return &node->n;
4608         default:
4609             break;
4610     }
4612     g_assert_not_reached();
4614     return NULL;
4617 /**
4618  * Return the other side of the node, given one of its sides.
4619  */
4620 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4622     g_assert(node);
4624     if (me == &node->p) return &node->n;
4625     if (me == &node->n) return &node->p;
4627     g_assert_not_reached();
4629     return NULL;
4632 /**
4633  * Return NRPathcode on the given side of the node.
4634  */
4635 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4637     g_assert(node);
4639     if (me == &node->p) {
4640         if (node->p.other) return (NRPathcode)node->code;
4641         return NR_MOVETO;
4642     }
4644     if (me == &node->n) {
4645         if (node->n.other) return (NRPathcode)node->n.other->code;
4646         return NR_MOVETO;
4647     }
4649     g_assert_not_reached();
4651     return NR_END;
4654 /**
4655  * Return node with the given index
4656  */
4657 Inkscape::NodePath::Node *
4658 sp_nodepath_get_node_by_index(int index)
4660     Inkscape::NodePath::Node *e = NULL;
4662     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4663     if (!nodepath) {
4664         return e;
4665     }
4667     //find segment
4668     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4670         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4671         int n = g_list_length(sp->nodes);
4672         if (sp->closed) {
4673             n++;
4674         }
4676         //if the piece belongs to this subpath grab it
4677         //otherwise move onto the next subpath
4678         if (index < n) {
4679             e = sp->first;
4680             for (int i = 0; i < index; ++i) {
4681                 e = e->n.other;
4682             }
4683             break;
4684         } else {
4685             if (sp->closed) {
4686                 index -= (n+1);
4687             } else {
4688                 index -= n;
4689             }
4690         }
4691     }
4693     return e;
4696 /**
4697  * Returns plain text meaning of node type.
4698  */
4699 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4701     unsigned retracted = 0;
4702     bool endnode = false;
4704     for (int which = -1; which <= 1; which += 2) {
4705         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4706         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4707             retracted ++;
4708         if (!side->other)
4709             endnode = true;
4710     }
4712     if (retracted == 0) {
4713         if (endnode) {
4714                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4715                 return _("end node");
4716         } else {
4717             switch (node->type) {
4718                 case Inkscape::NodePath::NODE_CUSP:
4719                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4720                     return _("cusp");
4721                 case Inkscape::NodePath::NODE_SMOOTH:
4722                     // TRANSLATORS: "smooth" is an adjective here
4723                     return _("smooth");
4724                 case Inkscape::NodePath::NODE_SYMM:
4725                     return _("symmetric");
4726             }
4727         }
4728     } else if (retracted == 1) {
4729         if (endnode) {
4730             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4731             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4732         } else {
4733             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4734         }
4735     } else {
4736         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4737     }
4739     return NULL;
4742 /**
4743  * Handles content of statusbar as long as node tool is active.
4744  */
4745 void
4746 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4748     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");
4749     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4751     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4752     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4753     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4754     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4756     SPDesktop *desktop = NULL;
4757     if (nodepath) {
4758         desktop = nodepath->desktop;
4759     } else {
4760         desktop = SP_ACTIVE_DESKTOP;
4761     }
4763     SPEventContext *ec = desktop->event_context;
4764     if (!ec) return;
4765     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4766     if (!mc) return;
4768     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4770     if (selected_nodes == 0) {
4771         Inkscape::Selection *sel = desktop->selection;
4772         if (!sel || sel->isEmpty()) {
4773             mc->setF(Inkscape::NORMAL_MESSAGE,
4774                      _("Select a single object to edit its nodes or handles."));
4775         } else {
4776             if (nodepath) {
4777             mc->setF(Inkscape::NORMAL_MESSAGE,
4778                      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.",
4779                               "<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.",
4780                               total_nodes),
4781                      total_nodes);
4782             } else {
4783                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4784                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4785                 } else {
4786                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4787                 }
4788             }
4789         }
4790     } else if (nodepath && selected_nodes == 1) {
4791         mc->setF(Inkscape::NORMAL_MESSAGE,
4792                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4793                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4794                           total_nodes),
4795                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4796     } else {
4797         if (selected_subpaths > 1) {
4798             mc->setF(Inkscape::NORMAL_MESSAGE,
4799                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4800                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4801                               total_nodes),
4802                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4803         } else {
4804             mc->setF(Inkscape::NORMAL_MESSAGE,
4805                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4806                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4807                               total_nodes),
4808                      selected_nodes, total_nodes, when_selected);
4809         }
4810     }
4813 /*
4814  * returns a *copy* of the curve of that object.
4815  */
4816 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4817     if (!object)
4818         return NULL;
4820     SPCurve *curve = NULL;
4821     if (SP_IS_PATH(object)) {
4822         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4823         curve = curve_new->copy();
4824     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4825         const gchar *svgd = object->repr->attribute(key);
4826         if (svgd) {
4827             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4828             SPCurve *curve_new = new SPCurve(pv);
4829             if (curve_new) {
4830                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4831             }
4832         }
4833     }
4835     return curve;
4838 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4839     if (!np || !np->object || !curve)
4840         return;
4842     if (SP_IS_PATH(np->object)) {
4843         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4844             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4845         } else {
4846             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4847         }
4848     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4849         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4850         if (pathparam) {
4851             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4852             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4853         }
4854     }
4857 /**
4858 SPCanvasItem *
4859 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4860     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4862 **/
4864 /**
4865 SPCanvasItem *
4866 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4867     SPCurve *flash_curve = curve->copy();
4868     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4869     flash_curve->transform(i2d);
4870     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4871     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4872     // unless we also flash the nodes...
4873     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4874     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4875     sp_canvas_item_show(canvasitem);
4876     flash_curve->unref();
4877     return canvasitem;
4880 SPCanvasItem *
4881 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4882     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4883                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4885 **/
4887 SPCanvasItem *
4888 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4889     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4890     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4891     flash_curve->transform(i2d);
4892     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4893     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4894     // unless we also flash the nodes...
4895     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4896     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4897     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4898     sp_canvas_item_show(canvasitem);
4899     flash_curve->unref();
4900     return canvasitem;
4903 // TODO: Merge this with sp_nodepath_make_helper_item()!
4904 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4905     np->show_helperpath = show;
4907     if (show) {
4908         SPCurve *helper_curve = np->curve->copy();
4909         helper_curve->transform(to_2geom(np->i2d));
4910         if (!np->helper_path) {
4911             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4913             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4914             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);
4915             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4916             sp_canvas_item_move_to_z(np->helper_path, 0);
4917             sp_canvas_item_show(np->helper_path);
4918         } else {
4919             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4920         }
4921         helper_curve->unref();
4922     } else {
4923         if (np->helper_path) {
4924             GtkObject *temp = np->helper_path;
4925             np->helper_path = NULL;
4926             gtk_object_destroy(temp);
4927         }
4928     }
4931 /* sp_nodepath_make_straight_path:
4932  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4933  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4934  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4935  */
4936 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4937     np->straight_path = true;
4938     np->show_handles = false;
4939     g_message("add code to make the path straight.");
4940     // do sp_nodepath_convert_node_type on all nodes?
4941     // coding tip: search for this text : "Make selected segments lines"
4944 /* convert all curve types to LineSegment or CubicBezier, because nodepath cannot handle other segment types */
4945 Geom::PathVector sp_nodepath_sanitize_path(Geom::PathVector const &pathv_in) {
4946     Geom::PathVector pathv;
4948     for (Geom::PathVector::const_iterator pit = pathv_in.begin(); pit != pathv_in.end(); ++pit) {
4949         pathv.push_back( Geom::Path() );
4950         Geom::Path &newpath = pathv.back();
4951         newpath.start(pit->initialPoint());
4952         newpath.close(pit->closed());
4954         for (Geom::Path::const_iterator c = pit->begin(); c != pit->end_open(); ++c) {
4955             if( dynamic_cast<Geom::CubicBezier const*>(&*c) ||
4956                 dynamic_cast<Geom::LineSegment const*>(&*c) ||
4957                 dynamic_cast<Geom::HLineSegment const*>(&*c) ||
4958                 dynamic_cast<Geom::VLineSegment const*>(&*c) )
4959             {
4960                 newpath.append(*c);
4961             }
4962             else {
4963                 //this case handles sbasis as well as all other curve types
4964                 Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c->toSBasis(), 0.1);
4965                 newpath.append(sbasis_path);
4966             }
4967         }
4968     }
4970     return pathv;
4973 /*
4974   Local Variables:
4975   mode:c++
4976   c-file-style:"stroustrup"
4977   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4978   indent-tabs-mode:nil
4979   fill-column:99
4980   End:
4981 */
4982 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :