Code

Get rid of a whole bunch of further instances of SP_ACTIVE_DESKTOP (where the desktop...
[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 "helper/geom.h"
30 #include "knot.h"
31 #include "inkscape.h"
32 #include "document.h"
33 #include "sp-namedview.h"
34 #include "desktop.h"
35 #include "desktop-handles.h"
36 #include "snap.h"
37 #include "message-stack.h"
38 #include "message-context.h"
39 #include "node-context.h"
40 #include "shape-editor.h"
41 #include "selection-chemistry.h"
42 #include "selection.h"
43 #include "xml/repr.h"
44 #include "prefs-utils.h"
45 #include "sp-metrics.h"
46 #include "sp-path.h"
47 #include "libnr/nr-matrix-ops.h"
48 #include "svg/svg.h"
49 #include "verbs.h"
50 #include "display/bezier-utils.h"
51 #include <vector>
52 #include <algorithm>
53 #include <cstring>
54 #include <cmath>
55 #include <string>
56 #include "live_effects/lpeobject.h"
57 #include "live_effects/lpeobject-reference.h"
58 #include "live_effects/effect.h"
59 #include "live_effects/parameter/parameter.h"
60 #include "live_effects/parameter/path.h"
61 #include "util/mathfns.h"
62 #include "display/snap-indicator.h"
63 #include "snapped-point.h"
65 class NR::Matrix;
67 /// \todo
68 /// evil evil evil. FIXME: conflict of two different Path classes!
69 /// There is a conflict in the namespace between two classes named Path.
70 /// #include "sp-flowtext.h"
71 /// #include "sp-flowregion.h"
73 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
74 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
75 GType sp_flowregion_get_type (void);
76 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
77 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
78 GType sp_flowtext_get_type (void);
79 // end evil workaround
81 #include "helper/stlport.h"
84 /// \todo fixme: Implement these via preferences */
86 #define NODE_FILL          0xbfbfbf00
87 #define NODE_STROKE        0x000000ff
88 #define NODE_FILL_HI       0xff000000
89 #define NODE_STROKE_HI     0x000000ff
90 #define NODE_FILL_SEL      0x0000ffff
91 #define NODE_STROKE_SEL    0x000000ff
92 #define NODE_FILL_SEL_HI   0xff000000
93 #define NODE_STROKE_SEL_HI 0x000000ff
94 #define KNOT_FILL          0xffffffff
95 #define KNOT_STROKE        0x000000ff
96 #define KNOT_FILL_HI       0xff000000
97 #define KNOT_STROKE_HI     0x000000ff
99 static GMemChunk *nodechunk = NULL;
101 /* Creation from object */
103 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
104 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
106 /* Object updating */
108 static void stamp_repr(Inkscape::NodePath::Path *np);
109 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
110 static gchar *create_typestr(Inkscape::NodePath::Path *np);
112 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
114 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
116 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
118 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
120 /* Adjust handle placement, if the node or the other handle is moved */
121 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
122 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
124 /* Node event callbacks */
125 static void node_clicked(SPKnot *knot, guint state, gpointer data);
126 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
127 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
128 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 /* Handle event callbacks */
131 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
132 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
134 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
135 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
138 /* Constructors and destructors */
140 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
141 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
142 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
143 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
144 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
145                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
146 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
148 /* Helpers */
150 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
151 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
152 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
154 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
155 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
157 // active_node indicates mouseover node
158 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
160 static SPCanvasItem *
161 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
162     SPCurve *helper_curve = curve->copy();
163     helper_curve->transform(np->i2d);
164     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
165     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
166     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
167     sp_canvas_item_move_to_z(helper_path, 0);
168     if (show) {
169         sp_canvas_item_show(helper_path);
170     }
171     helper_curve->unref();
172     return helper_path;
175 static SPCanvasItem *
176 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
177     SPCurve *helper_curve = new SPCurve(pathv);
178     return sp_nodepath_make_helper_item(np, helper_curve, show);
181 static void
182 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
183     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
184     if (!SP_IS_LPE_ITEM(np->item)) {
185         g_print ("Only LPEItems can have helperpaths!\n");
186         return;
187     }
189     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
190     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
191     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
192         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
193         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
194         // create new canvas items from the effect's helper paths
195         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
196         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
197             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
198         }
199     }
202 void
203 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
204     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
205     if (!SP_IS_LPE_ITEM(np->item)) {
206         g_print ("Only LPEItems can have helperpaths!\n");
207         return;
208     }
210     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
211     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
212     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
213         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
214         /* update canvas items from the effect's helper paths; note that this code relies on the
215          * fact that getHelperPaths() will always return the same number of helperpaths in the same
216          * order as during their creation in sp_nodepath_create_helperpaths
217          */
218         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
219         for (unsigned int j = 0; j < hpaths.size(); ++j) {
220             SPCurve *curve = new SPCurve(hpaths[j]);
221             curve->transform(np->i2d);
222             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
223             curve = curve->unref();
224         }
225     }
228 static void
229 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
230     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
231         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
232             GtkObject *temp = *j;
233             *j = NULL;
234             gtk_object_destroy(temp);
235         }
236     }
240 /**
241  * \brief Creates new nodepath from item
242  */
243 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
245     Inkscape::XML::Node *repr = object->repr;
247     /** \todo
248      * FIXME: remove this. We don't want to edit paths inside flowtext.
249      * Instead we will build our flowtext with cloned paths, so that the
250      * real paths are outside the flowtext and thus editable as usual.
251      */
252     if (SP_IS_FLOWTEXT(object)) {
253         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
254             if SP_IS_FLOWREGION(child) {
255                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
256                 if (grandchild && SP_IS_PATH(grandchild)) {
257                     object = SP_ITEM(grandchild);
258                     break;
259                 }
260             }
261         }
262     }
264     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
266     if (curve == NULL)
267         return NULL;
269     if (curve->get_segment_count() < 1) {
270         curve->unref();
271         return NULL; // prevent crash for one-node paths
272     }
274     //Create new nodepath
275     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
276     if (!np) {
277         curve->unref();
278         return NULL;
279     }
281     // Set defaults
282     np->desktop     = desktop;
283     np->object      = object;
284     np->subpaths    = NULL;
285     np->selected    = NULL;
286     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
287     np->local_change = 0;
288     np->show_handles = show_handles;
289     np->helper_path = NULL;
290     np->helper_path_vec = new HelperPathList;
291     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
292     np->helperpath_width = 1.0;
293     np->curve = curve->copy();
294     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
295     if (SP_IS_LPE_ITEM(object)) {
296         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
297         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
298             np->show_helperpath = true;
299         }            
300     }
301     np->straight_path = false;
302     if (IS_LIVEPATHEFFECT(object) && item) {
303         np->item = item;
304     } else {
305         np->item = SP_ITEM(object);
306     }
308     // we need to update item's transform from the repr here,
309     // because they may be out of sync when we respond
310     // to a change in repr by regenerating nodepath     --bb
311     sp_object_read_attr(SP_OBJECT(np->item), "transform");
313     np->i2d  = sp_item_i2d_affine(np->item);
314     np->d2i  = np->i2d.inverse();
316     np->repr = repr;
317     if (repr_key_in) { // apparantly the object is an LPEObject
318         np->repr_key = g_strdup(repr_key_in);
319         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
320         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
321         if (lpeparam) {
322             lpeparam->param_setup_nodepath(np);
323         }
324     } else {
325         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
326         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
327             np->repr_key = g_strdup("inkscape:original-d");
329             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
330             if (lpe) {
331                 lpe->setup_nodepath(np);
332             }
333         } else {
334             np->repr_key = g_strdup("d");
335         }
336     }
338     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
339      * So for example a closed rectangle has a nodetypestring of length 5.
340      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
341     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
342     np->curve->set_pathvector(pathv_sanitized);
343     guint length = np->curve->get_segment_count();
344     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
345         length += pit->empty() ? 0 : 1;
346     }
348     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
349     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
351     // create the subpath(s) from the bpath
352     subpaths_from_pathvector(np, pathv_sanitized, typestr);
354     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
355     np->subpaths = g_list_reverse(np->subpaths);
357     delete[] typestr;
358     curve->unref();
360     // Draw helper curve
361     if (np->show_helperpath) {
362         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
363     }
365     sp_nodepath_create_helperpaths(np);
367     return np;
370 /**
371  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
372  */
373 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
375     if (!np)  //soft fail, like delete
376         return;
378     while (np->subpaths) {
379         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
380     }
382     //Inform the ShapeEditor that made me, if any, that I am gone.
383     if (np->shape_editor)
384         np->shape_editor->nodepath_destroyed();
386     g_assert(!np->selected);
388     if (np->helper_path) {
389         GtkObject *temp = np->helper_path;
390         np->helper_path = NULL;
391         gtk_object_destroy(temp);
392     }
393     if (np->curve) {
394         np->curve->unref();
395         np->curve = NULL;
396     }
398     if (np->repr_key) {
399         g_free(np->repr_key);
400         np->repr_key = NULL;
401     }
402     if (np->repr_nodetypes_key) {
403         g_free(np->repr_nodetypes_key);
404         np->repr_nodetypes_key = NULL;
405     }
407     sp_nodepath_destroy_helperpaths(np);
408     delete np->helper_path_vec;
409     np->helper_path_vec = NULL;
411     np->desktop = NULL;
413     g_free(np);
416 /**
417  *  Return the node count of a given NodeSubPath.
418  */
419 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
421     if (!subpath)
422         return 0;
423     gint nodeCount = g_list_length(subpath->nodes);
424     return nodeCount;
427 /**
428  *  Return the node count of a given NodePath.
429  */
430 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
432     if (!np)
433         return 0;
434     gint nodeCount = 0;
435     for (GList *item = np->subpaths ; item ; item=item->next) {
436        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
437         nodeCount += g_list_length(subpath->nodes);
438     }
439     return nodeCount;
442 /**
443  *  Return the subpath count of a given NodePath.
444  */
445 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
447     if (!np)
448         return 0;
449     return g_list_length (np->subpaths);
452 /**
453  *  Return the selected node count of a given NodePath.
454  */
455 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
457     if (!np)
458         return 0;
459     return g_list_length (np->selected);
462 /**
463  *  Return the number of subpaths where nodes are selected in a given NodePath.
464  */
465 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
467     if (!np)
468         return 0;
469     if (!np->selected)
470         return 0;
471     if (!np->selected->next)
472         return 1;
473     gint count = 0;
474     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
475         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
476         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
477             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
478             if (node->selected) {
479                 count ++;
480                 break;
481             }
482         }
483     }
484     return count;
487 /**
488  * Clean up a nodepath after editing.
489  *
490  * Currently we are deleting trivial subpaths.
491  */
492 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
494     GList *badSubPaths = NULL;
496     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
497     for (GList *l = nodepath->subpaths; l ; l=l->next) {
498        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
499        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
500             badSubPaths = g_list_append(badSubPaths, sp);
501     }
503     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
504     //also removes the subpath from nodepath->subpaths
505     for (GList *l = badSubPaths; l ; l=l->next) {
506        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
507         sp_nodepath_subpath_destroy(sp);
508     }
510     g_list_free(badSubPaths);
513 /**
514  * Create new nodepaths from pathvector, make it subpaths of np.
515  * \param t The node type array.
516  */
517 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
519     guint i = 0;  // index into node type array
520     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
521         if (pit->empty())
522             continue;  // don't add single knot paths
524         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
526         NR::Point ppos = pit->initialPoint() * (Geom::Matrix)np->i2d;
527         NRPathcode pcode = NR_MOVETO;
529         /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/
530         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
531             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
532                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
533                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
534             {
535                 NR::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
536                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
538                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
539                 pcode = NR_LINETO;
540             }
541             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
542                 std::vector<Geom::Point> points = cubic_bezier->points();
543                 NR::Point pos = points[0] * (Geom::Matrix)np->i2d;
544                 NR::Point npos = points[1] * (Geom::Matrix)np->i2d;
545                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
547                 ppos = points[2] * (Geom::Matrix)np->i2d;
548                 pcode = NR_CURVETO;
549             }
550         }
552         if (pit->closed()) {
553             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
554             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
555              * If the length is zero, don't add it to the nodepath. */
556             Geom::Curve const &closing_seg = pit->back_closed();
557             if ( ! closing_seg.isDegenerate() ) {
558                 NR::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
559                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
560             }
562             sp_nodepath_subpath_close(sp);
563         }
564     }
567 /**
568  * Convert from sodipodi:nodetypes to new style type array.
569  */
570 static
571 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
573     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
575     guint pos = 0;
577     if (types) {
578         for (guint i = 0; types[i] && ( i < length ); i++) {
579             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
580             if (types[i] != '\0') {
581                 switch (types[i]) {
582                     case 's':
583                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
584                         break;
585                     case 'z':
586                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
587                         break;
588                     case 'c':
589                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
590                         break;
591                     default:
592                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
593                         break;
594                 }
595             }
596         }
597     }
599     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
601     return typestr;
604 /**
605  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
606  * updated but repr is not (for speed). Used during curve and node drag.
607  */
608 static void update_object(Inkscape::NodePath::Path *np)
610     g_assert(np);
612     np->curve->unref();
613     np->curve = create_curve(np);
615     sp_nodepath_set_curve(np, np->curve);
617     if (np->show_helperpath) {
618         SPCurve * helper_curve = np->curve->copy();
619         helper_curve->transform(np->i2d);
620         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
621         helper_curve->unref();
622     }
624     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
625     //sp_nodepath_update_helperpaths(np);
627     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
628     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
629     np->shape_editor->update_knotholder();
632 /**
633  * Update XML path node with data from path object.
634  */
635 static void update_repr_internal(Inkscape::NodePath::Path *np)
637     g_assert(np);
639     Inkscape::XML::Node *repr = np->object->repr;
641     np->curve->unref();
642     np->curve = create_curve(np);
644     gchar *typestr = create_typestr(np);
645     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
647     // determine if path has an effect applied and write to correct "d" attribute.
648     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
649         np->local_change++;
650         repr->setAttribute(np->repr_key, svgpath);
651     }
653     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
654         np->local_change++;
655         repr->setAttribute(np->repr_nodetypes_key, typestr);
656     }
658     g_free(svgpath);
659     g_free(typestr);
661     if (np->show_helperpath) {
662         SPCurve * helper_curve = np->curve->copy();
663         helper_curve->transform(np->i2d);
664         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
665         helper_curve->unref();
666     }
668     // TODO: do we need this call here? after all, update_object() should have been called just before
669     //sp_nodepath_update_helperpaths(np);
672 /**
673  * Update XML path node with data from path object, commit changes forever.
674  */
675 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
677     //fixme: np can be NULL, so check before proceeding
678     g_return_if_fail(np != NULL);
680     update_repr_internal(np);
681     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
683     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
684                      annotation);
687 /**
688  * Update XML path node with data from path object, commit changes with undo.
689  */
690 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
692     update_repr_internal(np);
693     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
694                            annotation);
697 /**
698  * Make duplicate of path, replace corresponding XML node in tree, commit.
699  */
700 static void stamp_repr(Inkscape::NodePath::Path *np)
702     g_assert(np);
704     Inkscape::XML::Node *old_repr = np->object->repr;
705     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
707     // remember the position of the item
708     gint pos = old_repr->position();
709     // remember parent
710     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
712     SPCurve *curve = create_curve(np);
713     gchar *typestr = create_typestr(np);
715     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
717     new_repr->setAttribute(np->repr_key, svgpath);
718     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
720     // add the new repr to the parent
721     parent->appendChild(new_repr);
722     // move to the saved position
723     new_repr->setPosition(pos > 0 ? pos : 0);
725     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
726                      _("Stamp"));
728     Inkscape::GC::release(new_repr);
729     g_free(svgpath);
730     g_free(typestr);
731     curve->unref();
734 /**
735  * Create curve from path.
736  */
737 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
739     SPCurve *curve = new SPCurve();
741     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
742        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
743         curve->moveto(sp->first->pos * np->d2i);
744        Inkscape::NodePath::Node *n = sp->first->n.other;
745         while (n) {
746             NR::Point const end_pt = n->pos * np->d2i;
747             switch (n->code) {
748                 case NR_LINETO:
749                     curve->lineto(end_pt);
750                     break;
751                 case NR_CURVETO:
752                     curve->curveto(n->p.other->n.pos * np->d2i,
753                                      n->p.pos * np->d2i,
754                                      end_pt);
755                     break;
756                 default:
757                     g_assert_not_reached();
758                     break;
759             }
760             if (n != sp->last) {
761                 n = n->n.other;
762             } else {
763                 n = NULL;
764             }
765         }
766         if (sp->closed) {
767             curve->closepath();
768         }
769     }
771     return curve;
774 /**
775  * Convert path type string to sodipodi:nodetypes style.
776  */
777 static gchar *create_typestr(Inkscape::NodePath::Path *np)
779     gchar *typestr = g_new(gchar, 32);
780     gint len = 32;
781     gint pos = 0;
783     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
784        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
786         if (pos >= len) {
787             typestr = g_renew(gchar, typestr, len + 32);
788             len += 32;
789         }
791         typestr[pos++] = 'c';
793        Inkscape::NodePath::Node *n;
794         n = sp->first->n.other;
795         while (n) {
796             gchar code;
798             switch (n->type) {
799                 case Inkscape::NodePath::NODE_CUSP:
800                     code = 'c';
801                     break;
802                 case Inkscape::NodePath::NODE_SMOOTH:
803                     code = 's';
804                     break;
805                 case Inkscape::NodePath::NODE_SYMM:
806                     code = 'z';
807                     break;
808                 default:
809                     g_assert_not_reached();
810                     code = '\0';
811                     break;
812             }
814             if (pos >= len) {
815                 typestr = g_renew(gchar, typestr, len + 32);
816                 len += 32;
817             }
819             typestr[pos++] = code;
821             if (n != sp->last) {
822                 n = n->n.other;
823             } else {
824                 n = NULL;
825             }
826         }
827     }
829     if (pos >= len) {
830         typestr = g_renew(gchar, typestr, len + 1);
831         len += 1;
832     }
834     typestr[pos++] = '\0';
836     return typestr;
839 /**
840  \brief Fills node and handle positions for three nodes, splitting line
841   marked by end at distance t.
842  */
843 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
845     g_assert(new_path != NULL);
846     g_assert(end      != NULL);
848     g_assert(end->p.other == new_path);
849    Inkscape::NodePath::Node *start = new_path->p.other;
850     g_assert(start);
852     if (end->code == NR_LINETO) {
853         new_path->type =Inkscape::NodePath::NODE_CUSP;
854         new_path->code = NR_LINETO;
855         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
856     } else {
857         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
858         new_path->code = NR_CURVETO;
859         gdouble s      = 1 - t;
860         for (int dim = 0; dim < 2; dim++) {
861             NR::Coord const f000 = start->pos[dim];
862             NR::Coord const f001 = start->n.pos[dim];
863             NR::Coord const f011 = end->p.pos[dim];
864             NR::Coord const f111 = end->pos[dim];
865             NR::Coord const f00t = s * f000 + t * f001;
866             NR::Coord const f01t = s * f001 + t * f011;
867             NR::Coord const f11t = s * f011 + t * f111;
868             NR::Coord const f0tt = s * f00t + t * f01t;
869             NR::Coord const f1tt = s * f01t + t * f11t;
870             NR::Coord const fttt = s * f0tt + t * f1tt;
871             start->n.pos[dim]    = f00t;
872             new_path->p.pos[dim] = f0tt;
873             new_path->pos[dim]   = fttt;
874             new_path->n.pos[dim] = f1tt;
875             end->p.pos[dim]      = f11t;
876         }
877     }
880 /**
881  * Adds new node on direct line between two nodes, activates handles of all
882  * three nodes.
883  */
884 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
886     g_assert(end);
887     g_assert(end->subpath);
888     g_assert(g_list_find(end->subpath->nodes, end));
890    Inkscape::NodePath::Node *start = end->p.other;
891     g_assert( start->n.other == end );
892    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
893                                                end,
894                                                (NRPathcode)end->code == NR_LINETO?
895                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
896                                                (NRPathcode)end->code,
897                                                &start->pos, &start->pos, &start->n.pos);
898     sp_nodepath_line_midpoint(newnode, end, t);
900     sp_node_adjust_handles(start);
901     sp_node_update_handles(start);
902     sp_node_update_handles(newnode);
903     sp_node_adjust_handles(end);
904     sp_node_update_handles(end);
906     return newnode;
909 /**
910 \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
911 */
912 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
914     g_assert(node);
915     g_assert(node->subpath);
916     g_assert(g_list_find(node->subpath->nodes, node));
918    Inkscape::NodePath::SubPath *sp = node->subpath;
919     Inkscape::NodePath::Path *np    = sp->nodepath;
921     if (sp->closed) {
922         sp_nodepath_subpath_open(sp, node);
923         return sp->first;
924     } else {
925         // no break for end nodes
926         if (node == sp->first) return NULL;
927         if (node == sp->last ) return NULL;
929         // create a new subpath
930        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
932         // duplicate the break node as start of the new subpath
933         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
935         // attach rest of curve to new node
936         g_assert(node->n.other);
937         newnode->n.other = node->n.other; node->n.other = NULL;
938         newnode->n.other->p.other = newnode;
939         newsubpath->last = sp->last;
940         sp->last = node;
941         node = newnode;
942         while (node->n.other) {
943             node = node->n.other;
944             node->subpath = newsubpath;
945             sp->nodes = g_list_remove(sp->nodes, node);
946             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
947         }
950         return newnode;
951     }
954 /**
955  * Duplicate node and connect to neighbours.
956  */
957 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
959     g_assert(node);
960     g_assert(node->subpath);
961     g_assert(g_list_find(node->subpath->nodes, node));
963    Inkscape::NodePath::SubPath *sp = node->subpath;
965     NRPathcode code = (NRPathcode) node->code;
966     if (code == NR_MOVETO) { // if node is the endnode,
967         node->code = NR_LINETO; // new one is inserted before it, so change that to line
968     }
970     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
972     if (!node->n.other || !node->p.other) // if node is an endnode, select it
973         return node;
974     else
975         return newnode; // otherwise select the newly created node
978 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
980     node->p.pos = (node->pos + (node->pos - node->n.pos));
983 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
985     node->n.pos = (node->pos + (node->pos - node->p.pos));
988 /**
989  * Change line type at node, with side effects on neighbours.
990  */
991 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
993     g_assert(end);
994     g_assert(end->subpath);
995     g_assert(end->p.other);
997     if (end->code == static_cast< guint > ( code ) )
998         return;
1000    Inkscape::NodePath::Node *start = end->p.other;
1002     end->code = code;
1004     if (code == NR_LINETO) {
1005         if (start->code == NR_LINETO) {
1006             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
1007         }
1008         if (end->n.other) {
1009             if (end->n.other->code == NR_LINETO) {
1010                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
1011             }
1012         }
1013     } else {
1014         NR::Point delta = end->pos - start->pos;
1015         start->n.pos = start->pos + delta / 3;
1016         end->p.pos = end->pos - delta / 3;
1017         sp_node_adjust_handle(start, 1);
1018         sp_node_adjust_handle(end, -1);
1019     }
1021     sp_node_update_handles(start);
1022     sp_node_update_handles(end);
1025 /**
1026  * Change node type, and its handles accordingly.
1027  */
1028 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1030     g_assert(node);
1031     g_assert(node->subpath);
1033     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1034         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1035             type =Inkscape::NodePath::NODE_CUSP;
1036         }
1037     }
1039     node->type = type;
1041     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1042         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1043         node->knot->setSize (node->selected? 11 : 9);
1044         sp_knot_update_ctrl(node->knot);
1045     } else {
1046         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1047         node->knot->setSize (node->selected? 9 : 7);
1048         sp_knot_update_ctrl(node->knot);
1049     }
1051     // if one of handles is mouseovered, preserve its position
1052     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1053         sp_node_adjust_handle(node, 1);
1054     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1055         sp_node_adjust_handle(node, -1);
1056     } else {
1057         sp_node_adjust_handles(node);
1058     }
1060     sp_node_update_handles(node);
1062     sp_nodepath_update_statusbar(node->subpath->nodepath);
1064     return node;
1067 bool
1068 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1070         Inkscape::NodePath::Node *othernode = side->other;
1071         if (!othernode)
1072             return false;
1073         NRPathcode const code = sp_node_path_code_from_side(node, side);
1074         if (code == NR_LINETO)
1075             return true;
1076         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1077         if (&node->p == side) {
1078             other_to_me = &othernode->n;
1079         } else if (&node->n == side) {
1080             other_to_me = &othernode->p;
1081         } 
1082         if (!other_to_me)
1083             return false;
1084         bool is_line = 
1085              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1086               NR::L2(node->pos - side->pos) < 1e-6);
1087         return is_line;
1090 /**
1091  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1092  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1093  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1094  * If already cusp and set to cusp, retracts handles.
1095 */
1096 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1098     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1100 /* 
1101   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1102  
1103         if (two_handles) {
1104             // do nothing, adjust_handles called via set_node_type will line them up
1105         } else if (one_handle) {
1106             if (opposite_to_handle_is_line) {
1107                 if (lined_up) {
1108                     // already half-smooth; pull opposite handle too making it fully smooth
1109                 } else {
1110                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1111                 }
1112             } else {
1113                 // pull opposite handle in line with the existing one
1114             }
1115         } else if (no_handles) {
1116             if (both_segments_are_lines OR both_segments_are_curves) {
1117                 //pull both handles
1118             } else {
1119                 // pull the handle opposite to line segment, making node half-smooth
1120             }
1121         }
1122 */
1123         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1124         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1125         bool p_is_line = sp_node_side_is_line(node, &node->p);
1126         bool n_is_line = sp_node_side_is_line(node, &node->n);
1128         if (p_has_handle && n_has_handle) {
1129             // do nothing, adjust_handles will line them up
1130         } else if (p_has_handle || n_has_handle) {
1131             if (p_has_handle && n_is_line) {
1132                 Radial line (node->n.other->pos - node->pos);
1133                 Radial handle (node->pos - node->p.pos);
1134                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1135                     // already half-smooth; pull opposite handle too making it fully smooth
1136                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1137                 } else {
1138                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1139                 }
1140             } else if (n_has_handle && p_is_line) {
1141                 Radial line (node->p.other->pos - node->pos);
1142                 Radial handle (node->pos - node->n.pos);
1143                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1144                     // already half-smooth; pull opposite handle too making it fully smooth
1145                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1146                 } else {
1147                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1148                 }
1149             } else if (p_has_handle && node->n.other) {
1150                 // pull n handle
1151                 node->n.other->code = NR_CURVETO;
1152                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1153                     NR::L2(node->p.pos - node->pos) :
1154                     NR::L2(node->n.other->pos - node->pos) / 3;
1155                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1156             } else if (n_has_handle && node->p.other) {
1157                 // pull p handle
1158                 node->code = NR_CURVETO;
1159                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1160                     NR::L2(node->n.pos - node->pos) :
1161                     NR::L2(node->p.other->pos - node->pos) / 3;
1162                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1163             }
1164         } else if (!p_has_handle && !n_has_handle) {
1165             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1166                 // no handles, but both segments are either lnes or curves:
1167                 //pull both handles
1169                 // convert both to curves:
1170                 node->code = NR_CURVETO;
1171                 node->n.other->code = NR_CURVETO;
1173                 NR::Point leg_prev = node->pos - node->p.other->pos;
1174                 NR::Point leg_next = node->pos - node->n.other->pos;
1176                 double norm_leg_prev = L2(leg_prev);
1177                 double norm_leg_next = L2(leg_next);
1179                 NR::Point delta;
1180                 if (norm_leg_next > 0.0) {
1181                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1182                     (&delta)->normalize();
1183                 }
1185                 if (type == Inkscape::NodePath::NODE_SYMM) {
1186                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1187                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1188                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1189                 } else {
1190                     // length of handle is proportional to distance to adjacent node
1191                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1192                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1193                 }
1195             } else {
1196                 // pull the handle opposite to line segment, making it half-smooth
1197                 if (p_is_line && node->n.other) {
1198                     if (type != Inkscape::NodePath::NODE_SYMM) {
1199                         // pull n handle
1200                         node->n.other->code = NR_CURVETO;
1201                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1202                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1203                     }
1204                 } else if (n_is_line && node->p.other) {
1205                     if (type != Inkscape::NodePath::NODE_SYMM) {
1206                         // pull p handle
1207                         node->code = NR_CURVETO;
1208                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1209                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1210                     }
1211                 }
1212             }
1213         }
1214     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1215         // cusping a cusp: retract nodes
1216         node->p.pos = node->pos;
1217         node->n.pos = node->pos;
1218     }
1220     sp_nodepath_set_node_type (node, type);
1223 /**
1224  * Move node to point, and adjust its and neighbouring handles.
1225  */
1226 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1228     NR::Point delta = p - node->pos;
1229     node->pos = p;
1231     node->p.pos += delta;
1232     node->n.pos += delta;
1234     Inkscape::NodePath::Node *node_p = NULL;
1235     Inkscape::NodePath::Node *node_n = NULL;
1237     if (node->p.other) {
1238         if (node->code == NR_LINETO) {
1239             sp_node_adjust_handle(node, 1);
1240             sp_node_adjust_handle(node->p.other, -1);
1241             node_p = node->p.other;
1242         }
1243     }
1244     if (node->n.other) {
1245         if (node->n.other->code == NR_LINETO) {
1246             sp_node_adjust_handle(node, -1);
1247             sp_node_adjust_handle(node->n.other, 1);
1248             node_n = node->n.other;
1249         }
1250     }
1252     // this function is only called from batch movers that will update display at the end
1253     // themselves, so here we just move all the knots without emitting move signals, for speed
1254     sp_node_update_handles(node, false);
1255     if (node_n) {
1256         sp_node_update_handles(node_n, false);
1257     }
1258     if (node_p) {
1259         sp_node_update_handles(node_p, false);
1260     }
1263 /**
1264  * Call sp_node_moveto() for node selection and handle possible snapping.
1265  */
1266 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1267                                             bool const snap, bool constrained = false, 
1268                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1270     NR::Coord best = NR_HUGE;
1271     NR::Point delta(dx, dy);
1272     NR::Point best_pt = delta;
1273     Inkscape::SnappedPoint best_abs;
1274     
1275     if (snap) {    
1276         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1277          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1278          * must provide that information. */
1279           
1280         // Build a list of the unselected nodes to which the snapper should snap 
1281         std::vector<Geom::Point> unselected_nodes;
1282         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1283             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1284             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1285                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1286                 if (!node->selected) {
1287                     unselected_nodes.push_back(to_2geom(node->pos));
1288                 }    
1289             }
1290         }        
1291         
1292         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1293         
1294         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1295             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1296             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1297             Inkscape::SnappedPoint s;
1298             if (constrained) {
1299                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1300                 dedicated_constraint.setPoint(n->pos);
1301                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1302             } else {
1303                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1304             }            
1305             if (s.getSnapped() && (s.getDistance() < best)) {
1306                 best = s.getDistance();
1307                 best_abs = s;
1308                 best_pt = from_2geom(s.getPoint()) - n->pos;
1309             }
1310         }
1311                         
1312         if (best_abs.getSnapped()) {
1313             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1314         } else {
1315             nodepath->desktop->snapindicator->remove_snappoint();    
1316         }
1317     }
1319     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1320         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1321         sp_node_moveto(n, n->pos + best_pt);
1322     }
1324     // do not update repr here so that node dragging is acceptably fast
1325     update_object(nodepath);
1328 /**
1329 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1330 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1331 near x = 0.
1332  */
1333 double
1334 sculpt_profile (double x, double alpha, guint profile)
1336     if (x >= 1)
1337         return 0;
1338     if (x <= 0)
1339         return 1;
1341     switch (profile) {
1342         case SCULPT_PROFILE_LINEAR:
1343         return 1 - x;
1344         case SCULPT_PROFILE_BELL:
1345         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1346         case SCULPT_PROFILE_ELLIPTIC:
1347         return sqrt(1 - x*x);
1348     }
1350     return 1;
1353 double
1354 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1356     // extremely primitive for now, don't have time to look for the real one
1357     double lower = NR::L2(b - a);
1358     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1359     return (lower + upper)/2;
1362 void
1363 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1365     n->pos = n->origin + delta;
1366     n->n.pos = n->n.origin + delta_n;
1367     n->p.pos = n->p.origin + delta_p;
1368     sp_node_adjust_handles(n);
1369     sp_node_update_handles(n, false);
1372 /**
1373  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1374  * on how far they are from the dragged node n.
1375  */
1376 static void
1377 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1379     g_assert (n);
1380     g_assert (nodepath);
1381     g_assert (n->subpath->nodepath == nodepath);
1383     double pressure = n->knot->pressure;
1384     if (pressure == 0)
1385         pressure = 0.5; // default
1386     pressure = CLAMP (pressure, 0.2, 0.8);
1388     // map pressure to alpha = 1/5 ... 5
1389     double alpha = 1 - 2 * fabs(pressure - 0.5);
1390     if (pressure > 0.5)
1391         alpha = 1/alpha;
1393     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1395     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1396         // Only one subpath has selected nodes:
1397         // use linear mode, where the distance from n to node being dragged is calculated along the path
1399         double n_sel_range = 0, p_sel_range = 0;
1400         guint n_nodes = 0, p_nodes = 0;
1401         guint n_sel_nodes = 0, p_sel_nodes = 0;
1403         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1404         {
1405             double n_range = 0, p_range = 0;
1406             bool n_going = true, p_going = true;
1407             Inkscape::NodePath::Node *n_node = n;
1408             Inkscape::NodePath::Node *p_node = n;
1409             do {
1410                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1411                 if (n_node && n_going)
1412                     n_node = n_node->n.other;
1413                 if (n_node == NULL) {
1414                     n_going = false;
1415                 } else {
1416                     n_nodes ++;
1417                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1418                     if (n_node->selected) {
1419                         n_sel_nodes ++;
1420                         n_sel_range = n_range;
1421                     }
1422                     if (n_node == p_node) {
1423                         n_going = false;
1424                         p_going = false;
1425                     }
1426                 }
1427                 if (p_node && p_going)
1428                     p_node = p_node->p.other;
1429                 if (p_node == NULL) {
1430                     p_going = false;
1431                 } else {
1432                     p_nodes ++;
1433                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1434                     if (p_node->selected) {
1435                         p_sel_nodes ++;
1436                         p_sel_range = p_range;
1437                     }
1438                     if (p_node == n_node) {
1439                         n_going = false;
1440                         p_going = false;
1441                     }
1442                 }
1443             } while (n_going || p_going);
1444         }
1446         // Second pass: actually move nodes in this subpath
1447         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1448         {
1449             double n_range = 0, p_range = 0;
1450             bool n_going = true, p_going = true;
1451             Inkscape::NodePath::Node *n_node = n;
1452             Inkscape::NodePath::Node *p_node = n;
1453             do {
1454                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1455                 if (n_node && n_going)
1456                     n_node = n_node->n.other;
1457                 if (n_node == NULL) {
1458                     n_going = false;
1459                 } else {
1460                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1461                     if (n_node->selected) {
1462                         sp_nodepath_move_node_and_handles (n_node,
1463                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1464                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1465                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1466                     }
1467                     if (n_node == p_node) {
1468                         n_going = false;
1469                         p_going = false;
1470                     }
1471                 }
1472                 if (p_node && p_going)
1473                     p_node = p_node->p.other;
1474                 if (p_node == NULL) {
1475                     p_going = false;
1476                 } else {
1477                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1478                     if (p_node->selected) {
1479                         sp_nodepath_move_node_and_handles (p_node,
1480                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1481                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1482                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1483                     }
1484                     if (p_node == n_node) {
1485                         n_going = false;
1486                         p_going = false;
1487                     }
1488                 }
1489             } while (n_going || p_going);
1490         }
1492     } else {
1493         // Multiple subpaths have selected nodes:
1494         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1495         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1496         // fix the pear-like shape when sculpting e.g. a ring
1498         // First pass: calculate range
1499         gdouble direct_range = 0;
1500         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1501             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1502             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1503                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1504                 if (node->selected) {
1505                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1506                 }
1507             }
1508         }
1510         // Second pass: actually move nodes
1511         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1512             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1513             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1514                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1515                 if (node->selected) {
1516                     if (direct_range > 1e-6) {
1517                         sp_nodepath_move_node_and_handles (node,
1518                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1519                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1520                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1521                     } else {
1522                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1523                     }
1525                 }
1526             }
1527         }
1528     }
1530     // do not update repr here so that node dragging is acceptably fast
1531     update_object(nodepath);
1535 /**
1536  * Move node selection to point, adjust its and neighbouring handles,
1537  * handle possible snapping, and commit the change with possible undo.
1538  */
1539 void
1540 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1542     if (!nodepath) return;
1544     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1546     if (dx == 0) {
1547         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1548     } else if (dy == 0) {
1549         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1550     } else {
1551         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1552     }
1555 /**
1556  * Move node selection off screen and commit the change.
1557  */
1558 void
1559 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1561     // borrowed from sp_selection_move_screen in selection-chemistry.c
1562     // we find out the current zoom factor and divide deltas by it
1564     gdouble zoom = desktop->current_zoom();
1565     gdouble zdx = dx / zoom;
1566     gdouble zdy = dy / zoom;
1568     if (!nodepath) return;
1570     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1572     if (dx == 0) {
1573         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1574     } else if (dy == 0) {
1575         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1576     } else {
1577         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1578     }
1581 /**
1582  * Move selected nodes to the absolute position given
1583  */
1584 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1586     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1587         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1588         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1589         sp_node_moveto(n, npos);
1590     }
1592     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1595 /**
1596  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1597  */
1598 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1600     boost::optional<Geom::Coord> no_coord;
1601     g_return_val_if_fail(nodepath->selected, no_coord);
1603     // determine coordinate of first selected node
1604     GList *nsel = nodepath->selected;
1605     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1606     NR::Coord coord = n->pos[axis];
1607     bool coincide = true;
1609     // compare it to the coordinates of all the other selected nodes
1610     for (GList *l = nsel->next; l != NULL; l = l->next) {
1611         n = (Inkscape::NodePath::Node *) l->data;
1612         if (n->pos[axis] != coord) {
1613             coincide = false;
1614         }
1615     }
1616     if (coincide) {
1617         return coord;
1618     } else {
1619         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1620         // currently we return the coordinate of the bounding box midpoint because I don't know how
1621         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1622         return bbox.midpoint()[axis];
1623     }
1626 /** If they don't yet exist, creates knot and line for the given side of the node */
1627 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1629     if (!side->knot) {
1630         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"));
1632         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1633         side->knot->setSize (7);
1634         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1635         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1636         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1637         sp_knot_update_ctrl(side->knot);
1639         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1640         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1641         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1642         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1643         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1644         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1645     }
1647     if (!side->line) {
1648         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1649                                         SP_TYPE_CTRLLINE, NULL);
1650     }
1653 /**
1654  * Ensure the given handle of the node is visible/invisible, update its screen position
1655  */
1656 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1658     g_assert(node != NULL);
1660    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1661     NRPathcode code = sp_node_path_code_from_side(node, side);
1663     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1665     if (show_handle) {
1666         if (!side->knot) { // No handle knot at all
1667             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1668             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1669             side->knot->pos = side->pos;
1670             if (side->knot->item)
1671                 SP_CTRL(side->knot->item)->moveto(side->pos);
1672             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1673             sp_knot_show(side->knot);
1674         } else {
1675             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1676                 if (fire_move_signals) {
1677                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1678                 } else {
1679                     sp_knot_moveto(side->knot, side->pos);
1680                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1681                 }
1682             }
1683             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1684                 sp_knot_show(side->knot);
1685             }
1686         }
1687         sp_canvas_item_show(side->line);
1688     } else {
1689         if (side->knot) {
1690             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1691                 sp_knot_hide(side->knot);
1692             }
1693         }
1694         if (side->line) {
1695             sp_canvas_item_hide(side->line);
1696         }
1697     }
1700 /**
1701  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1702  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1703  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1704  * updated; otherwise, just move the knots silently (used in batch moves).
1705  */
1706 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1708     g_assert(node != NULL);
1710     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1711         sp_knot_show(node->knot);
1712     }
1714     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1715         if (fire_move_signals)
1716             sp_knot_set_position(node->knot, node->pos, 0);
1717         else
1718             sp_knot_moveto(node->knot, node->pos);
1719     }
1721     gboolean show_handles = node->selected;
1722     if (node->p.other != NULL) {
1723         if (node->p.other->selected) show_handles = TRUE;
1724     }
1725     if (node->n.other != NULL) {
1726         if (node->n.other->selected) show_handles = TRUE;
1727     }
1729     if (node->subpath->nodepath->show_handles == false)
1730         show_handles = FALSE;
1732     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1733     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1736 /**
1737  * Call sp_node_update_handles() for all nodes on subpath.
1738  */
1739 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1741     g_assert(subpath != NULL);
1743     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1744         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1745     }
1748 /**
1749  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1750  */
1751 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1753     g_assert(nodepath != NULL);
1755     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1756         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1757     }
1760 void
1761 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1763     if (nodepath == NULL) return;
1765     nodepath->show_handles = show;
1766     sp_nodepath_update_handles(nodepath);
1769 /**
1770  * Adds all selected nodes in nodepath to list.
1771  */
1772 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1774     StlConv<Node *>::list(l, selected);
1775 /// \todo this adds a copying, rework when the selection becomes a stl list
1778 /**
1779  * Align selected nodes on the specified axis.
1780  */
1781 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1783     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1784         return;
1785     }
1787     if ( !nodepath->selected->next ) { // only one node selected
1788         return;
1789     }
1790    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1791     NR::Point dest(pNode->pos);
1792     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1793         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1794         if (pNode) {
1795             dest[axis] = pNode->pos[axis];
1796             sp_node_moveto(pNode, dest);
1797         }
1798     }
1800     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1803 /// Helper struct.
1804 struct NodeSort
1806    Inkscape::NodePath::Node *_node;
1807     NR::Coord _coord;
1808     /// \todo use vectorof pointers instead of calling copy ctor
1809     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1810         _node(node), _coord(node->pos[axis])
1811     {}
1813 };
1815 static bool operator<(NodeSort const &a, NodeSort const &b)
1817     return (a._coord < b._coord);
1820 /**
1821  * Distribute selected nodes on the specified axis.
1822  */
1823 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1825     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1826         return;
1827     }
1829     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1830         return;
1831     }
1833    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1834     std::vector<NodeSort> sorted;
1835     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1836         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1837         if (pNode) {
1838             NodeSort n(pNode, axis);
1839             sorted.push_back(n);
1840             //dest[axis] = pNode->pos[axis];
1841             //sp_node_moveto(pNode, dest);
1842         }
1843     }
1844     std::sort(sorted.begin(), sorted.end());
1845     unsigned int len = sorted.size();
1846     //overall bboxes span
1847     float dist = (sorted.back()._coord -
1848                   sorted.front()._coord);
1849     //new distance between each bbox
1850     float step = (dist) / (len - 1);
1851     float pos = sorted.front()._coord;
1852     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1853           it < sorted.end();
1854           it ++ )
1855     {
1856         NR::Point dest((*it)._node->pos);
1857         dest[axis] = pos;
1858         sp_node_moveto((*it)._node, dest);
1859         pos += step;
1860     }
1862     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1866 /**
1867  * Call sp_nodepath_line_add_node() for all selected segments.
1868  */
1869 void
1870 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1872     if (!nodepath) {
1873         return;
1874     }
1876     GList *nl = NULL;
1878     int n_added = 0;
1880     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1881        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1882         g_assert(t->selected);
1883         if (t->p.other && t->p.other->selected) {
1884             nl = g_list_prepend(nl, t);
1885         }
1886     }
1888     while (nl) {
1889        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1890        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1891        sp_nodepath_node_select(n, TRUE, FALSE);
1892        n_added ++;
1893        nl = g_list_remove(nl, t);
1894     }
1896     /** \todo fixme: adjust ? */
1897     sp_nodepath_update_handles(nodepath);
1899     if (n_added > 1) {
1900         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1901     } else if (n_added > 0) {
1902         sp_nodepath_update_repr(nodepath, _("Add node"));
1903     }
1905     sp_nodepath_update_statusbar(nodepath);
1908 /**
1909  * Select segment nearest to point
1910  */
1911 void
1912 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1914     if (!nodepath) {
1915         return;
1916     }
1918     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1919     Geom::PathVector const &pathv = curve->get_pathvector();
1920     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1922     // calculate index for nodepath's representation.
1923     unsigned int segment_index = floor(pvpos.t) + 1;
1924     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1925         segment_index += pathv[i].size() + 1;
1926         if (pathv[i].closed()) {
1927             segment_index += 1;
1928         }
1929     }
1931     curve->unref();
1933     //find segment to segment
1934     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
1936     //fixme: this can return NULL, so check before proceeding.
1937     g_return_if_fail(e != NULL);
1939     gboolean force = FALSE;
1940     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1941         force = TRUE;
1942     }
1943     sp_nodepath_node_select(e, (gboolean) toggle, force);
1944     if (e->p.other)
1945         sp_nodepath_node_select(e->p.other, TRUE, force);
1947     sp_nodepath_update_handles(nodepath);
1949     sp_nodepath_update_statusbar(nodepath);
1952 /**
1953  * Add a node nearest to point
1954  */
1955 void
1956 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1958     if (!nodepath) {
1959         return;
1960     }
1962     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1963     Geom::PathVector const &pathv = curve->get_pathvector();
1964     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1966     // calculate index for nodepath's representation.
1967     double int_part;
1968     double t = std::modf(pvpos.t, &int_part);
1969     unsigned int segment_index = (unsigned int)int_part + 1;
1970     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1971         segment_index += pathv[i].size() + 1;
1972         if (pathv[i].closed()) {
1973             segment_index += 1;
1974         }
1975     }
1977     curve->unref();
1979     //find segment to split
1980     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
1982     //don't know why but t seems to flip for lines
1983     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1984         t = 1.0 - t;
1985     }
1987     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
1988     sp_nodepath_node_select(n, FALSE, TRUE);
1990     /* fixme: adjust ? */
1991     sp_nodepath_update_handles(nodepath);
1993     sp_nodepath_update_repr(nodepath, _("Add node"));
1995     sp_nodepath_update_statusbar(nodepath);
1998 /*
1999  * Adjusts a segment so that t moves by a certain delta for dragging
2000  * converts lines to curves
2001  *
2002  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2003  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2004  */
2005 void
2006 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, NR::Point delta)
2008     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2010     //fixme: e and e->p can be NULL, so check for those before proceeding
2011     g_return_if_fail(e != NULL);
2012     g_return_if_fail(&e->p != NULL);
2014     /* feel good is an arbitrary parameter that distributes the delta between handles
2015      * if t of the drag point is less than 1/6 distance form the endpoint only
2016      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2017      */
2018     double feel_good;
2019     if (t <= 1.0 / 6.0)
2020         feel_good = 0;
2021     else if (t <= 0.5)
2022         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2023     else if (t <= 5.0 / 6.0)
2024         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2025     else
2026         feel_good = 1;
2028     //if we're dragging a line convert it to a curve
2029     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2030         sp_nodepath_set_line_type(e, NR_CURVETO);
2031     }
2033     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2034     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2035     e->p.other->n.pos += offsetcoord0;
2036     e->p.pos += offsetcoord1;
2038     // adjust handles of adjacent nodes where necessary
2039     sp_node_adjust_handle(e,1);
2040     sp_node_adjust_handle(e->p.other,-1);
2042     sp_nodepath_update_handles(e->subpath->nodepath);
2044     update_object(e->subpath->nodepath);
2046     sp_nodepath_update_statusbar(e->subpath->nodepath);
2050 /**
2051  * Call sp_nodepath_break() for all selected segments.
2052  */
2053 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2055     if (!nodepath) return;
2057     GList *tempin = g_list_copy(nodepath->selected);
2058     GList *temp = NULL;
2059     for (GList *l = tempin; l != NULL; l = l->next) {
2060        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2061        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2062         if (nn == NULL) continue; // no break, no new node
2063         temp = g_list_prepend(temp, nn);
2064     }
2065     g_list_free(tempin);
2067     if (temp) {
2068         sp_nodepath_deselect(nodepath);
2069     }
2070     for (GList *l = temp; l != NULL; l = l->next) {
2071         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2072     }
2074     sp_nodepath_update_handles(nodepath);
2076     sp_nodepath_update_repr(nodepath, _("Break path"));
2079 /**
2080  * Duplicate the selected node(s).
2081  */
2082 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2084     if (!nodepath) {
2085         return;
2086     }
2088     GList *temp = NULL;
2089     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2090        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2091        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2092         if (nn == NULL) continue; // could not duplicate
2093         temp = g_list_prepend(temp, nn);
2094     }
2096     if (temp) {
2097         sp_nodepath_deselect(nodepath);
2098     }
2099     for (GList *l = temp; l != NULL; l = l->next) {
2100         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2101     }
2103     sp_nodepath_update_handles(nodepath);
2105     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2108 /**
2109  *  Internal function to join two nodes by merging them into one.
2110  */
2111 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2113     /* a and b are endpoints */
2115     // if one of the two nodes is mouseovered, fix its position
2116     NR::Point c;
2117     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2118         c = a->pos;
2119     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2120         c = b->pos;
2121     } else {
2122         // otherwise, move joined node to the midpoint
2123         c = (a->pos + b->pos) / 2;
2124     }
2126     if (a->subpath == b->subpath) {
2127        Inkscape::NodePath::SubPath *sp = a->subpath;
2128         sp_nodepath_subpath_close(sp);
2129         sp_node_moveto (sp->first, c);
2131         sp_nodepath_update_handles(sp->nodepath);
2132         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2133         return;
2134     }
2136     /* a and b are separate subpaths */
2137     Inkscape::NodePath::SubPath *sa = a->subpath;
2138     Inkscape::NodePath::SubPath *sb = b->subpath;
2139     NR::Point p;
2140     Inkscape::NodePath::Node *n;
2141     NRPathcode code;
2142     if (a == sa->first) {
2143         // we will now reverse sa, so that a is its last node, not first, and drop that node
2144         p = sa->first->n.pos;
2145         code = (NRPathcode)sa->first->n.other->code;
2146         // create new subpath
2147        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2148        // create a first moveto node on it
2149         n = sa->last;
2150         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2151         n = n->p.other;
2152         if (n == sa->first) n = NULL;
2153         while (n) {
2154             // copy the rest of the nodes from sa to t, going backwards
2155             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2156             n = n->p.other;
2157             if (n == sa->first) n = NULL;
2158         }
2159         // replace sa with t
2160         sp_nodepath_subpath_destroy(sa);
2161         sa = t;
2162     } else if (a == sa->last) {
2163         // a is already last, just drop it
2164         p = sa->last->p.pos;
2165         code = (NRPathcode)sa->last->code;
2166         sp_nodepath_node_destroy(sa->last);
2167     } else {
2168         code = NR_END;
2169         g_assert_not_reached();
2170     }
2172     if (b == sb->first) {
2173         // copy all nodes from b to a, forward 
2174         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2175         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2176             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2177         }
2178     } else if (b == sb->last) {
2179         // copy all nodes from b to a, backward 
2180         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2181         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2182             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2183         }
2184     } else {
2185         g_assert_not_reached();
2186     }
2187     /* and now destroy sb */
2189     sp_nodepath_subpath_destroy(sb);
2191     sp_nodepath_update_handles(sa->nodepath);
2193     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2195     sp_nodepath_update_statusbar(nodepath);
2198 /**
2199  *  Internal function to join two nodes by adding a segment between them.
2200  */
2201 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2203     if (a->subpath == b->subpath) {
2204        Inkscape::NodePath::SubPath *sp = a->subpath;
2206         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2207         sp->closed = TRUE;
2209         sp->first->p.other = sp->last;
2210         sp->last->n.other  = sp->first;
2212         sp_node_handle_mirror_p_to_n(sp->last);
2213         sp_node_handle_mirror_n_to_p(sp->first);
2215         sp->first->code = sp->last->code;
2216         sp->first       = sp->last;
2218         sp_nodepath_update_handles(sp->nodepath);
2220         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2222         return;
2223     }
2225     /* a and b are separate subpaths */
2226     Inkscape::NodePath::SubPath *sa = a->subpath;
2227     Inkscape::NodePath::SubPath *sb = b->subpath;
2229     Inkscape::NodePath::Node *n;
2230     NR::Point p;
2231     NRPathcode code;
2232     if (a == sa->first) {
2233         code = (NRPathcode) sa->first->n.other->code;
2234        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2235         n = sa->last;
2236         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2237         for (n = n->p.other; n != NULL; n = n->p.other) {
2238             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2239         }
2240         sp_nodepath_subpath_destroy(sa);
2241         sa = t;
2242     } else if (a == sa->last) {
2243         code = (NRPathcode)sa->last->code;
2244     } else {
2245         code = NR_END;
2246         g_assert_not_reached();
2247     }
2249     if (b == sb->first) {
2250         n = sb->first;
2251         sp_node_handle_mirror_p_to_n(sa->last);
2252         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2253         sp_node_handle_mirror_n_to_p(sa->last);
2254         for (n = n->n.other; n != NULL; n = n->n.other) {
2255             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2256         }
2257     } else if (b == sb->last) {
2258         n = sb->last;
2259         sp_node_handle_mirror_p_to_n(sa->last);
2260         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2261         sp_node_handle_mirror_n_to_p(sa->last);
2262         for (n = n->p.other; n != NULL; n = n->p.other) {
2263             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2264         }
2265     } else {
2266         g_assert_not_reached();
2267     }
2268     /* and now destroy sb */
2270     sp_nodepath_subpath_destroy(sb);
2272     sp_nodepath_update_handles(sa->nodepath);
2274     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2277 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2279 /**
2280  * Internal function to handle joining two nodes.
2281  */
2282 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2284     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2286     if (g_list_length(nodepath->selected) != 2) {
2287         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2288         return;
2289     }
2291     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2292     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2294     g_assert(a != b);
2295     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2296         // someone tried to join an orphan node (i.e. a single-node subpath).
2297         // this is not worth an error message, just fail silently.
2298         return;
2299     }
2301     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2302         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2303         return;
2304     }
2306     switch(mode) {
2307         case NODE_JOIN_ENDPOINTS:
2308             do_node_selected_join(nodepath, a, b);
2309             break;
2310         case NODE_JOIN_SEGMENT:
2311             do_node_selected_join_segment(nodepath, a, b);
2312             break;
2313     }
2316 /**
2317  *  Join two nodes by merging them into one.
2318  */
2319 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2321     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2324 /**
2325  *  Join two nodes by adding a segment between them.
2326  */
2327 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2329     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2332 /**
2333  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2334  */
2335 void sp_node_delete_preserve(GList *nodes_to_delete)
2337     GSList *nodepaths = NULL;
2339     while (nodes_to_delete) {
2340         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2341         Inkscape::NodePath::SubPath *sp = node->subpath;
2342         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2343         Inkscape::NodePath::Node *sample_cursor = NULL;
2344         Inkscape::NodePath::Node *sample_end = NULL;
2345         Inkscape::NodePath::Node *delete_cursor = node;
2346         bool just_delete = false;
2348         //find the start of this contiguous selection
2349         //move left to the first node that is not selected
2350         //or the start of the non-closed path
2351         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2352             delete_cursor = curr;
2353         }
2355         //just delete at the beginning of an open path
2356         if (!delete_cursor->p.other) {
2357             sample_cursor = delete_cursor;
2358             just_delete = true;
2359         } else {
2360             sample_cursor = delete_cursor->p.other;
2361         }
2363         //calculate points for each segment
2364         int rate = 5;
2365         float period = 1.0 / rate;
2366         std::vector<NR::Point> data;
2367         if (!just_delete) {
2368             data.push_back(sample_cursor->pos);
2369             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2370                 //just delete at the end of an open path
2371                 if (!sp->closed && curr == sp->last) {
2372                     just_delete = true;
2373                     break;
2374                 }
2376                 //sample points on the contiguous selected segment
2377                 NR::Point *bez;
2378                 bez = new NR::Point [4];
2379                 bez[0] = curr->pos;
2380                 bez[1] = curr->n.pos;
2381                 bez[2] = curr->n.other->p.pos;
2382                 bez[3] = curr->n.other->pos;
2383                 for (int i=1; i<rate; i++) {
2384                     gdouble t = i * period;
2385                     NR::Point p = bezier_pt(3, bez, t);
2386                     data.push_back(p);
2387                 }
2388                 data.push_back(curr->n.other->pos);
2390                 sample_end = curr->n.other;
2391                 //break if we've come full circle or hit the end of the selection
2392                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2393                     break;
2394                 }
2395             }
2396         }
2398         if (!just_delete) {
2399             //calculate the best fitting single segment and adjust the endpoints
2400             NR::Point *adata;
2401             adata = new NR::Point [data.size()];
2402             copy(data.begin(), data.end(), adata);
2404             NR::Point *bez;
2405             bez = new NR::Point [4];
2406             //would decreasing error create a better fitting approximation?
2407             gdouble error = 1.0;
2408             gint ret;
2409             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2411             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2412             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2413             //the resulting nodes behave as expected.
2414             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2415                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2416             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2417                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2419             //adjust endpoints
2420             sample_cursor->n.pos = bez[1];
2421             sample_end->p.pos = bez[2];
2422         }
2424         //destroy this contiguous selection
2425         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2426             Inkscape::NodePath::Node *temp = delete_cursor;
2427             if (delete_cursor->n.other == delete_cursor) {
2428                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2429                 delete_cursor = NULL;
2430             } else {
2431                 delete_cursor = delete_cursor->n.other;
2432             }
2433             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2434             sp_nodepath_node_destroy(temp);
2435         }
2437         sp_nodepath_update_handles(nodepath);
2439         if (!g_slist_find(nodepaths, nodepath))
2440             nodepaths = g_slist_prepend (nodepaths, nodepath);
2441     }
2443     for (GSList *i = nodepaths; i; i = i->next) {
2444         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2445         // different nodepaths will give us one undo event per nodepath
2446         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2448         // if the entire nodepath is removed, delete the selected object.
2449         if (nodepath->subpaths == NULL ||
2450             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2451             //at least 2
2452             sp_nodepath_get_node_count(nodepath) < 2) {
2453             SPDocument *document = sp_desktop_document (nodepath->desktop);
2454             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2455             //delete this nodepath's object, not the entire selection! (though at this time, this
2456             //does not matter)
2457             sp_selection_delete(nodepath->desktop);
2458             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2459                               _("Delete nodes"));
2460         } else {
2461             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2462             sp_nodepath_update_statusbar(nodepath);
2463         }
2464     }
2466     g_slist_free (nodepaths);
2469 /**
2470  * Delete one or more selected nodes.
2471  */
2472 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2474     if (!nodepath) return;
2475     if (!nodepath->selected) return;
2477     /** \todo fixme: do it the right way */
2478     while (nodepath->selected) {
2479        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2480         sp_nodepath_node_destroy(node);
2481     }
2484     //clean up the nodepath (such as for trivial subpaths)
2485     sp_nodepath_cleanup(nodepath);
2487     sp_nodepath_update_handles(nodepath);
2489     // if the entire nodepath is removed, delete the selected object.
2490     if (nodepath->subpaths == NULL ||
2491         sp_nodepath_get_node_count(nodepath) < 2) {
2492         SPDocument *document = sp_desktop_document (nodepath->desktop);
2493         sp_selection_delete(nodepath->desktop);
2494         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2495                           _("Delete nodes"));
2496         return;
2497     }
2499     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2501     sp_nodepath_update_statusbar(nodepath);
2504 /**
2505  * Delete one or more segments between two selected nodes.
2506  * This is the code for 'split'.
2507  */
2508 void
2509 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2511    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2512    Inkscape::NodePath::Node *curr, *next;     //Iterators
2514     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2516     if (g_list_length(nodepath->selected) != 2) {
2517         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2518                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2519         return;
2520     }
2522     //Selected nodes, not inclusive
2523    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2524    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2526     if ( ( a==b)                       ||  //same node
2527          (a->subpath  != b->subpath )  ||  //not the same path
2528          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2529          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2530     {
2531         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2532                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2533         return;
2534     }
2536     //###########################################
2537     //# BEGIN EDITS
2538     //###########################################
2539     //##################################
2540     //# CLOSED PATH
2541     //##################################
2542     if (a->subpath->closed) {
2545         gboolean reversed = FALSE;
2547         //Since we can go in a circle, we need to find the shorter distance.
2548         //  a->b or b->a
2549         start = end = NULL;
2550         int distance    = 0;
2551         int minDistance = 0;
2552         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2553             if (curr==b) {
2554                 //printf("a to b:%d\n", distance);
2555                 start = a;//go from a to b
2556                 end   = b;
2557                 minDistance = distance;
2558                 //printf("A to B :\n");
2559                 break;
2560             }
2561             distance++;
2562         }
2564         //try again, the other direction
2565         distance = 0;
2566         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2567             if (curr==a) {
2568                 //printf("b to a:%d\n", distance);
2569                 if (distance < minDistance) {
2570                     start    = b;  //we go from b to a
2571                     end      = a;
2572                     reversed = TRUE;
2573                     //printf("B to A\n");
2574                 }
2575                 break;
2576             }
2577             distance++;
2578         }
2581         //Copy everything from 'end' to 'start' to a new subpath
2582        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2583         for (curr=end ; curr ; curr=curr->n.other) {
2584             NRPathcode code = (NRPathcode) curr->code;
2585             if (curr == end)
2586                 code = NR_MOVETO;
2587             sp_nodepath_node_new(t, NULL,
2588                                  (Inkscape::NodePath::NodeType)curr->type, code,
2589                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2590             if (curr == start)
2591                 break;
2592         }
2593         sp_nodepath_subpath_destroy(a->subpath);
2596     }
2600     //##################################
2601     //# OPEN PATH
2602     //##################################
2603     else {
2605         //We need to get the direction of the list between A and B
2606         //Can we walk from a to b?
2607         start = end = NULL;
2608         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2609             if (curr==b) {
2610                 start = a;  //did it!  we go from a to b
2611                 end   = b;
2612                 //printf("A to B\n");
2613                 break;
2614             }
2615         }
2616         if (!start) {//didn't work?  let's try the other direction
2617             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2618                 if (curr==a) {
2619                     start = b;  //did it!  we go from b to a
2620                     end   = a;
2621                     //printf("B to A\n");
2622                     break;
2623                 }
2624             }
2625         }
2626         if (!start) {
2627             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2628                                                      _("Cannot find path between nodes."));
2629             return;
2630         }
2634         //Copy everything after 'end' to a new subpath
2635        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2636         for (curr=end ; curr ; curr=curr->n.other) {
2637             NRPathcode code = (NRPathcode) curr->code;
2638             if (curr == end)
2639                 code = NR_MOVETO;
2640             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2641                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2642         }
2644         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2645         for (curr = start->n.other ; curr  ; curr=next) {
2646             next = curr->n.other;
2647             sp_nodepath_node_destroy(curr);
2648         }
2650     }
2651     //###########################################
2652     //# END EDITS
2653     //###########################################
2655     //clean up the nodepath (such as for trivial subpaths)
2656     sp_nodepath_cleanup(nodepath);
2658     sp_nodepath_update_handles(nodepath);
2660     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2662     sp_nodepath_update_statusbar(nodepath);
2665 /**
2666  * Call sp_nodepath_set_line() for all selected segments.
2667  */
2668 void
2669 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2671     if (nodepath == NULL) return;
2673     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2674        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2675         g_assert(n->selected);
2676         if (n->p.other && n->p.other->selected) {
2677             sp_nodepath_set_line_type(n, code);
2678         }
2679     }
2681     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2684 /**
2685  * Call sp_nodepath_convert_node_type() for all selected nodes.
2686  */
2687 void
2688 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2690     if (nodepath == NULL) return;
2692     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2694     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2695         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2696     }
2698     sp_nodepath_update_repr(nodepath, _("Change node type"));
2701 /**
2702  * Change select status of node, update its own and neighbour handles.
2703  */
2704 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2706     node->selected = selected;
2708     if (selected) {
2709         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2710         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2711         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2712         sp_knot_update_ctrl(node->knot);
2713     } else {
2714         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2715         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2716         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2717         sp_knot_update_ctrl(node->knot);
2718     }
2720     sp_node_update_handles(node);
2721     if (node->n.other) sp_node_update_handles(node->n.other);
2722     if (node->p.other) sp_node_update_handles(node->p.other);
2725 /**
2726 \brief Select a node
2727 \param node     The node to select
2728 \param incremental   If true, add to selection, otherwise deselect others
2729 \param override   If true, always select this node, otherwise toggle selected status
2730 */
2731 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2733     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2735     if (incremental) {
2736         if (override) {
2737             if (!g_list_find(nodepath->selected, node)) {
2738                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2739             }
2740             sp_node_set_selected(node, TRUE);
2741         } else { // toggle
2742             if (node->selected) {
2743                 g_assert(g_list_find(nodepath->selected, node));
2744                 nodepath->selected = g_list_remove(nodepath->selected, node);
2745             } else {
2746                 g_assert(!g_list_find(nodepath->selected, node));
2747                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2748             }
2749             sp_node_set_selected(node, !node->selected);
2750         }
2751     } else {
2752         sp_nodepath_deselect(nodepath);
2753         nodepath->selected = g_list_prepend(nodepath->selected, node);
2754         sp_node_set_selected(node, TRUE);
2755     }
2757     sp_nodepath_update_statusbar(nodepath);
2761 /**
2762 \brief Deselect all nodes in the nodepath
2763 */
2764 void
2765 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2767     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2769     while (nodepath->selected) {
2770         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2771         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2772     }
2773     sp_nodepath_update_statusbar(nodepath);
2776 /**
2777 \brief Select or invert selection of all nodes in the nodepath
2778 */
2779 void
2780 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2782     if (!nodepath) return;
2784     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2785        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2786         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2787            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2788            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2789         }
2790     }
2793 /**
2794  * If nothing selected, does the same as sp_nodepath_select_all();
2795  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2796  * (i.e., similar to "select all in layer", with the "selected" subpaths
2797  * being treated as "layers" in the path).
2798  */
2799 void
2800 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2802     if (!nodepath) return;
2804     if (g_list_length (nodepath->selected) == 0) {
2805         sp_nodepath_select_all (nodepath, invert);
2806         return;
2807     }
2809     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2810     GSList *subpaths = NULL;
2812     for (GList *l = copy; l != NULL; l = l->next) {
2813         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2814         Inkscape::NodePath::SubPath *subpath = n->subpath;
2815         if (!g_slist_find (subpaths, subpath))
2816             subpaths = g_slist_prepend (subpaths, subpath);
2817     }
2819     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2820         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->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? !g_list_find(copy, node) : TRUE);
2824         }
2825     }
2827     g_slist_free (subpaths);
2828     g_list_free (copy);
2831 /**
2832  * \brief Select the node after the last selected; if none is selected,
2833  * select the first within path.
2834  */
2835 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2837     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2839    Inkscape::NodePath::Node *last = NULL;
2840     if (nodepath->selected) {
2841         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2842            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2843             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2844             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2845                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2846                 if (node->selected) {
2847                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2848                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2849                             if (spl->next) { // there's a next subpath
2850                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2851                                 last = subpath_next->first;
2852                             } else if (spl->prev) { // there's a previous subpath
2853                                 last = NULL; // to be set later to the first node of first subpath
2854                             } else {
2855                                 last = node->n.other;
2856                             }
2857                         } else {
2858                             last = node->n.other;
2859                         }
2860                     } else {
2861                         if (node->n.other) {
2862                             last = node->n.other;
2863                         } else {
2864                             if (spl->next) { // there's a next subpath
2865                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2866                                 last = subpath_next->first;
2867                             } else if (spl->prev) { // there's a previous subpath
2868                                 last = NULL; // to be set later to the first node of first subpath
2869                             } else {
2870                                 last = (Inkscape::NodePath::Node *) subpath->first;
2871                             }
2872                         }
2873                     }
2874                 }
2875             }
2876         }
2877         sp_nodepath_deselect(nodepath);
2878     }
2880     if (last) { // there's at least one more node after selected
2881         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2882     } else { // no more nodes, select the first one in first subpath
2883        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2884         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2885     }
2888 /**
2889  * \brief Select the node before the first selected; if none is selected,
2890  * select the last within path
2891  */
2892 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2894     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2896    Inkscape::NodePath::Node *last = NULL;
2897     if (nodepath->selected) {
2898         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2899            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2900             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2901                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2902                 if (node->selected) {
2903                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2904                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2905                             if (spl->prev) { // there's a prev subpath
2906                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2907                                 last = subpath_prev->last;
2908                             } else if (spl->next) { // there's a next subpath
2909                                 last = NULL; // to be set later to the last node of last subpath
2910                             } else {
2911                                 last = node->p.other;
2912                             }
2913                         } else {
2914                             last = node->p.other;
2915                         }
2916                     } else {
2917                         if (node->p.other) {
2918                             last = node->p.other;
2919                         } else {
2920                             if (spl->prev) { // there's a prev subpath
2921                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2922                                 last = subpath_prev->last;
2923                             } else if (spl->next) { // there's a next subpath
2924                                 last = NULL; // to be set later to the last node of last subpath
2925                             } else {
2926                                 last = (Inkscape::NodePath::Node *) subpath->last;
2927                             }
2928                         }
2929                     }
2930                 }
2931             }
2932         }
2933         sp_nodepath_deselect(nodepath);
2934     }
2936     if (last) { // there's at least one more node before selected
2937         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2938     } else { // no more nodes, select the last one in last subpath
2939         GList *spl = g_list_last(nodepath->subpaths);
2940        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2941         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2942     }
2945 /**
2946  * \brief Select all nodes that are within the rectangle.
2947  */
2948 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2950     if (!incremental) {
2951         sp_nodepath_deselect(nodepath);
2952     }
2954     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2955        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2956         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2957            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2959             if (b.contains(node->pos)) {
2960                 sp_nodepath_node_select(node, TRUE, TRUE);
2961             }
2962         }
2963     }
2967 void
2968 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2970     g_assert (n);
2971     g_assert (nodepath);
2972     g_assert (n->subpath->nodepath == nodepath);
2974     if (g_list_length (nodepath->selected) == 0) {
2975         if (grow > 0) {
2976             sp_nodepath_node_select(n, TRUE, TRUE);
2977         }
2978         return;
2979     }
2981     if (g_list_length (nodepath->selected) == 1) {
2982         if (grow < 0) {
2983             sp_nodepath_deselect (nodepath);
2984             return;
2985         }
2986     }
2988         double n_sel_range = 0, p_sel_range = 0;
2989             Inkscape::NodePath::Node *farthest_n_node = n;
2990             Inkscape::NodePath::Node *farthest_p_node = n;
2992         // Calculate ranges
2993         {
2994             double n_range = 0, p_range = 0;
2995             bool n_going = true, p_going = true;
2996             Inkscape::NodePath::Node *n_node = n;
2997             Inkscape::NodePath::Node *p_node = n;
2998             do {
2999                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3000                 if (n_node && n_going)
3001                     n_node = n_node->n.other;
3002                 if (n_node == NULL) {
3003                     n_going = false;
3004                 } else {
3005                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3006                     if (n_node->selected) {
3007                         n_sel_range = n_range;
3008                         farthest_n_node = n_node;
3009                     }
3010                     if (n_node == p_node) {
3011                         n_going = false;
3012                         p_going = false;
3013                     }
3014                 }
3015                 if (p_node && p_going)
3016                     p_node = p_node->p.other;
3017                 if (p_node == NULL) {
3018                     p_going = false;
3019                 } else {
3020                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3021                     if (p_node->selected) {
3022                         p_sel_range = p_range;
3023                         farthest_p_node = p_node;
3024                     }
3025                     if (p_node == n_node) {
3026                         n_going = false;
3027                         p_going = false;
3028                     }
3029                 }
3030             } while (n_going || p_going);
3031         }
3033     if (grow > 0) {
3034         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3035                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3036         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3037                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3038         }
3039     } else {
3040         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3041                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3042         } else if (farthest_p_node && farthest_p_node->selected) {
3043                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3044         }
3045     }
3048 void
3049 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3051     g_assert (n);
3052     g_assert (nodepath);
3053     g_assert (n->subpath->nodepath == nodepath);
3055     if (g_list_length (nodepath->selected) == 0) {
3056         if (grow > 0) {
3057             sp_nodepath_node_select(n, TRUE, TRUE);
3058         }
3059         return;
3060     }
3062     if (g_list_length (nodepath->selected) == 1) {
3063         if (grow < 0) {
3064             sp_nodepath_deselect (nodepath);
3065             return;
3066         }
3067     }
3069     Inkscape::NodePath::Node *farthest_selected = NULL;
3070     double farthest_dist = 0;
3072     Inkscape::NodePath::Node *closest_unselected = NULL;
3073     double closest_dist = NR_HUGE;
3075     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3076        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3077         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3078            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3079            if (node == n)
3080                continue;
3081            if (node->selected) {
3082                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3083                    farthest_dist = NR::L2(node->pos - n->pos);
3084                    farthest_selected = node;
3085                }
3086            } else {
3087                if (NR::L2(node->pos - n->pos) < closest_dist) {
3088                    closest_dist = NR::L2(node->pos - n->pos);
3089                    closest_unselected = node;
3090                }
3091            }
3092         }
3093     }
3095     if (grow > 0) {
3096         if (closest_unselected) {
3097             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3098         }
3099     } else {
3100         if (farthest_selected) {
3101             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3102         }
3103     }
3107 /**
3108 \brief  Saves all nodes' and handles' current positions in their origin members
3109 */
3110 void
3111 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3113     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3114        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3115         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3116            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3117            n->origin = n->pos;
3118            n->p.origin = n->p.pos;
3119            n->n.origin = n->n.pos;
3120         }
3121     }
3124 /**
3125 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3126 */
3127 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3129     if (!nodepath->selected) {
3130         return NULL;
3131     }
3133     GList *r = NULL;
3134     guint i = 0;
3135     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3136        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3137         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3138            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3139             i++;
3140             if (node->selected) {
3141                 r = g_list_append(r, GINT_TO_POINTER(i));
3142             }
3143         }
3144     }
3145     return r;
3148 /**
3149 \brief  Restores selection by selecting nodes whose positions are in the list
3150 */
3151 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3153     sp_nodepath_deselect(nodepath);
3155     guint i = 0;
3156     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3157        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3158         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3159            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3160             i++;
3161             if (g_list_find(r, GINT_TO_POINTER(i))) {
3162                 sp_nodepath_node_select(node, TRUE, TRUE);
3163             }
3164         }
3165     }
3169 /**
3170 \brief Adjusts handle according to node type and line code.
3171 */
3172 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3174     g_assert(node);
3176    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3177    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3179    // nothing to do if we are an end node
3180     if (me->other == NULL) return;
3181     if (other->other == NULL) return;
3183     // nothing to do if we are a cusp node
3184     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3186     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3187     NRPathcode mecode;
3188     if (which_adjust == 1) {
3189         mecode = (NRPathcode)me->other->code;
3190     } else {
3191         mecode = (NRPathcode)node->code;
3192     }
3193     if (mecode == NR_LINETO) return;
3195     if (sp_node_side_is_line(node, other)) {
3196         // other is a line, and we are either smooth or symm
3197        Inkscape::NodePath::Node *othernode = other->other;
3198         double len = NR::L2(me->pos - node->pos);
3199         NR::Point delta = node->pos - othernode->pos;
3200         double linelen = NR::L2(delta);
3201         if (linelen < 1e-18)
3202             return;
3203         me->pos = node->pos + (len / linelen)*delta;
3204         return;
3205     }
3207     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3208         // symmetrize 
3209         me->pos = 2 * node->pos - other->pos;
3210         return;
3211     } else {
3212         // smoothify
3213         double len = NR::L2(me->pos - node->pos);
3214         NR::Point delta = other->pos - node->pos;
3215         double otherlen = NR::L2(delta);
3216         if (otherlen < 1e-18) return;
3217         me->pos = node->pos - (len / otherlen) * delta;
3218     }
3221 /**
3222  \brief Adjusts both handles according to node type and line code
3223  */
3224 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3226     g_assert(node);
3228     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3230     /* we are either smooth or symm */
3232     if (node->p.other == NULL) return;
3233     if (node->n.other == NULL) return;
3235     if (sp_node_side_is_line(node, &node->p)) {
3236         sp_node_adjust_handle(node, 1);
3237         return;
3238     }
3240     if (sp_node_side_is_line(node, &node->n)) {
3241         sp_node_adjust_handle(node, -1);
3242         return;
3243     }
3245     /* both are curves */
3246     NR::Point const delta( node->n.pos - node->p.pos );
3248     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3249         node->p.pos = node->pos - delta / 2;
3250         node->n.pos = node->pos + delta / 2;
3251         return;
3252     }
3254     /* We are smooth */
3255     double plen = NR::L2(node->p.pos - node->pos);
3256     if (plen < 1e-18) return;
3257     double nlen = NR::L2(node->n.pos - node->pos);
3258     if (nlen < 1e-18) return;
3259     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3260     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3263 /**
3264  * Node event callback.
3265  */
3266 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3268     gboolean ret = FALSE;
3269     switch (event->type) {
3270         case GDK_ENTER_NOTIFY:
3271             Inkscape::NodePath::Path::active_node = n;
3272             break;
3273         case GDK_LEAVE_NOTIFY:
3274             Inkscape::NodePath::Path::active_node = NULL;
3275             break;
3276         case GDK_SCROLL:
3277             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3278                 switch (event->scroll.direction) {
3279                     case GDK_SCROLL_UP:
3280                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3281                         break;
3282                     case GDK_SCROLL_DOWN:
3283                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3284                         break;
3285                     default:
3286                         break;
3287                 }
3288                 ret = TRUE;
3289             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3290                 switch (event->scroll.direction) {
3291                     case GDK_SCROLL_UP:
3292                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3293                         break;
3294                     case GDK_SCROLL_DOWN:
3295                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3296                         break;
3297                     default:
3298                         break;
3299                 }
3300                 ret = TRUE;
3301             }
3302             break;
3303         case GDK_KEY_PRESS:
3304             switch (get_group0_keyval (&event->key)) {
3305                 case GDK_space:
3306                     if (event->key.state & GDK_BUTTON1_MASK) {
3307                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3308                         stamp_repr(nodepath);
3309                         ret = TRUE;
3310                     }
3311                     break;
3312                 case GDK_Page_Up:
3313                     if (event->key.state & GDK_CONTROL_MASK) {
3314                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3315                     } else {
3316                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3317                     }
3318                     break;
3319                 case GDK_Page_Down:
3320                     if (event->key.state & GDK_CONTROL_MASK) {
3321                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3322                     } else {
3323                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3324                     }
3325                     break;
3326                 default:
3327                     break;
3328             }
3329             break;
3330         default:
3331             break;
3332     }
3334     return ret;
3337 /**
3338  * Handle keypress on node; directly called.
3339  */
3340 gboolean node_key(GdkEvent *event)
3342     Inkscape::NodePath::Path *np;
3344     // there is no way to verify nodes so set active_node to nil when deleting!!
3345     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3347     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3348         gint ret = FALSE;
3349         switch (get_group0_keyval (&event->key)) {
3350             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3351             case GDK_BackSpace:
3352                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3353                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3354                 sp_nodepath_update_repr(np, _("Delete node"));
3355                 Inkscape::NodePath::Path::active_node = NULL;
3356                 ret = TRUE;
3357                 break;
3358             case GDK_c:
3359                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3360                 ret = TRUE;
3361                 break;
3362             case GDK_s:
3363                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3364                 ret = TRUE;
3365                 break;
3366             case GDK_y:
3367                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3368                 ret = TRUE;
3369                 break;
3370             case GDK_b:
3371                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3372                 ret = TRUE;
3373                 break;
3374         }
3375         return ret;
3376     }
3377     return FALSE;
3380 /**
3381  * Mouseclick on node callback.
3382  */
3383 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3385    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3387     if (state & GDK_CONTROL_MASK) {
3388         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3390         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3391             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3392                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3393             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3394                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3395             } else {
3396                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3397             }
3398             sp_nodepath_update_repr(nodepath, _("Change node type"));
3399             sp_nodepath_update_statusbar(nodepath);
3401         } else { //ctrl+alt+click: delete node
3402             GList *node_to_delete = NULL;
3403             node_to_delete = g_list_append(node_to_delete, n);
3404             sp_node_delete_preserve(node_to_delete);
3405         }
3407     } else {
3408         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3409     }
3412 /**
3413  * Mouse grabbed node callback.
3414  */
3415 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3417    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3419     if (!n->selected) {
3420         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3421     }
3423     n->is_dragging = true;
3424     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3426     sp_nodepath_remember_origins (n->subpath->nodepath);
3429 /**
3430  * Mouse ungrabbed node callback.
3431  */
3432 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3434    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3436    n->dragging_out = NULL;
3437    n->is_dragging = false;
3438    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3440    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3443 /**
3444  * The point on a line, given by its angle, closest to the given point.
3445  * \param p  A point.
3446  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3447  * \param closest  Pointer to the point struct where the result is stored.
3448  * \todo FIXME: use dot product perhaps?
3449  */
3450 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3452     if (a == HUGE_VAL) { // vertical
3453         *closest = NR::Point(0, (*p)[NR::Y]);
3454     } else {
3455         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3456         (*closest)[NR::Y] = a * (*closest)[NR::X];
3457     }
3460 /**
3461  * Distance from the point to a line given by its angle.
3462  * \param p  A point.
3463  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3464  */
3465 static double point_line_distance(NR::Point *p, double a)
3467     NR::Point c;
3468     point_line_closest(p, a, &c);
3469     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]));
3472 /**
3473  * Callback for node "request" signal.
3474  * \todo fixme: This goes to "moved" event? (lauris)
3475  */
3476 static gboolean
3477 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3479     double yn, xn, yp, xp;
3480     double an, ap, na, pa;
3481     double d_an, d_ap, d_na, d_pa;
3482     gboolean collinear = FALSE;
3483     NR::Point c;
3484     NR::Point pr;
3486     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3488     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3490     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3491     if ( (!n->subpath->nodepath->straight_path) &&
3492          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3493            || n->dragging_out ) )
3494     {
3495        NR::Point mouse = (*p);
3497        if (!n->dragging_out) {
3498            // This is the first drag-out event; find out which handle to drag out
3499            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3500            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3502            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3503                return FALSE;
3505            Inkscape::NodePath::NodeSide *opposite;
3506            if (appr_p > appr_n) { // closer to p
3507                n->dragging_out = &n->p;
3508                opposite = &n->n;
3509                n->code = NR_CURVETO;
3510            } else if (appr_p < appr_n) { // closer to n
3511                n->dragging_out = &n->n;
3512                opposite = &n->p;
3513                n->n.other->code = NR_CURVETO;
3514            } else { // p and n nodes are the same
3515                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3516                    n->dragging_out = &n->p;
3517                    opposite = &n->n;
3518                    n->code = NR_CURVETO;
3519                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3520                    n->dragging_out = &n->n;
3521                    opposite = &n->p;
3522                    n->n.other->code = NR_CURVETO;
3523                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3524                    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);
3525                    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);
3526                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3527                        n->dragging_out = &n->n;
3528                        opposite = &n->p;
3529                        n->n.other->code = NR_CURVETO;
3530                    } else { // closer to other's n handle
3531                        n->dragging_out = &n->p;
3532                        opposite = &n->n;
3533                        n->code = NR_CURVETO;
3534                    }
3535                }
3536            }
3538            // if there's another handle, make sure the one we drag out starts parallel to it
3539            if (opposite->pos != n->pos) {
3540                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3541            }
3543            // knots might not be created yet!
3544            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3545            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3546        }
3548        // pass this on to the handle-moved callback
3549        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3550        sp_node_update_handles(n);
3551        return TRUE;
3552    }
3554     if (state & GDK_CONTROL_MASK) { // constrained motion
3556         // calculate relative distances of handles
3557         // n handle:
3558         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3559         xn = n->n.pos[NR::X] - n->pos[NR::X];
3560         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3561         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3562             if (n->n.other) { // if there is the next point
3563                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3564                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3565                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3566             }
3567         }
3568         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3569         if (yn < 0) { xn = -xn; yn = -yn; }
3571         // p handle:
3572         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3573         xp = n->p.pos[NR::X] - n->pos[NR::X];
3574         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3575         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3576             if (n->p.other) {
3577                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3578                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3579                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3580             }
3581         }
3582         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3583         if (yp < 0) { xp = -xp; yp = -yp; }
3585         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3586             // sliding on handles, only if at least one of the handles is non-vertical
3587             // (otherwise it's the same as ctrl+drag anyway)
3589             // calculate angles of the handles
3590             if (xn == 0) {
3591                 if (yn == 0) { // no handle, consider it the continuation of the other one
3592                     an = 0;
3593                     collinear = TRUE;
3594                 }
3595                 else an = 0; // vertical; set the angle to horizontal
3596             } else an = yn/xn;
3598             if (xp == 0) {
3599                 if (yp == 0) { // no handle, consider it the continuation of the other one
3600                     ap = an;
3601                 }
3602                 else ap = 0; // vertical; set the angle to horizontal
3603             } else  ap = yp/xp;
3605             if (collinear) an = ap;
3607             // angles of the perpendiculars; HUGE_VAL means vertical
3608             if (an == 0) na = HUGE_VAL; else na = -1/an;
3609             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3611             // mouse point relative to the node's original pos
3612             pr = (*p) - n->origin;
3614             // distances to the four lines (two handles and two perpendiculars)
3615             d_an = point_line_distance(&pr, an);
3616             d_na = point_line_distance(&pr, na);
3617             d_ap = point_line_distance(&pr, ap);
3618             d_pa = point_line_distance(&pr, pa);
3620             // find out which line is the closest, save its closest point in c
3621             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3622                 point_line_closest(&pr, an, &c);
3623             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3624                 point_line_closest(&pr, ap, &c);
3625             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3626                 point_line_closest(&pr, na, &c);
3627             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3628                 point_line_closest(&pr, pa, &c);
3629             }
3631             // move the node to the closest point
3632             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3633                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3634                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3635                                             true);
3637         } else {  // constraining to hor/vert
3639             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3640                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3641                                                 (*p)[NR::X] - n->pos[NR::X], 
3642                                                 n->origin[NR::Y] - n->pos[NR::Y],
3643                                                 true, 
3644                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3645             } else { // snap to vert
3646                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3647                                                 n->origin[NR::X] - n->pos[NR::X],
3648                                                 (*p)[NR::Y] - n->pos[NR::Y],
3649                                                 true,
3650                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3651             }
3652         }
3653     } else { // move freely
3654         if (n->is_dragging) {
3655             if (state & GDK_MOD1_MASK) { // sculpt
3656                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3657             } else {
3658                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3659                                             (*p)[NR::X] - n->pos[NR::X],
3660                                             (*p)[NR::Y] - n->pos[NR::Y],
3661                                             (state & GDK_SHIFT_MASK) == 0);
3662             }
3663         }
3664     }
3666     n->subpath->nodepath->desktop->scroll_to_point(p);
3668     return TRUE;
3671 /**
3672  * Node handle clicked callback.
3673  */
3674 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3676    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3678     if (state & GDK_CONTROL_MASK) { // "delete" handle
3679         if (n->p.knot == knot) {
3680             n->p.pos = n->pos;
3681         } else if (n->n.knot == knot) {
3682             n->n.pos = n->pos;
3683         }
3684         sp_node_update_handles(n);
3685         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3686         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3687         sp_nodepath_update_statusbar(nodepath);
3689     } else { // just select or add to selection, depending in Shift
3690         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3691     }
3694 /**
3695  * Node handle grabbed callback.
3696  */
3697 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3699    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3701     if (!n->selected) {
3702         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3703     }
3705     // remember the origin point of the handle
3706     if (n->p.knot == knot) {
3707         n->p.origin_radial = n->p.pos - n->pos;
3708     } else if (n->n.knot == knot) {
3709         n->n.origin_radial = n->n.pos - n->pos;
3710     } else {
3711         g_assert_not_reached();
3712     }
3714     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3717 /**
3718  * Node handle ungrabbed callback.
3719  */
3720 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3722    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3724     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3725     if (n->p.knot == knot) {
3726         n->p.origin_radial.a = 0;
3727         sp_knot_set_position(knot, n->p.pos, state);
3728     } else if (n->n.knot == knot) {
3729         n->n.origin_radial.a = 0;
3730         sp_knot_set_position(knot, n->n.pos, state);
3731     } else {
3732         g_assert_not_reached();
3733     }
3735     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3738 /**
3739  * Node handle "request" signal callback.
3740  */
3741 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3743     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3745     Inkscape::NodePath::NodeSide *me, *opposite;
3746     gint which;
3747     if (n->p.knot == knot) {
3748         me = &n->p;
3749         opposite = &n->n;
3750         which = -1;
3751     } else if (n->n.knot == knot) {
3752         me = &n->n;
3753         opposite = &n->p;
3754         which = 1;
3755     } else {
3756         me = opposite = NULL;
3757         which = 0;
3758         g_assert_not_reached();
3759     }
3761     SPDesktop *desktop = n->subpath->nodepath->desktop;
3762     SnapManager &m = desktop->namedview->snap_manager;
3763     m.setup(desktop, n->subpath->nodepath->item);
3764     Inkscape::SnappedPoint s;
3765     
3766     if ((state & GDK_SHIFT_MASK) != 0) {
3767         // We will not try to snap when the shift-key is pressed
3768         // so remove the old snap indicator and don't wait for it to time-out  
3769         desktop->snapindicator->remove_snappoint();     
3770     }
3772     Inkscape::NodePath::Node *othernode = opposite->other;
3773     if (othernode) {
3774         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3775             /* We are smooth node adjacent with line */
3776             NR::Point const delta = *p - n->pos;
3777             NR::Coord const len = NR::L2(delta);
3778             Inkscape::NodePath::Node *othernode = opposite->other;
3779             NR::Point const ndelta = n->pos - othernode->pos;
3780             NR::Coord const linelen = NR::L2(ndelta);
3781             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3782                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3783                 (*p) = n->pos + (scal / linelen) * ndelta;
3784             }
3785             if ((state & GDK_SHIFT_MASK) == 0) {
3786                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p), Inkscape::Snapper::ConstraintLine(*p, ndelta));
3787             }
3788         } else {
3789                 if ((state & GDK_SHIFT_MASK) == 0) {
3790                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3791                 }
3792         }
3793     } else {
3794         if ((state & GDK_SHIFT_MASK) == 0) {
3795                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3796         }
3797     }
3798     
3799     Geom::Point pt2g = *p;
3800     s.getPoint(pt2g);
3801     *p = pt2g;
3802     
3803     sp_node_adjust_handle(n, -which);
3805     return FALSE;
3808 /**
3809  * Node handle moved callback.
3810  */
3811 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3813    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3815    Inkscape::NodePath::NodeSide *me;
3816    Inkscape::NodePath::NodeSide *other;
3817     if (n->p.knot == knot) {
3818         me = &n->p;
3819         other = &n->n;
3820     } else if (n->n.knot == knot) {
3821         me = &n->n;
3822         other = &n->p;
3823     } else {
3824         me = NULL;
3825         other = NULL;
3826         g_assert_not_reached();
3827     }
3829     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3830     Radial rme(me->pos - n->pos);
3831     Radial rother(other->pos - n->pos);
3832     Radial rnew(*p - n->pos);
3834     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3835         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3836         /* 0 interpreted as "no snapping". */
3838         // 1. Snap to the closest PI/snaps angle, starting from zero.
3839         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3841         // 2. Snap to the original angle, its opposite and perpendiculars
3842         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3843             /* The closest PI/2 angle, starting from original angle */
3844             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3846             // Snap to the closest.
3847             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3848                        ? a_snapped
3849                        : a_ortho );
3850         }
3852         // 3. Snap to the angle of the opposite line, if any
3853         Inkscape::NodePath::Node *othernode = other->other;
3854         if (othernode) {
3855             NR::Point other_to_snap(0,0);
3856             if (sp_node_side_is_line(n, other)) {
3857                 other_to_snap = othernode->pos - n->pos;
3858             } else {
3859                 other_to_snap = other->pos - n->pos;
3860             }
3861             if (NR::L2(other_to_snap) > 1e-3) {
3862                 Radial rother_to_snap(other_to_snap);
3863                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3864                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3866                 // Snap to the closest.
3867                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3868                        ? a_snapped
3869                        : a_oppo );
3870             }
3871         }
3873         rnew.a = a_snapped;
3874     }
3876     if (state & GDK_MOD1_MASK) {
3877         // lock handle length
3878         rnew.r = me->origin_radial.r;
3879     }
3881     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3882         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3883         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3884         rother.a += rnew.a - rme.a;
3885         other->pos = NR::Point(rother) + n->pos;
3886         if (other->knot) {
3887             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3888             sp_knot_moveto(other->knot, other->pos);
3889         }
3890     }
3892     me->pos = NR::Point(rnew) + n->pos;
3893     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3895     // move knot, but without emitting the signal:
3896     // we cannot emit a "moved" signal because we're now processing it
3897     sp_knot_moveto(me->knot, me->pos);
3899     update_object(n->subpath->nodepath);
3901     /* status text */
3902     SPDesktop *desktop = n->subpath->nodepath->desktop;
3903     if (!desktop) return;
3904     SPEventContext *ec = desktop->event_context;
3905     if (!ec) return;
3906     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3907     if (!mc) return;
3909     double degrees = 180 / M_PI * rnew.a;
3910     if (degrees > 180) degrees -= 360;
3911     if (degrees < -180) degrees += 360;
3912     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3913         degrees = angle_to_compass (degrees);
3915     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3917     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3918          _("<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);
3920     g_string_free(length, TRUE);
3923 /**
3924  * Node handle event callback.
3925  */
3926 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3928     gboolean ret = FALSE;
3929     switch (event->type) {
3930         case GDK_KEY_PRESS:
3931             switch (get_group0_keyval (&event->key)) {
3932                 case GDK_space:
3933                     if (event->key.state & GDK_BUTTON1_MASK) {
3934                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3935                         stamp_repr(nodepath);
3936                         ret = TRUE;
3937                     }
3938                     break;
3939                 default:
3940                     break;
3941             }
3942             break;
3943         case GDK_ENTER_NOTIFY:
3944             // we use an experimentally determined threshold that seems to work fine
3945             if (NR::L2(n->pos - knot->pos) < 0.75)
3946                 Inkscape::NodePath::Path::active_node = n;
3947             break;
3948         case GDK_LEAVE_NOTIFY:
3949             // we use an experimentally determined threshold that seems to work fine
3950             if (NR::L2(n->pos - knot->pos) < 0.75)
3951                 Inkscape::NodePath::Path::active_node = NULL;
3952             break;
3953         default:
3954             break;
3955     }
3957     return ret;
3960 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3961                                  Radial &rme, Radial &rother, gboolean const both)
3963     rme.a += angle;
3964     if ( both
3965          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3966          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3967     {
3968         rother.a += angle;
3969     }
3972 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3973                                         Radial &rme, Radial &rother, gboolean const both)
3975     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3977     gdouble r;
3978     if ( both
3979          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3980          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3981     {
3982         r = MAX(rme.r, rother.r);
3983     } else {
3984         r = rme.r;
3985     }
3987     gdouble const weird_angle = atan2(norm_angle, r);
3988 /* Bulia says norm_angle is just the visible distance that the
3989  * object's end must travel on the screen.  Left as 'angle' for want of
3990  * a better name.*/
3992     rme.a += weird_angle;
3993     if ( both
3994          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3995          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3996     {
3997         rother.a += weird_angle;
3998     }
4001 /**
4002  * Rotate one node.
4003  */
4004 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4006     Inkscape::NodePath::NodeSide *me, *other;
4007     bool both = false;
4009     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4010     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4012     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4013         me = &(n->p);
4014         other = &(n->n);
4015     } else if (!n->p.other) {
4016         me = &(n->n);
4017         other = &(n->p);
4018     } else {
4019         if (which > 0) { // right handle
4020             if (xn > xp) {
4021                 me = &(n->n);
4022                 other = &(n->p);
4023             } else {
4024                 me = &(n->p);
4025                 other = &(n->n);
4026             }
4027         } else if (which < 0){ // left handle
4028             if (xn <= xp) {
4029                 me = &(n->n);
4030                 other = &(n->p);
4031             } else {
4032                 me = &(n->p);
4033                 other = &(n->n);
4034             }
4035         } else { // both handles
4036             me = &(n->n);
4037             other = &(n->p);
4038             both = true;
4039         }
4040     }
4042     Radial rme(me->pos - n->pos);
4043     Radial rother(other->pos - n->pos);
4045     if (screen) {
4046         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4047     } else {
4048         node_rotate_one_internal (*n, angle, rme, rother, both);
4049     }
4051     me->pos = n->pos + NR::Point(rme);
4053     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4054         other->pos =  n->pos + NR::Point(rother);
4055     }
4057     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4058     // so here we just move all the knots without emitting move signals, for speed
4059     sp_node_update_handles(n, false);
4062 /**
4063  * Rotate selected nodes.
4064  */
4065 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4067     if (!nodepath || !nodepath->selected) return;
4069     if (g_list_length(nodepath->selected) == 1) {
4070        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4071         node_rotate_one (n, angle, which, screen);
4072     } else {
4073        // rotate as an object:
4075         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4076         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4077         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4078             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4079             box.expandTo (n->pos); // contain all selected nodes
4080         }
4082         gdouble rot;
4083         if (screen) {
4084             gdouble const zoom = nodepath->desktop->current_zoom();
4085             gdouble const zmove = angle / zoom;
4086             gdouble const r = NR::L2(box.max() - box.midpoint());
4087             rot = atan2(zmove, r);
4088         } else {
4089             rot = angle;
4090         }
4092         NR::Point rot_center;
4093         if (Inkscape::NodePath::Path::active_node == NULL)
4094             rot_center = box.midpoint();
4095         else
4096             rot_center = Inkscape::NodePath::Path::active_node->pos;
4098         NR::Matrix t =
4099             NR::Matrix (NR::translate(-rot_center)) *
4100             NR::Matrix (NR::rotate(rot)) *
4101             NR::Matrix (NR::translate(rot_center));
4103         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4104             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4105             n->pos *= t;
4106             n->n.pos *= t;
4107             n->p.pos *= t;
4108             sp_node_update_handles(n, false);
4109         }
4110     }
4112     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4115 /**
4116  * Scale one node.
4117  */
4118 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4120     bool both = false;
4121     Inkscape::NodePath::NodeSide *me, *other;
4123     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4124     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4126     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4127         me = &(n->p);
4128         other = &(n->n);
4129         n->code = NR_CURVETO;
4130     } else if (!n->p.other) {
4131         me = &(n->n);
4132         other = &(n->p);
4133         if (n->n.other)
4134             n->n.other->code = NR_CURVETO;
4135     } else {
4136         if (which > 0) { // right handle
4137             if (xn > xp) {
4138                 me = &(n->n);
4139                 other = &(n->p);
4140                 if (n->n.other)
4141                     n->n.other->code = NR_CURVETO;
4142             } else {
4143                 me = &(n->p);
4144                 other = &(n->n);
4145                 n->code = NR_CURVETO;
4146             }
4147         } else if (which < 0){ // left handle
4148             if (xn <= xp) {
4149                 me = &(n->n);
4150                 other = &(n->p);
4151                 if (n->n.other)
4152                     n->n.other->code = NR_CURVETO;
4153             } else {
4154                 me = &(n->p);
4155                 other = &(n->n);
4156                 n->code = NR_CURVETO;
4157             }
4158         } else { // both handles
4159             me = &(n->n);
4160             other = &(n->p);
4161             both = true;
4162             n->code = NR_CURVETO;
4163             if (n->n.other)
4164                 n->n.other->code = NR_CURVETO;
4165         }
4166     }
4168     Radial rme(me->pos - n->pos);
4169     Radial rother(other->pos - n->pos);
4171     rme.r += grow;
4172     if (rme.r < 0) rme.r = 0;
4173     if (rme.a == HUGE_VAL) {
4174         if (me->other) { // if direction is unknown, initialize it towards the next node
4175             Radial rme_next(me->other->pos - n->pos);
4176             rme.a = rme_next.a;
4177         } else { // if there's no next, initialize to 0
4178             rme.a = 0;
4179         }
4180     }
4181     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4182         rother.r += grow;
4183         if (rother.r < 0) rother.r = 0;
4184         if (rother.a == HUGE_VAL) {
4185             rother.a = rme.a + M_PI;
4186         }
4187     }
4189     me->pos = n->pos + NR::Point(rme);
4191     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4192         other->pos = n->pos + NR::Point(rother);
4193     }
4195     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4196     // so here we just move all the knots without emitting move signals, for speed
4197     sp_node_update_handles(n, false);
4200 /**
4201  * Scale selected nodes.
4202  */
4203 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4205     if (!nodepath || !nodepath->selected) return;
4207     if (g_list_length(nodepath->selected) == 1) {
4208         // scale handles of the single selected node
4209         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4210         node_scale_one (n, grow, which);
4211     } else {
4212         // scale nodes as an "object":
4214         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4215         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4216         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4217             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4218             box.expandTo (n->pos); // contain all selected nodes
4219         }
4221         double scale = (box.maxExtent() + grow)/box.maxExtent();
4223         NR::Point scale_center;
4224         if (Inkscape::NodePath::Path::active_node == NULL)
4225             scale_center = box.midpoint();
4226         else
4227             scale_center = Inkscape::NodePath::Path::active_node->pos;
4229         NR::Matrix t =
4230             NR::Matrix (NR::translate(-scale_center)) *
4231             NR::Matrix (NR::scale(scale, scale)) *
4232             NR::Matrix (NR::translate(scale_center));
4234         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4235             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4236             n->pos *= t;
4237             n->n.pos *= t;
4238             n->p.pos *= t;
4239             sp_node_update_handles(n, false);
4240         }
4241     }
4243     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4246 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4248     if (!nodepath) return;
4249     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4252 /**
4253  * Flip selected nodes horizontally/vertically.
4254  */
4255 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, boost::optional<NR::Point> center)
4257     if (!nodepath || !nodepath->selected) return;
4259     if (g_list_length(nodepath->selected) == 1 && !center) {
4260         // flip handles of the single selected node
4261         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4262         double temp = n->p.pos[axis];
4263         n->p.pos[axis] = n->n.pos[axis];
4264         n->n.pos[axis] = temp;
4265         sp_node_update_handles(n, false);
4266     } else {
4267         // scale nodes as an "object":
4269         Geom::Rect box = sp_node_selected_bbox (nodepath);
4270         if (!center) {
4271             center = box.midpoint();
4272         }
4273         NR::Matrix t =
4274             NR::Matrix (NR::translate(- *center)) *
4275             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4276             NR::Matrix (NR::translate(*center));
4278         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4279             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4280             n->pos *= t;
4281             n->n.pos *= t;
4282             n->p.pos *= t;
4283             sp_node_update_handles(n, false);
4284         }
4285     }
4287     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4290 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4292     g_assert (nodepath->selected);
4294     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4295     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4296     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4297         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4298         box.expandTo (n->pos); // contain all selected nodes
4299     }
4300     return box;
4303 //-----------------------------------------------
4304 /**
4305  * Return new subpath under given nodepath.
4306  */
4307 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4309     g_assert(nodepath);
4310     g_assert(nodepath->desktop);
4312    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4314     s->nodepath = nodepath;
4315     s->closed = FALSE;
4316     s->nodes = NULL;
4317     s->first = NULL;
4318     s->last = NULL;
4320     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4321     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4322     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4324     return s;
4327 /**
4328  * Destroy nodes in subpath, then subpath itself.
4329  */
4330 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4332     g_assert(subpath);
4333     g_assert(subpath->nodepath);
4334     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4336     while (subpath->nodes) {
4337         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4338     }
4340     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4342     g_free(subpath);
4345 /**
4346  * Link head to tail in subpath.
4347  */
4348 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4350     g_assert(!sp->closed);
4351     g_assert(sp->last != sp->first);
4352     g_assert(sp->first->code == NR_MOVETO);
4354     sp->closed = TRUE;
4356     //Link the head to the tail
4357     sp->first->p.other = sp->last;
4358     sp->last->n.other  = sp->first;
4359     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4360     sp->first          = sp->last;
4362     //Remove the extra end node
4363     sp_nodepath_node_destroy(sp->last->n.other);
4366 /**
4367  * Open closed (loopy) subpath at node.
4368  */
4369 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4371     g_assert(sp->closed);
4372     g_assert(n->subpath == sp);
4373     g_assert(sp->first == sp->last);
4375     /* We create new startpoint, current node will become last one */
4377    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4378                                                 &n->pos, &n->pos, &n->n.pos);
4381     sp->closed        = FALSE;
4383     //Unlink to make a head and tail
4384     sp->first         = new_path;
4385     sp->last          = n;
4386     n->n.other        = NULL;
4387     new_path->p.other = NULL;
4390 /**
4391  * Return new node in subpath with given properties.
4392  * \param pos Position of node.
4393  * \param ppos Handle position in previous direction
4394  * \param npos Handle position in previous direction
4395  */
4396 Inkscape::NodePath::Node *
4397 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)
4399     g_assert(sp);
4400     g_assert(sp->nodepath);
4401     g_assert(sp->nodepath->desktop);
4403     if (nodechunk == NULL)
4404         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4406     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4408     n->subpath  = sp;
4410     if (type != Inkscape::NodePath::NODE_NONE) {
4411         // use the type from sodipodi:nodetypes
4412         n->type = type;
4413     } else {
4414         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4415             // points are (almost) collinear
4416             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4417                 // endnode, or a node with a retracted handle
4418                 n->type = Inkscape::NodePath::NODE_CUSP;
4419             } else {
4420                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4421             }
4422         } else {
4423             n->type = Inkscape::NodePath::NODE_CUSP;
4424         }
4425     }
4427     n->code     = code;
4428     n->selected = FALSE;
4429     n->pos      = *pos;
4430     n->p.pos    = *ppos;
4431     n->n.pos    = *npos;
4433     n->dragging_out = NULL;
4435     Inkscape::NodePath::Node *prev;
4436     if (next) {
4437         //g_assert(g_list_find(sp->nodes, next));
4438         prev = next->p.other;
4439     } else {
4440         prev = sp->last;
4441     }
4443     if (prev)
4444         prev->n.other = n;
4445     else
4446         sp->first = n;
4448     if (next)
4449         next->p.other = n;
4450     else
4451         sp->last = n;
4453     n->p.other = prev;
4454     n->n.other = next;
4456     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"));
4457     sp_knot_set_position(n->knot, *pos, 0);
4459     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4460     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4461     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4462     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4463     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4464     sp_knot_update_ctrl(n->knot);
4466     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4467     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4468     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4469     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4470     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4471     sp_knot_show(n->knot);
4473     // We only create handle knots and lines on demand
4474     n->p.knot = NULL;
4475     n->p.line = NULL;
4476     n->n.knot = NULL;
4477     n->n.line = NULL;
4479     sp->nodes = g_list_prepend(sp->nodes, n);
4481     return n;
4484 /**
4485  * Destroy node and its knots, link neighbors in subpath.
4486  */
4487 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4489     g_assert(node);
4490     g_assert(node->subpath);
4491     g_assert(SP_IS_KNOT(node->knot));
4493    Inkscape::NodePath::SubPath *sp = node->subpath;
4495     if (node->selected) { // first, deselect
4496         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4497         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4498     }
4500     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4502     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4503     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4504     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4505     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4506     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4507     g_object_unref(G_OBJECT(node->knot));
4509     if (node->p.knot) {
4510         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4511         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4512         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4513         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4514         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4515         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4516         g_object_unref(G_OBJECT(node->p.knot));
4517         node->p.knot = NULL;
4518     }
4520     if (node->n.knot) {
4521         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4522         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4523         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4524         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4525         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4526         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4527         g_object_unref(G_OBJECT(node->n.knot));
4528         node->n.knot = NULL;
4529     }
4531     if (node->p.line)
4532         gtk_object_destroy(GTK_OBJECT(node->p.line));
4533     if (node->n.line)
4534         gtk_object_destroy(GTK_OBJECT(node->n.line));
4536     if (sp->nodes) { // there are others nodes on the subpath
4537         if (sp->closed) {
4538             if (sp->first == node) {
4539                 g_assert(sp->last == node);
4540                 sp->first = node->n.other;
4541                 sp->last = sp->first;
4542             }
4543             node->p.other->n.other = node->n.other;
4544             node->n.other->p.other = node->p.other;
4545         } else {
4546             if (sp->first == node) {
4547                 sp->first = node->n.other;
4548                 sp->first->code = NR_MOVETO;
4549             }
4550             if (sp->last == node) sp->last = node->p.other;
4551             if (node->p.other) node->p.other->n.other = node->n.other;
4552             if (node->n.other) node->n.other->p.other = node->p.other;
4553         }
4554     } else { // this was the last node on subpath
4555         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4556     }
4558     g_mem_chunk_free(nodechunk, node);
4561 /**
4562  * Returns one of the node's two sides.
4563  * \param which Indicates which side.
4564  * \return Pointer to previous node side if which==-1, next if which==1.
4565  */
4566 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4568     g_assert(node);
4570     switch (which) {
4571         case -1:
4572             return &node->p;
4573         case 1:
4574             return &node->n;
4575         default:
4576             break;
4577     }
4579     g_assert_not_reached();
4581     return NULL;
4584 /**
4585  * Return the other side of the node, given one of its sides.
4586  */
4587 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4589     g_assert(node);
4591     if (me == &node->p) return &node->n;
4592     if (me == &node->n) return &node->p;
4594     g_assert_not_reached();
4596     return NULL;
4599 /**
4600  * Return NRPathcode on the given side of the node.
4601  */
4602 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4604     g_assert(node);
4606     if (me == &node->p) {
4607         if (node->p.other) return (NRPathcode)node->code;
4608         return NR_MOVETO;
4609     }
4611     if (me == &node->n) {
4612         if (node->n.other) return (NRPathcode)node->n.other->code;
4613         return NR_MOVETO;
4614     }
4616     g_assert_not_reached();
4618     return NR_END;
4621 /**
4622  * Return node with the given index
4623  */
4624 Inkscape::NodePath::Node *
4625 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4627     Inkscape::NodePath::Node *e = NULL;
4629     if (!nodepath) {
4630         return e;
4631     }
4633     //find segment
4634     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4636         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4637         int n = g_list_length(sp->nodes);
4638         if (sp->closed) {
4639             n++;
4640         }
4642         //if the piece belongs to this subpath grab it
4643         //otherwise move onto the next subpath
4644         if (index < n) {
4645             e = sp->first;
4646             for (int i = 0; i < index; ++i) {
4647                 e = e->n.other;
4648             }
4649             break;
4650         } else {
4651             if (sp->closed) {
4652                 index -= (n+1);
4653             } else {
4654                 index -= n;
4655             }
4656         }
4657     }
4659     return e;
4662 /**
4663  * Returns plain text meaning of node type.
4664  */
4665 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4667     unsigned retracted = 0;
4668     bool endnode = false;
4670     for (int which = -1; which <= 1; which += 2) {
4671         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4672         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4673             retracted ++;
4674         if (!side->other)
4675             endnode = true;
4676     }
4678     if (retracted == 0) {
4679         if (endnode) {
4680                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4681                 return _("end node");
4682         } else {
4683             switch (node->type) {
4684                 case Inkscape::NodePath::NODE_CUSP:
4685                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4686                     return _("cusp");
4687                 case Inkscape::NodePath::NODE_SMOOTH:
4688                     // TRANSLATORS: "smooth" is an adjective here
4689                     return _("smooth");
4690                 case Inkscape::NodePath::NODE_SYMM:
4691                     return _("symmetric");
4692             }
4693         }
4694     } else if (retracted == 1) {
4695         if (endnode) {
4696             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4697             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4698         } else {
4699             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4700         }
4701     } else {
4702         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4703     }
4705     return NULL;
4708 /**
4709  * Handles content of statusbar as long as node tool is active.
4710  */
4711 void
4712 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4714     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");
4715     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4717     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4718     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4719     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4720     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4722     SPDesktop *desktop = NULL;
4723     if (nodepath) {
4724         desktop = nodepath->desktop;
4725     } else {
4726         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4727     }
4729     SPEventContext *ec = desktop->event_context;
4730     if (!ec) return;
4731     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4732     if (!mc) return;
4734     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4736     if (selected_nodes == 0) {
4737         Inkscape::Selection *sel = desktop->selection;
4738         if (!sel || sel->isEmpty()) {
4739             mc->setF(Inkscape::NORMAL_MESSAGE,
4740                      _("Select a single object to edit its nodes or handles."));
4741         } else {
4742             if (nodepath) {
4743             mc->setF(Inkscape::NORMAL_MESSAGE,
4744                      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.",
4745                               "<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.",
4746                               total_nodes),
4747                      total_nodes);
4748             } else {
4749                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4750                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4751                 } else {
4752                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4753                 }
4754             }
4755         }
4756     } else if (nodepath && selected_nodes == 1) {
4757         mc->setF(Inkscape::NORMAL_MESSAGE,
4758                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4759                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4760                           total_nodes),
4761                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4762     } else {
4763         if (selected_subpaths > 1) {
4764             mc->setF(Inkscape::NORMAL_MESSAGE,
4765                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4766                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4767                               total_nodes),
4768                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4769         } else {
4770             mc->setF(Inkscape::NORMAL_MESSAGE,
4771                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4772                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4773                               total_nodes),
4774                      selected_nodes, total_nodes, when_selected);
4775         }
4776     }
4779 /*
4780  * returns a *copy* of the curve of that object.
4781  */
4782 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4783     if (!object)
4784         return NULL;
4786     SPCurve *curve = NULL;
4787     if (SP_IS_PATH(object)) {
4788         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4789         curve = curve_new->copy();
4790     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4791         const gchar *svgd = object->repr->attribute(key);
4792         if (svgd) {
4793             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4794             SPCurve *curve_new = new SPCurve(pv);
4795             if (curve_new) {
4796                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4797             }
4798         }
4799     }
4801     return curve;
4804 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4805     if (!np || !np->object || !curve)
4806         return;
4808     if (SP_IS_PATH(np->object)) {
4809         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4810             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4811         } else {
4812             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4813         }
4814     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4815         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4816         if (pathparam) {
4817             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4818             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4819         }
4820     }
4823 /**
4824 SPCanvasItem *
4825 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4826     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4828 **/
4830 /**
4831 SPCanvasItem *
4832 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4833     SPCurve *flash_curve = curve->copy();
4834     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4835     flash_curve->transform(i2d);
4836     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4837     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4838     // unless we also flash the nodes...
4839     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4840     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4841     sp_canvas_item_show(canvasitem);
4842     flash_curve->unref();
4843     return canvasitem;
4846 SPCanvasItem *
4847 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4848     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4849                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4851 **/
4853 SPCanvasItem *
4854 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4855     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4856     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4857     flash_curve->transform(i2d);
4858     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4859     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4860     // unless we also flash the nodes...
4861     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4862     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4863     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4864     sp_canvas_item_show(canvasitem);
4865     flash_curve->unref();
4866     return canvasitem;
4869 // TODO: Merge this with sp_nodepath_make_helper_item()!
4870 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4871     np->show_helperpath = show;
4873     if (show) {
4874         SPCurve *helper_curve = np->curve->copy();
4875         helper_curve->transform(np->i2d);
4876         if (!np->helper_path) {
4877             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4879             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4880             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);
4881             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4882             sp_canvas_item_move_to_z(np->helper_path, 0);
4883             sp_canvas_item_show(np->helper_path);
4884         } else {
4885             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4886         }
4887         helper_curve->unref();
4888     } else {
4889         if (np->helper_path) {
4890             GtkObject *temp = np->helper_path;
4891             np->helper_path = NULL;
4892             gtk_object_destroy(temp);
4893         }
4894     }
4897 /* sp_nodepath_make_straight_path:
4898  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4899  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4900  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4901  */
4902 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4903     np->straight_path = true;
4904     np->show_handles = false;
4905     g_message("add code to make the path straight.");
4906     // do sp_nodepath_convert_node_type on all nodes?
4907     // coding tip: search for this text : "Make selected segments lines"
4910 /*
4911   Local Variables:
4912   mode:c++
4913   c-file-style:"stroustrup"
4914   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4915   indent-tabs-mode:nil
4916   fill-column:99
4917   End:
4918 */
4919 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :