Code

aec5ff058b83417c50c9b3c8fb01b51d22fab4f1
[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 "libnr/n-art-bpath.h"
25 #include "libnr/nr-path.h"
26 #include <2geom/pathvector.h>
27 #include <2geom/sbasis-to-bezier.h>
28 #include "helper/units.h"
29 #include "knot.h"
30 #include "inkscape.h"
31 #include "document.h"
32 #include "sp-namedview.h"
33 #include "desktop.h"
34 #include "desktop-handles.h"
35 #include "snap.h"
36 #include "message-stack.h"
37 #include "message-context.h"
38 #include "node-context.h"
39 #include "shape-editor.h"
40 #include "selection-chemistry.h"
41 #include "selection.h"
42 #include "xml/repr.h"
43 #include "prefs-utils.h"
44 #include "sp-metrics.h"
45 #include "sp-path.h"
46 #include "libnr/nr-matrix-ops.h"
47 #include "splivarot.h"
48 #include "svg/svg.h"
49 #include "verbs.h"
50 #include "display/bezier-utils.h"
51 #include <vector>
52 #include <algorithm>
53 #include <cstring>
54 #include <string>
55 #include "live_effects/lpeobject.h"
56 #include "live_effects/effect.h"
57 #include "live_effects/parameter/parameter.h"
58 #include "util/mathfns.h"
59 #include "display/snap-indicator.h"
60 #include "snapped-point.h"
62 class NR::Matrix;
64 /// \todo
65 /// evil evil evil. FIXME: conflict of two different Path classes!
66 /// There is a conflict in the namespace between two classes named Path.
67 /// #include "sp-flowtext.h"
68 /// #include "sp-flowregion.h"
70 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
71 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
72 GType sp_flowregion_get_type (void);
73 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
74 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
75 GType sp_flowtext_get_type (void);
76 // end evil workaround
78 #include "helper/stlport.h"
81 /// \todo fixme: Implement these via preferences */
83 #define NODE_FILL          0xbfbfbf00
84 #define NODE_STROKE        0x000000ff
85 #define NODE_FILL_HI       0xff000000
86 #define NODE_STROKE_HI     0x000000ff
87 #define NODE_FILL_SEL      0x0000ffff
88 #define NODE_STROKE_SEL    0x000000ff
89 #define NODE_FILL_SEL_HI   0xff000000
90 #define NODE_STROKE_SEL_HI 0x000000ff
91 #define KNOT_FILL          0xffffffff
92 #define KNOT_STROKE        0x000000ff
93 #define KNOT_FILL_HI       0xff000000
94 #define KNOT_STROKE_HI     0x000000ff
96 static GMemChunk *nodechunk = NULL;
98 /* Creation from object */
100 static NArtBpath const * subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath const *b, Inkscape::NodePath::NodeType const *t);
101 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
102 static void add_curve_to_subpath( Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c,
103                                   Inkscape::NodePath::NodeType const *t, guint & i, NR::Point & ppos, NRPathcode & pcode  );
104 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, gint length);
106 /* Object updating */
108 static void stamp_repr(Inkscape::NodePath::Path *np);
109 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
110 static gchar *create_typestr(Inkscape::NodePath::Path *np);
112 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
114 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
116 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
118 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
120 /* Adjust handle placement, if the node or the other handle is moved */
121 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
122 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
124 /* Node event callbacks */
125 static void node_clicked(SPKnot *knot, guint state, gpointer data);
126 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
127 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
128 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 /* Handle event callbacks */
131 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
132 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
134 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
135 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
138 /* Constructors and destructors */
140 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
141 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
142 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
143 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
144 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
145                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
146 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
148 /* Helpers */
150 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
151 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
152 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
154 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
155 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
157 // active_node indicates mouseover node
158 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
160 static void sp_nodepath_draw_helper_curve(Inkscape::NodePath::Path *np, SPDesktop *desktop) {
161     // Draw helper curve
162     if (np->show_helperpath) {
163         SPCurve *helper_curve = np->curve->copy();
164         helper_curve->transform(np->i2d );
165         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
166         sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
167         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
168         sp_canvas_item_move_to_z(np->helper_path, 0);
169         sp_canvas_item_show(np->helper_path);
170         helper_curve->unref();
171     }
174 /**
175  * \brief Creates new nodepath from item
176  */
177 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
179     Inkscape::XML::Node *repr = object->repr;
181     /** \todo
182      * FIXME: remove this. We don't want to edit paths inside flowtext.
183      * Instead we will build our flowtext with cloned paths, so that the
184      * real paths are outside the flowtext and thus editable as usual.
185      */
186     if (SP_IS_FLOWTEXT(object)) {
187         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
188             if SP_IS_FLOWREGION(child) {
189                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
190                 if (grandchild && SP_IS_PATH(grandchild)) {
191                     object = SP_ITEM(grandchild);
192                     break;
193                 }
194             }
195         }
196     }
198     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
200     if (curve == NULL)
201         return NULL;
203     gint length = curve->get_length();
204     if (length == 0) {
205         curve->unref();
206         return NULL; // prevent crash for one-node paths
207     }
209     //Create new nodepath
210     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
211     if (!np) {
212         curve->unref();
213         return NULL;
214     }
216     // Set defaults
217     np->desktop     = desktop;
218     np->object      = object;
219     np->subpaths    = NULL;
220     np->selected    = NULL;
221     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
222     np->livarot_path = NULL;
223     np->local_change = 0;
224     np->show_handles = show_handles;
225     np->helper_path = NULL;
226     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
227     np->helperpath_width = 1.0;
228     np->curve = curve->copy();
229     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
230     if (SP_IS_LPE_ITEM(object)) {
231         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
232         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
233             np->show_helperpath = true;
234         }            
235     }
236     np->straight_path = false;
237     if (IS_LIVEPATHEFFECT(object) && item) {
238         np->item = item;
239     } else {
240         np->item = SP_ITEM(object);
241     }
243     // we need to update item's transform from the repr here,
244     // because they may be out of sync when we respond
245     // to a change in repr by regenerating nodepath     --bb
246     sp_object_read_attr(SP_OBJECT(np->item), "transform");
248     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
249     np->d2i  = np->i2d.inverse();
251     np->repr = repr;
252     if (repr_key_in) { // apparantly the object is an LPEObject
253         np->repr_key = g_strdup(repr_key_in);
254         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
255         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
256         if (lpeparam) {
257             lpeparam->param_setup_nodepath(np);
258         }
259     } else {
260         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
261         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
262             np->repr_key = g_strdup("inkscape:original-d");
264             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
265             if (lpe) {
266                 lpe->setup_nodepath(np);
267             }
268         } else {
269             np->repr_key = g_strdup("d");
270         }
271     }
273     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
274     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
276     // create the subpath(s) from the bpath
277 //    NArtBpath const *bpath = curve->get_bpath();
278 //    NArtBpath const *b = bpath;
279 //    while (b->code != NR_END) {
280 //        b = subpath_from_bpath(np, b, typestr + (b - bpath));
281 //    }
282     subpaths_from_pathvector(np, curve->get_pathvector(), typestr);
284     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
285     np->subpaths = g_list_reverse(np->subpaths);
287     delete[] typestr;
288     curve->unref();
290     // create the livarot representation from the same item
291     sp_nodepath_ensure_livarot_path(np);
293     sp_nodepath_draw_helper_curve(np, desktop);
295     return np;
298 /**
299  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
300  */
301 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
303     if (!np)  //soft fail, like delete
304         return;
306     while (np->subpaths) {
307         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
308     }
310     //Inform the ShapeEditor that made me, if any, that I am gone.
311     if (np->shape_editor)
312         np->shape_editor->nodepath_destroyed();
314     g_assert(!np->selected);
316     if (np->livarot_path) {
317         delete np->livarot_path;
318         np->livarot_path = NULL;
319     }
321     if (np->helper_path) {
322         GtkObject *temp = np->helper_path;
323         np->helper_path = NULL;
324         gtk_object_destroy(temp);
325     }
326     if (np->curve) {
327         np->curve->unref();
328         np->curve = NULL;
329     }
331     if (np->repr_key) {
332         g_free(np->repr_key);
333         np->repr_key = NULL;
334     }
335     if (np->repr_nodetypes_key) {
336         g_free(np->repr_nodetypes_key);
337         np->repr_nodetypes_key = NULL;
338     }
340     np->desktop = NULL;
342     g_free(np);
346 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
348     if (np && np->livarot_path == NULL) {
349         SPCurve *curve = create_curve(np);
350         np->livarot_path = new Path;
351         np->livarot_path->LoadPathVector(curve->get_pathvector());
353         if (np->livarot_path)
354             np->livarot_path->ConvertWithBackData(0.01);
356         curve->unref();
357     }
361 /**
362  *  Return the node count of a given NodeSubPath.
363  */
364 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
366     if (!subpath)
367         return 0;
368     gint nodeCount = g_list_length(subpath->nodes);
369     return nodeCount;
372 /**
373  *  Return the node count of a given NodePath.
374  */
375 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
377     if (!np)
378         return 0;
379     gint nodeCount = 0;
380     for (GList *item = np->subpaths ; item ; item=item->next) {
381        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
382         nodeCount += g_list_length(subpath->nodes);
383     }
384     return nodeCount;
387 /**
388  *  Return the subpath count of a given NodePath.
389  */
390 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
392     if (!np)
393         return 0;
394     return g_list_length (np->subpaths);
397 /**
398  *  Return the selected node count of a given NodePath.
399  */
400 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
402     if (!np)
403         return 0;
404     return g_list_length (np->selected);
407 /**
408  *  Return the number of subpaths where nodes are selected in a given NodePath.
409  */
410 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
412     if (!np)
413         return 0;
414     if (!np->selected)
415         return 0;
416     if (!np->selected->next)
417         return 1;
418     gint count = 0;
419     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
420         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
421         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
422             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
423             if (node->selected) {
424                 count ++;
425                 break;
426             }
427         }
428     }
429     return count;
432 /**
433  * Clean up a nodepath after editing.
434  *
435  * Currently we are deleting trivial subpaths.
436  */
437 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
439     GList *badSubPaths = NULL;
441     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
442     for (GList *l = nodepath->subpaths; l ; l=l->next) {
443        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
444        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
445             badSubPaths = g_list_append(badSubPaths, sp);
446     }
448     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
449     //also removes the subpath from nodepath->subpaths
450     for (GList *l = badSubPaths; l ; l=l->next) {
451        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
452         sp_nodepath_subpath_destroy(sp);
453     }
455     g_list_free(badSubPaths);
458 /**
459  * Create new nodepath from b, make it subpath of np.
460  * \param t The node type.
461  */
462 static NArtBpath const * subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath const *b, Inkscape::NodePath::NodeType const *t)
464     NR::Point ppos, pos, npos;
466     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
468     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
469     bool const closed = (b->code == NR_MOVETO);
471     pos = NR::Point(b->x3, b->y3) * np->i2d;
472     if (b[1].code == NR_CURVETO) {
473         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
474     } else {
475         npos = pos;
476     }
477     Inkscape::NodePath::Node *n;
478     n = sp_nodepath_node_new(sp, NULL, *t, NR_MOVETO, &pos, &pos, &npos);
479     g_assert(sp->first == n);
480     g_assert(sp->last  == n);
482     b++;
483     t++;
484     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
485         pos = NR::Point(b->x3, b->y3) * np->i2d;
486         if (b->code == NR_CURVETO) {
487             ppos = NR::Point(b->x2, b->y2) * np->i2d;
488         } else {
489             ppos = pos;
490         }
491         if (b[1].code == NR_CURVETO) {
492             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
493         } else {
494             npos = pos;
495         }
496         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
497         b++;
498         t++;
499     }
501     if (closed) sp_nodepath_subpath_close(sp);
503     return b;
506 /**
507  * Create new nodepaths from pathvector, make it subpaths of np.
508  * \param t The node type array.
509  */
510 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
512     guint i = 0;  // index into node type array
513     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
514         if (pit->empty())
515             continue;  // don't add single knot paths
517         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
519         NR::Point ppos = from_2geom(pit->initialPoint()) * np->i2d;
520         NRPathcode pcode = NR_MOVETO;
522         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
523             add_curve_to_subpath(np, sp, *cit, t, i, ppos, pcode);
524         }
526         if (pit->closed()) {
527             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
528             // Remember that last closing segment is always a lineto, and that its endpoint is the path's initialPoint because the path is closed
529             NR::Point pos = from_2geom(pit->initialPoint()) * np->i2d;
530             sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
531             sp_nodepath_subpath_close(sp);
532         }
533     }
535 // should add initial point of curve with type of previous curve:
536 static void add_curve_to_subpath(Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c, Inkscape::NodePath::NodeType const *t, guint & i,
537                                  NR::Point & ppos, NRPathcode & pcode)
539     if( dynamic_cast<Geom::LineSegment const*>(&c) ||
540         dynamic_cast<Geom::HLineSegment const*>(&c) ||
541         dynamic_cast<Geom::VLineSegment const*>(&c) )
542     {
543         NR::Point pos = from_2geom(c.initialPoint()) * np->i2d;
544         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
545         ppos = from_2geom(c.finalPoint());
546         pcode = NR_LINETO;
547     }
548     else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) {
549         std::vector<Geom::Point> points = cubic_bezier->points();
550         NR::Point pos = from_2geom(points[0]) * np->i2d;
551         NR::Point npos = from_2geom(points[1]) * np->i2d;
552         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
553         ppos = from_2geom(points[2]) * np->i2d;
554         pcode = NR_CURVETO;
555     }
556     else {
557         //this case handles sbasis as well as all other curve types
558         Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1);
560         for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
561             add_curve_to_subpath(np, sp, *iter, t, i, ppos, pcode);
562         }
563     }
567 /**
568  * Convert from sodipodi:nodetypes to new style type array.
569  */
570 static
571 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, gint length)
573     g_assert(length > 0);
575     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
577     gint pos = 0;
579     if (types) {
580         for (gint i = 0; types[i] && ( i < length ); i++) {
581             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
582             if (types[i] != '\0') {
583                 switch (types[i]) {
584                     case 's':
585                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
586                         break;
587                     case 'z':
588                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
589                         break;
590                     case 'c':
591                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
592                         break;
593                     default:
594                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
595                         break;
596                 }
597             }
598         }
599     }
601     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
603     return typestr;
606 /**
607  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
608  * updated but repr is not (for speed). Used during curve and node drag.
609  */
610 static void update_object(Inkscape::NodePath::Path *np)
612     g_assert(np);
614     np->curve->unref();
615     np->curve = create_curve(np);
617     sp_nodepath_set_curve(np, np->curve);
619     if (np->show_helperpath) {
620         SPCurve * helper_curve = np->curve->copy();
621         helper_curve->transform(np->i2d );
622         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
623         helper_curve->unref();
624     }
627 /**
628  * Update XML path node with data from path object.
629  */
630 static void update_repr_internal(Inkscape::NodePath::Path *np)
632     g_assert(np);
634     Inkscape::XML::Node *repr = np->object->repr;
636     np->curve->unref();
637     np->curve = create_curve(np);
639     gchar *typestr = create_typestr(np);
640     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
642     // determine if path has an effect applied and write to correct "d" attribute.
643     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
644         np->local_change++;
645         repr->setAttribute(np->repr_key, svgpath);
646     }
648     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
649         np->local_change++;
650         repr->setAttribute(np->repr_nodetypes_key, typestr);
651     }
653     g_free(svgpath);
654     g_free(typestr);
656     if (np->show_helperpath) {
657         SPCurve * helper_curve = np->curve->copy();
658         helper_curve->transform(np->i2d );
659         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
660         helper_curve->unref();
661     }
662  }
664 /**
665  * Update XML path node with data from path object, commit changes forever.
666  */
667 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
669     //fixme: np can be NULL, so check before proceeding
670     g_return_if_fail(np != NULL);
672     if (np->livarot_path) {
673         delete np->livarot_path;
674         np->livarot_path = NULL;
675     }
677     update_repr_internal(np);
678     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
680     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
681                      annotation);
684 /**
685  * Update XML path node with data from path object, commit changes with undo.
686  */
687 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
689     if (np->livarot_path) {
690         delete np->livarot_path;
691         np->livarot_path = NULL;
692     }
694     update_repr_internal(np);
695     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
696                            annotation);
699 /**
700  * Make duplicate of path, replace corresponding XML node in tree, commit.
701  */
702 static void stamp_repr(Inkscape::NodePath::Path *np)
704     g_assert(np);
706     Inkscape::XML::Node *old_repr = np->object->repr;
707     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
709     // remember the position of the item
710     gint pos = old_repr->position();
711     // remember parent
712     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
714     SPCurve *curve = create_curve(np);
715     gchar *typestr = create_typestr(np);
717     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
719     new_repr->setAttribute(np->repr_key, svgpath);
720     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
722     // add the new repr to the parent
723     parent->appendChild(new_repr);
724     // move to the saved position
725     new_repr->setPosition(pos > 0 ? pos : 0);
727     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
728                      _("Stamp"));
730     Inkscape::GC::release(new_repr);
731     g_free(svgpath);
732     g_free(typestr);
733     curve->unref();
736 /**
737  * Create curve from path.
738  */
739 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
741     SPCurve *curve = new SPCurve();
743     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
744        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
745         curve->moveto(sp->first->pos * np->d2i);
746        Inkscape::NodePath::Node *n = sp->first->n.other;
747         while (n) {
748             NR::Point const end_pt = n->pos * np->d2i;
749             switch (n->code) {
750                 case NR_LINETO:
751                     curve->lineto(end_pt);
752                     break;
753                 case NR_CURVETO:
754                     curve->curveto(n->p.other->n.pos * np->d2i,
755                                      n->p.pos * np->d2i,
756                                      end_pt);
757                     break;
758                 default:
759                     g_assert_not_reached();
760                     break;
761             }
762             if (n != sp->last) {
763                 n = n->n.other;
764             } else {
765                 n = NULL;
766             }
767         }
768         if (sp->closed) {
769             curve->closepath();
770         }
771     }
773     return curve;
776 /**
777  * Convert path type string to sodipodi:nodetypes style.
778  */
779 static gchar *create_typestr(Inkscape::NodePath::Path *np)
781     gchar *typestr = g_new(gchar, 32);
782     gint len = 32;
783     gint pos = 0;
785     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
786        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
788         if (pos >= len) {
789             typestr = g_renew(gchar, typestr, len + 32);
790             len += 32;
791         }
793         typestr[pos++] = 'c';
795        Inkscape::NodePath::Node *n;
796         n = sp->first->n.other;
797         while (n) {
798             gchar code;
800             switch (n->type) {
801                 case Inkscape::NodePath::NODE_CUSP:
802                     code = 'c';
803                     break;
804                 case Inkscape::NodePath::NODE_SMOOTH:
805                     code = 's';
806                     break;
807                 case Inkscape::NodePath::NODE_SYMM:
808                     code = 'z';
809                     break;
810                 default:
811                     g_assert_not_reached();
812                     code = '\0';
813                     break;
814             }
816             if (pos >= len) {
817                 typestr = g_renew(gchar, typestr, len + 32);
818                 len += 32;
819             }
821             typestr[pos++] = code;
823             if (n != sp->last) {
824                 n = n->n.other;
825             } else {
826                 n = NULL;
827             }
828         }
829     }
831     if (pos >= len) {
832         typestr = g_renew(gchar, typestr, len + 1);
833         len += 1;
834     }
836     typestr[pos++] = '\0';
838     return typestr;
841 /**
842  * Returns current path in context. // later eliminate this function at all!
843  */
844 static Inkscape::NodePath::Path *sp_nodepath_current()
846     if (!SP_ACTIVE_DESKTOP) {
847         return NULL;
848     }
850     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
852     if (!SP_IS_NODE_CONTEXT(event_context)) {
853         return NULL;
854     }
856     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
861 /**
862  \brief Fills node and handle positions for three nodes, splitting line
863   marked by end at distance t.
864  */
865 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
867     g_assert(new_path != NULL);
868     g_assert(end      != NULL);
870     g_assert(end->p.other == new_path);
871    Inkscape::NodePath::Node *start = new_path->p.other;
872     g_assert(start);
874     if (end->code == NR_LINETO) {
875         new_path->type =Inkscape::NodePath::NODE_CUSP;
876         new_path->code = NR_LINETO;
877         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
878     } else {
879         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
880         new_path->code = NR_CURVETO;
881         gdouble s      = 1 - t;
882         for (int dim = 0; dim < 2; dim++) {
883             NR::Coord const f000 = start->pos[dim];
884             NR::Coord const f001 = start->n.pos[dim];
885             NR::Coord const f011 = end->p.pos[dim];
886             NR::Coord const f111 = end->pos[dim];
887             NR::Coord const f00t = s * f000 + t * f001;
888             NR::Coord const f01t = s * f001 + t * f011;
889             NR::Coord const f11t = s * f011 + t * f111;
890             NR::Coord const f0tt = s * f00t + t * f01t;
891             NR::Coord const f1tt = s * f01t + t * f11t;
892             NR::Coord const fttt = s * f0tt + t * f1tt;
893             start->n.pos[dim]    = f00t;
894             new_path->p.pos[dim] = f0tt;
895             new_path->pos[dim]   = fttt;
896             new_path->n.pos[dim] = f1tt;
897             end->p.pos[dim]      = f11t;
898         }
899     }
902 /**
903  * Adds new node on direct line between two nodes, activates handles of all
904  * three nodes.
905  */
906 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
908     g_assert(end);
909     g_assert(end->subpath);
910     g_assert(g_list_find(end->subpath->nodes, end));
912    Inkscape::NodePath::Node *start = end->p.other;
913     g_assert( start->n.other == end );
914    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
915                                                end,
916                                                (NRPathcode)end->code == NR_LINETO?
917                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
918                                                (NRPathcode)end->code,
919                                                &start->pos, &start->pos, &start->n.pos);
920     sp_nodepath_line_midpoint(newnode, end, t);
922     sp_node_adjust_handles(start);
923     sp_node_update_handles(start);
924     sp_node_update_handles(newnode);
925     sp_node_adjust_handles(end);
926     sp_node_update_handles(end);
928     return newnode;
931 /**
932 \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
933 */
934 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
936     g_assert(node);
937     g_assert(node->subpath);
938     g_assert(g_list_find(node->subpath->nodes, node));
940    Inkscape::NodePath::SubPath *sp = node->subpath;
941     Inkscape::NodePath::Path *np    = sp->nodepath;
943     if (sp->closed) {
944         sp_nodepath_subpath_open(sp, node);
945         return sp->first;
946     } else {
947         // no break for end nodes
948         if (node == sp->first) return NULL;
949         if (node == sp->last ) return NULL;
951         // create a new subpath
952        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
954         // duplicate the break node as start of the new subpath
955         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
957         // attach rest of curve to new node
958         g_assert(node->n.other);
959         newnode->n.other = node->n.other; node->n.other = NULL;
960         newnode->n.other->p.other = newnode;
961         newsubpath->last = sp->last;
962         sp->last = node;
963         node = newnode;
964         while (node->n.other) {
965             node = node->n.other;
966             node->subpath = newsubpath;
967             sp->nodes = g_list_remove(sp->nodes, node);
968             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
969         }
972         return newnode;
973     }
976 /**
977  * Duplicate node and connect to neighbours.
978  */
979 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
981     g_assert(node);
982     g_assert(node->subpath);
983     g_assert(g_list_find(node->subpath->nodes, node));
985    Inkscape::NodePath::SubPath *sp = node->subpath;
987     NRPathcode code = (NRPathcode) node->code;
988     if (code == NR_MOVETO) { // if node is the endnode,
989         node->code = NR_LINETO; // new one is inserted before it, so change that to line
990     }
992     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
994     if (!node->n.other || !node->p.other) // if node is an endnode, select it
995         return node;
996     else
997         return newnode; // otherwise select the newly created node
1000 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1002     node->p.pos = (node->pos + (node->pos - node->n.pos));
1005 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1007     node->n.pos = (node->pos + (node->pos - node->p.pos));
1010 /**
1011  * Change line type at node, with side effects on neighbours.
1012  */
1013 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1015     g_assert(end);
1016     g_assert(end->subpath);
1017     g_assert(end->p.other);
1019     if (end->code == static_cast< guint > ( code ) )
1020         return;
1022    Inkscape::NodePath::Node *start = end->p.other;
1024     end->code = code;
1026     if (code == NR_LINETO) {
1027         if (start->code == NR_LINETO) {
1028             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
1029         }
1030         if (end->n.other) {
1031             if (end->n.other->code == NR_LINETO) {
1032                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
1033             }
1034         }
1035     } else {
1036         NR::Point delta = end->pos - start->pos;
1037         start->n.pos = start->pos + delta / 3;
1038         end->p.pos = end->pos - delta / 3;
1039         sp_node_adjust_handle(start, 1);
1040         sp_node_adjust_handle(end, -1);
1041     }
1043     sp_node_update_handles(start);
1044     sp_node_update_handles(end);
1047 /**
1048  * Change node type, and its handles accordingly.
1049  */
1050 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1052     g_assert(node);
1053     g_assert(node->subpath);
1055     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1056         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1057             type =Inkscape::NodePath::NODE_CUSP;
1058         }
1059     }
1061     node->type = type;
1063     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1064         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1065         node->knot->setSize (node->selected? 11 : 9);
1066         sp_knot_update_ctrl(node->knot);
1067     } else {
1068         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1069         node->knot->setSize (node->selected? 9 : 7);
1070         sp_knot_update_ctrl(node->knot);
1071     }
1073     // if one of handles is mouseovered, preserve its position
1074     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1075         sp_node_adjust_handle(node, 1);
1076     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1077         sp_node_adjust_handle(node, -1);
1078     } else {
1079         sp_node_adjust_handles(node);
1080     }
1082     sp_node_update_handles(node);
1084     sp_nodepath_update_statusbar(node->subpath->nodepath);
1086     return node;
1089 bool
1090 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1092         Inkscape::NodePath::Node *othernode = side->other;
1093         if (!othernode)
1094             return false;
1095         NRPathcode const code = sp_node_path_code_from_side(node, side);
1096         if (code == NR_LINETO)
1097             return true;
1098         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1099         if (&node->p == side) {
1100             other_to_me = &othernode->n;
1101         } else if (&node->n == side) {
1102             other_to_me = &othernode->p;
1103         } 
1104         if (!other_to_me)
1105             return false;
1106         bool is_line = 
1107              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1108               NR::L2(node->pos - side->pos) < 1e-6);
1109         return is_line;
1112 /**
1113  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1114  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1115  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1116  * If already cusp and set to cusp, retracts handles.
1117 */
1118 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1120     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1122 /* 
1123   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1124  
1125         if (two_handles) {
1126             // do nothing, adjust_handles called via set_node_type will line them up
1127         } else if (one_handle) {
1128             if (opposite_to_handle_is_line) {
1129                 if (lined_up) {
1130                     // already half-smooth; pull opposite handle too making it fully smooth
1131                 } else {
1132                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1133                 }
1134             } else {
1135                 // pull opposite handle in line with the existing one
1136             }
1137         } else if (no_handles) {
1138             if (both_segments_are_lines OR both_segments_are_curves) {
1139                 //pull both handles
1140             } else {
1141                 // pull the handle opposite to line segment, making node half-smooth
1142             }
1143         }
1144 */
1145         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1146         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1147         bool p_is_line = sp_node_side_is_line(node, &node->p);
1148         bool n_is_line = sp_node_side_is_line(node, &node->n);
1150         if (p_has_handle && n_has_handle) {
1151             // do nothing, adjust_handles will line them up
1152         } else if (p_has_handle || n_has_handle) {
1153             if (p_has_handle && n_is_line) {
1154                 Radial line (node->n.other->pos - node->pos);
1155                 Radial handle (node->pos - node->p.pos);
1156                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1157                     // already half-smooth; pull opposite handle too making it fully smooth
1158                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1159                 } else {
1160                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1161                 }
1162             } else if (n_has_handle && p_is_line) {
1163                 Radial line (node->p.other->pos - node->pos);
1164                 Radial handle (node->pos - node->n.pos);
1165                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1166                     // already half-smooth; pull opposite handle too making it fully smooth
1167                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1168                 } else {
1169                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1170                 }
1171             } else if (p_has_handle && node->n.other) {
1172                 // pull n handle
1173                 node->n.other->code = NR_CURVETO;
1174                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1175                     NR::L2(node->p.pos - node->pos) :
1176                     NR::L2(node->n.other->pos - node->pos) / 3;
1177                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1178             } else if (n_has_handle && node->p.other) {
1179                 // pull p handle
1180                 node->code = NR_CURVETO;
1181                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1182                     NR::L2(node->n.pos - node->pos) :
1183                     NR::L2(node->p.other->pos - node->pos) / 3;
1184                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1185             }
1186         } else if (!p_has_handle && !n_has_handle) {
1187             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1188                 // no handles, but both segments are either lnes or curves:
1189                 //pull both handles
1191                 // convert both to curves:
1192                 node->code = NR_CURVETO;
1193                 node->n.other->code = NR_CURVETO;
1195                 NR::Point leg_prev = node->pos - node->p.other->pos;
1196                 NR::Point leg_next = node->pos - node->n.other->pos;
1198                 double norm_leg_prev = L2(leg_prev);
1199                 double norm_leg_next = L2(leg_next);
1201                 NR::Point delta;
1202                 if (norm_leg_next > 0.0) {
1203                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1204                     (&delta)->normalize();
1205                 }
1207                 if (type == Inkscape::NodePath::NODE_SYMM) {
1208                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1209                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1210                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1211                 } else {
1212                     // length of handle is proportional to distance to adjacent node
1213                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1214                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1215                 }
1217             } else {
1218                 // pull the handle opposite to line segment, making it half-smooth
1219                 if (p_is_line && node->n.other) {
1220                     if (type != Inkscape::NodePath::NODE_SYMM) {
1221                         // pull n handle
1222                         node->n.other->code = NR_CURVETO;
1223                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1224                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1225                     }
1226                 } else if (n_is_line && node->p.other) {
1227                     if (type != Inkscape::NodePath::NODE_SYMM) {
1228                         // pull p handle
1229                         node->code = NR_CURVETO;
1230                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1231                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1232                     }
1233                 }
1234             }
1235         }
1236     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1237         // cusping a cusp: retract nodes
1238         node->p.pos = node->pos;
1239         node->n.pos = node->pos;
1240     }
1242     sp_nodepath_set_node_type (node, type);
1245 /**
1246  * Move node to point, and adjust its and neighbouring handles.
1247  */
1248 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1250     NR::Point delta = p - node->pos;
1251     node->pos = p;
1253     node->p.pos += delta;
1254     node->n.pos += delta;
1256     Inkscape::NodePath::Node *node_p = NULL;
1257     Inkscape::NodePath::Node *node_n = NULL;
1259     if (node->p.other) {
1260         if (node->code == NR_LINETO) {
1261             sp_node_adjust_handle(node, 1);
1262             sp_node_adjust_handle(node->p.other, -1);
1263             node_p = node->p.other;
1264         }
1265     }
1266     if (node->n.other) {
1267         if (node->n.other->code == NR_LINETO) {
1268             sp_node_adjust_handle(node, -1);
1269             sp_node_adjust_handle(node->n.other, 1);
1270             node_n = node->n.other;
1271         }
1272     }
1274     // this function is only called from batch movers that will update display at the end
1275     // themselves, so here we just move all the knots without emitting move signals, for speed
1276     sp_node_update_handles(node, false);
1277     if (node_n) {
1278         sp_node_update_handles(node_n, false);
1279     }
1280     if (node_p) {
1281         sp_node_update_handles(node_p, false);
1282     }
1285 /**
1286  * Call sp_node_moveto() for node selection and handle possible snapping.
1287  */
1288 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1289                                             bool const snap, bool constrained = false, 
1290                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1292     NR::Coord best = NR_HUGE;
1293     NR::Point delta(dx, dy);
1294     NR::Point best_pt = delta;
1295     Inkscape::SnappedPoint best_abs;
1296     
1297     if (snap) {    
1298         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1299          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1300          * must provide that information. */
1301           
1302         // Build a list of the unselected nodes to which the snapper should snap 
1303         std::vector<NR::Point> unselected_nodes;
1304         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1305             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1306             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1307                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1308                 if (!node->selected) {
1309                     unselected_nodes.push_back(node->pos);
1310                 }    
1311             }
1312         }        
1313         
1314         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1315         
1316         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1317             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1318             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1319             Inkscape::SnappedPoint s;
1320             if (constrained) {
1321                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1322                 dedicated_constraint.setPoint(n->pos);
1323                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1324             } else {
1325                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1326             }            
1327             if (s.getSnapped() && (s.getDistance() < best)) {
1328                 best = s.getDistance();
1329                 best_abs = s;
1330                 best_pt = s.getPoint() - n->pos;
1331             }
1332         }
1333                         
1334         if (best_abs.getSnapped()) {
1335             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1336         } else {
1337             nodepath->desktop->snapindicator->remove_snappoint();    
1338         }
1339     }
1341     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1342         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1343         sp_node_moveto(n, n->pos + best_pt);
1344     }
1346     // do not update repr here so that node dragging is acceptably fast
1347     update_object(nodepath);
1350 /**
1351 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1352 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1353 near x = 0.
1354  */
1355 double
1356 sculpt_profile (double x, double alpha, guint profile)
1358     if (x >= 1)
1359         return 0;
1360     if (x <= 0)
1361         return 1;
1363     switch (profile) {
1364         case SCULPT_PROFILE_LINEAR:
1365         return 1 - x;
1366         case SCULPT_PROFILE_BELL:
1367         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1368         case SCULPT_PROFILE_ELLIPTIC:
1369         return sqrt(1 - x*x);
1370     }
1372     return 1;
1375 double
1376 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1378     // extremely primitive for now, don't have time to look for the real one
1379     double lower = NR::L2(b - a);
1380     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1381     return (lower + upper)/2;
1384 void
1385 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1387     n->pos = n->origin + delta;
1388     n->n.pos = n->n.origin + delta_n;
1389     n->p.pos = n->p.origin + delta_p;
1390     sp_node_adjust_handles(n);
1391     sp_node_update_handles(n, false);
1394 /**
1395  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1396  * on how far they are from the dragged node n.
1397  */
1398 static void
1399 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1401     g_assert (n);
1402     g_assert (nodepath);
1403     g_assert (n->subpath->nodepath == nodepath);
1405     double pressure = n->knot->pressure;
1406     if (pressure == 0)
1407         pressure = 0.5; // default
1408     pressure = CLAMP (pressure, 0.2, 0.8);
1410     // map pressure to alpha = 1/5 ... 5
1411     double alpha = 1 - 2 * fabs(pressure - 0.5);
1412     if (pressure > 0.5)
1413         alpha = 1/alpha;
1415     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1417     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1418         // Only one subpath has selected nodes:
1419         // use linear mode, where the distance from n to node being dragged is calculated along the path
1421         double n_sel_range = 0, p_sel_range = 0;
1422         guint n_nodes = 0, p_nodes = 0;
1423         guint n_sel_nodes = 0, p_sel_nodes = 0;
1425         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1426         {
1427             double n_range = 0, p_range = 0;
1428             bool n_going = true, p_going = true;
1429             Inkscape::NodePath::Node *n_node = n;
1430             Inkscape::NodePath::Node *p_node = n;
1431             do {
1432                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1433                 if (n_node && n_going)
1434                     n_node = n_node->n.other;
1435                 if (n_node == NULL) {
1436                     n_going = false;
1437                 } else {
1438                     n_nodes ++;
1439                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1440                     if (n_node->selected) {
1441                         n_sel_nodes ++;
1442                         n_sel_range = n_range;
1443                     }
1444                     if (n_node == p_node) {
1445                         n_going = false;
1446                         p_going = false;
1447                     }
1448                 }
1449                 if (p_node && p_going)
1450                     p_node = p_node->p.other;
1451                 if (p_node == NULL) {
1452                     p_going = false;
1453                 } else {
1454                     p_nodes ++;
1455                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1456                     if (p_node->selected) {
1457                         p_sel_nodes ++;
1458                         p_sel_range = p_range;
1459                     }
1460                     if (p_node == n_node) {
1461                         n_going = false;
1462                         p_going = false;
1463                     }
1464                 }
1465             } while (n_going || p_going);
1466         }
1468         // Second pass: actually move nodes in this subpath
1469         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1470         {
1471             double n_range = 0, p_range = 0;
1472             bool n_going = true, p_going = true;
1473             Inkscape::NodePath::Node *n_node = n;
1474             Inkscape::NodePath::Node *p_node = n;
1475             do {
1476                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1477                 if (n_node && n_going)
1478                     n_node = n_node->n.other;
1479                 if (n_node == NULL) {
1480                     n_going = false;
1481                 } else {
1482                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1483                     if (n_node->selected) {
1484                         sp_nodepath_move_node_and_handles (n_node,
1485                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1486                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1487                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1488                     }
1489                     if (n_node == p_node) {
1490                         n_going = false;
1491                         p_going = false;
1492                     }
1493                 }
1494                 if (p_node && p_going)
1495                     p_node = p_node->p.other;
1496                 if (p_node == NULL) {
1497                     p_going = false;
1498                 } else {
1499                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1500                     if (p_node->selected) {
1501                         sp_nodepath_move_node_and_handles (p_node,
1502                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1503                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1504                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1505                     }
1506                     if (p_node == n_node) {
1507                         n_going = false;
1508                         p_going = false;
1509                     }
1510                 }
1511             } while (n_going || p_going);
1512         }
1514     } else {
1515         // Multiple subpaths have selected nodes:
1516         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1517         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1518         // fix the pear-like shape when sculpting e.g. a ring
1520         // First pass: calculate range
1521         gdouble direct_range = 0;
1522         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1523             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1524             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1525                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1526                 if (node->selected) {
1527                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1528                 }
1529             }
1530         }
1532         // Second pass: actually move nodes
1533         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1534             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1535             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1536                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1537                 if (node->selected) {
1538                     if (direct_range > 1e-6) {
1539                         sp_nodepath_move_node_and_handles (node,
1540                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1541                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1542                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1543                     } else {
1544                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1545                     }
1547                 }
1548             }
1549         }
1550     }
1552     // do not update repr here so that node dragging is acceptably fast
1553     update_object(nodepath);
1557 /**
1558  * Move node selection to point, adjust its and neighbouring handles,
1559  * handle possible snapping, and commit the change with possible undo.
1560  */
1561 void
1562 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1564     if (!nodepath) return;
1566     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1568     if (dx == 0) {
1569         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1570     } else if (dy == 0) {
1571         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1572     } else {
1573         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1574     }
1577 /**
1578  * Move node selection off screen and commit the change.
1579  */
1580 void
1581 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1583     // borrowed from sp_selection_move_screen in selection-chemistry.c
1584     // we find out the current zoom factor and divide deltas by it
1585     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1587     gdouble zoom = desktop->current_zoom();
1588     gdouble zdx = dx / zoom;
1589     gdouble zdy = dy / zoom;
1591     if (!nodepath) return;
1593     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1595     if (dx == 0) {
1596         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1597     } else if (dy == 0) {
1598         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1599     } else {
1600         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1601     }
1604 /**
1605  * Move selected nodes to the absolute position given
1606  */
1607 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1609     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1610         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1611         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1612         sp_node_moveto(n, npos);
1613     }
1615     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1618 /**
1619  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1620  */
1621 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1623     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1624     g_return_val_if_fail(nodepath->selected, no_coord);
1626     // determine coordinate of first selected node
1627     GList *nsel = nodepath->selected;
1628     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1629     NR::Coord coord = n->pos[axis];
1630     bool coincide = true;
1632     // compare it to the coordinates of all the other selected nodes
1633     for (GList *l = nsel->next; l != NULL; l = l->next) {
1634         n = (Inkscape::NodePath::Node *) l->data;
1635         if (n->pos[axis] != coord) {
1636             coincide = false;
1637         }
1638     }
1639     if (coincide) {
1640         return coord;
1641     } else {
1642         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1643         // currently we return the coordinate of the bounding box midpoint because I don't know how
1644         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1645         return bbox.midpoint()[axis];
1646     }
1649 /** If they don't yet exist, creates knot and line for the given side of the node */
1650 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1652     if (!side->knot) {
1653         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"));
1655         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1656         side->knot->setSize (7);
1657         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1658         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1659         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1660         sp_knot_update_ctrl(side->knot);
1662         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1663         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1664         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1665         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1666         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1667         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1668     }
1670     if (!side->line) {
1671         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1672                                         SP_TYPE_CTRLLINE, NULL);
1673     }
1676 /**
1677  * Ensure the given handle of the node is visible/invisible, update its screen position
1678  */
1679 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1681     g_assert(node != NULL);
1683    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1684     NRPathcode code = sp_node_path_code_from_side(node, side);
1686     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1688     if (show_handle) {
1689         if (!side->knot) { // No handle knot at all
1690             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1691             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1692             side->knot->pos = side->pos;
1693             if (side->knot->item)
1694                 SP_CTRL(side->knot->item)->moveto(side->pos);
1695             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1696             sp_knot_show(side->knot);
1697         } else {
1698             if (side->knot->pos != side->pos) { // only if it's really moved
1699                 if (fire_move_signals) {
1700                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1701                 } else {
1702                     sp_knot_moveto(side->knot, &side->pos);
1703                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1704                 }
1705             }
1706             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1707                 sp_knot_show(side->knot);
1708             }
1709         }
1710         sp_canvas_item_show(side->line);
1711     } else {
1712         if (side->knot) {
1713             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1714                 sp_knot_hide(side->knot);
1715             }
1716         }
1717         if (side->line) {
1718             sp_canvas_item_hide(side->line);
1719         }
1720     }
1723 /**
1724  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1725  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1726  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1727  * updated; otherwise, just move the knots silently (used in batch moves).
1728  */
1729 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1731     g_assert(node != NULL);
1733     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1734         sp_knot_show(node->knot);
1735     }
1737     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1738         if (fire_move_signals)
1739             sp_knot_set_position(node->knot, &node->pos, 0);
1740         else
1741             sp_knot_moveto(node->knot, &node->pos);
1742     }
1744     gboolean show_handles = node->selected;
1745     if (node->p.other != NULL) {
1746         if (node->p.other->selected) show_handles = TRUE;
1747     }
1748     if (node->n.other != NULL) {
1749         if (node->n.other->selected) show_handles = TRUE;
1750     }
1752     if (node->subpath->nodepath->show_handles == false)
1753         show_handles = FALSE;
1755     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1756     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1759 /**
1760  * Call sp_node_update_handles() for all nodes on subpath.
1761  */
1762 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1764     g_assert(subpath != NULL);
1766     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1767         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1768     }
1771 /**
1772  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1773  */
1774 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1776     g_assert(nodepath != NULL);
1778     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1779         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1780     }
1783 void
1784 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1786     if (nodepath == NULL) return;
1788     nodepath->show_handles = show;
1789     sp_nodepath_update_handles(nodepath);
1792 /**
1793  * Adds all selected nodes in nodepath to list.
1794  */
1795 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1797     StlConv<Node *>::list(l, selected);
1798 /// \todo this adds a copying, rework when the selection becomes a stl list
1801 /**
1802  * Align selected nodes on the specified axis.
1803  */
1804 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1806     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1807         return;
1808     }
1810     if ( !nodepath->selected->next ) { // only one node selected
1811         return;
1812     }
1813    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1814     NR::Point dest(pNode->pos);
1815     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1816         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1817         if (pNode) {
1818             dest[axis] = pNode->pos[axis];
1819             sp_node_moveto(pNode, dest);
1820         }
1821     }
1823     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1826 /// Helper struct.
1827 struct NodeSort
1829    Inkscape::NodePath::Node *_node;
1830     NR::Coord _coord;
1831     /// \todo use vectorof pointers instead of calling copy ctor
1832     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1833         _node(node), _coord(node->pos[axis])
1834     {}
1836 };
1838 static bool operator<(NodeSort const &a, NodeSort const &b)
1840     return (a._coord < b._coord);
1843 /**
1844  * Distribute selected nodes on the specified axis.
1845  */
1846 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1848     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1849         return;
1850     }
1852     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1853         return;
1854     }
1856    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1857     std::vector<NodeSort> sorted;
1858     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1859         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1860         if (pNode) {
1861             NodeSort n(pNode, axis);
1862             sorted.push_back(n);
1863             //dest[axis] = pNode->pos[axis];
1864             //sp_node_moveto(pNode, dest);
1865         }
1866     }
1867     std::sort(sorted.begin(), sorted.end());
1868     unsigned int len = sorted.size();
1869     //overall bboxes span
1870     float dist = (sorted.back()._coord -
1871                   sorted.front()._coord);
1872     //new distance between each bbox
1873     float step = (dist) / (len - 1);
1874     float pos = sorted.front()._coord;
1875     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1876           it < sorted.end();
1877           it ++ )
1878     {
1879         NR::Point dest((*it)._node->pos);
1880         dest[axis] = pos;
1881         sp_node_moveto((*it)._node, dest);
1882         pos += step;
1883     }
1885     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1889 /**
1890  * Call sp_nodepath_line_add_node() for all selected segments.
1891  */
1892 void
1893 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1895     if (!nodepath) {
1896         return;
1897     }
1899     GList *nl = NULL;
1901     int n_added = 0;
1903     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1904        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1905         g_assert(t->selected);
1906         if (t->p.other && t->p.other->selected) {
1907             nl = g_list_prepend(nl, t);
1908         }
1909     }
1911     while (nl) {
1912        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1913        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1914        sp_nodepath_node_select(n, TRUE, FALSE);
1915        n_added ++;
1916        nl = g_list_remove(nl, t);
1917     }
1919     /** \todo fixme: adjust ? */
1920     sp_nodepath_update_handles(nodepath);
1922     if (n_added > 1) {
1923         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1924     } else if (n_added > 0) {
1925         sp_nodepath_update_repr(nodepath, _("Add node"));
1926     }
1928     sp_nodepath_update_statusbar(nodepath);
1931 /**
1932  * Select segment nearest to point
1933  */
1934 void
1935 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1937     if (!nodepath) {
1938         return;
1939     }
1941     sp_nodepath_ensure_livarot_path(nodepath);
1942     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1943     if (!maybe_position) {
1944         return;
1945     }
1946     Path::cut_position position = *maybe_position;
1948     //find segment to segment
1949     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1951     //fixme: this can return NULL, so check before proceeding.
1952     g_return_if_fail(e != NULL);
1954     gboolean force = FALSE;
1955     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1956         force = TRUE;
1957     }
1958     sp_nodepath_node_select(e, (gboolean) toggle, force);
1959     if (e->p.other)
1960         sp_nodepath_node_select(e->p.other, TRUE, force);
1962     sp_nodepath_update_handles(nodepath);
1964     sp_nodepath_update_statusbar(nodepath);
1967 /**
1968  * Add a node nearest to point
1969  */
1970 void
1971 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1973     if (!nodepath) {
1974         return;
1975     }
1977     sp_nodepath_ensure_livarot_path(nodepath);
1978     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1979     if (!maybe_position) {
1980         return;
1981     }
1982     Path::cut_position position = *maybe_position;
1984     //find segment to split
1985     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1987     //don't know why but t seems to flip for lines
1988     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1989         position.t = 1.0 - position.t;
1990     }
1991     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1992     sp_nodepath_node_select(n, FALSE, TRUE);
1994     /* fixme: adjust ? */
1995     sp_nodepath_update_handles(nodepath);
1997     sp_nodepath_update_repr(nodepath, _("Add node"));
1999     sp_nodepath_update_statusbar(nodepath);
2002 /*
2003  * Adjusts a segment so that t moves by a certain delta for dragging
2004  * converts lines to curves
2005  *
2006  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2007  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2008  */
2009 void
2010 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
2012     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
2014     //fixme: e and e->p can be NULL, so check for those before proceeding
2015     g_return_if_fail(e != NULL);
2016     g_return_if_fail(&e->p != NULL);
2018     /* feel good is an arbitrary parameter that distributes the delta between handles
2019      * if t of the drag point is less than 1/6 distance form the endpoint only
2020      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2021      */
2022     double feel_good;
2023     if (t <= 1.0 / 6.0)
2024         feel_good = 0;
2025     else if (t <= 0.5)
2026         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2027     else if (t <= 5.0 / 6.0)
2028         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2029     else
2030         feel_good = 1;
2032     //if we're dragging a line convert it to a curve
2033     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2034         sp_nodepath_set_line_type(e, NR_CURVETO);
2035     }
2037     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2038     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2039     e->p.other->n.pos += offsetcoord0;
2040     e->p.pos += offsetcoord1;
2042     // adjust handles of adjacent nodes where necessary
2043     sp_node_adjust_handle(e,1);
2044     sp_node_adjust_handle(e->p.other,-1);
2046     sp_nodepath_update_handles(e->subpath->nodepath);
2048     update_object(e->subpath->nodepath);
2050     sp_nodepath_update_statusbar(e->subpath->nodepath);
2054 /**
2055  * Call sp_nodepath_break() for all selected segments.
2056  */
2057 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2059     if (!nodepath) return;
2061     GList *tempin = g_list_copy(nodepath->selected);
2062     GList *temp = NULL;
2063     for (GList *l = tempin; l != NULL; l = l->next) {
2064        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2065        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2066         if (nn == NULL) continue; // no break, no new node
2067         temp = g_list_prepend(temp, nn);
2068     }
2069     g_list_free(tempin);
2071     if (temp) {
2072         sp_nodepath_deselect(nodepath);
2073     }
2074     for (GList *l = temp; l != NULL; l = l->next) {
2075         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2076     }
2078     sp_nodepath_update_handles(nodepath);
2080     sp_nodepath_update_repr(nodepath, _("Break path"));
2083 /**
2084  * Duplicate the selected node(s).
2085  */
2086 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2088     if (!nodepath) {
2089         return;
2090     }
2092     GList *temp = NULL;
2093     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2094        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2095        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2096         if (nn == NULL) continue; // could not duplicate
2097         temp = g_list_prepend(temp, nn);
2098     }
2100     if (temp) {
2101         sp_nodepath_deselect(nodepath);
2102     }
2103     for (GList *l = temp; l != NULL; l = l->next) {
2104         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2105     }
2107     sp_nodepath_update_handles(nodepath);
2109     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2112 /**
2113  *  Internal function to join two nodes by merging them into one.
2114  */
2115 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2117     /* a and b are endpoints */
2119     // if one of the two nodes is mouseovered, fix its position
2120     NR::Point c;
2121     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2122         c = a->pos;
2123     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2124         c = b->pos;
2125     } else {
2126         // otherwise, move joined node to the midpoint
2127         c = (a->pos + b->pos) / 2;
2128     }
2130     if (a->subpath == b->subpath) {
2131        Inkscape::NodePath::SubPath *sp = a->subpath;
2132         sp_nodepath_subpath_close(sp);
2133         sp_node_moveto (sp->first, c);
2135         sp_nodepath_update_handles(sp->nodepath);
2136         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2137         return;
2138     }
2140     /* a and b are separate subpaths */
2141     Inkscape::NodePath::SubPath *sa = a->subpath;
2142     Inkscape::NodePath::SubPath *sb = b->subpath;
2143     NR::Point p;
2144     Inkscape::NodePath::Node *n;
2145     NRPathcode code;
2146     if (a == sa->first) {
2147         // we will now reverse sa, so that a is its last node, not first, and drop that node
2148         p = sa->first->n.pos;
2149         code = (NRPathcode)sa->first->n.other->code;
2150         // create new subpath
2151        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2152        // create a first moveto node on it
2153         n = sa->last;
2154         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2155         n = n->p.other;
2156         if (n == sa->first) n = NULL;
2157         while (n) {
2158             // copy the rest of the nodes from sa to t, going backwards
2159             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2160             n = n->p.other;
2161             if (n == sa->first) n = NULL;
2162         }
2163         // replace sa with t
2164         sp_nodepath_subpath_destroy(sa);
2165         sa = t;
2166     } else if (a == sa->last) {
2167         // a is already last, just drop it
2168         p = sa->last->p.pos;
2169         code = (NRPathcode)sa->last->code;
2170         sp_nodepath_node_destroy(sa->last);
2171     } else {
2172         code = NR_END;
2173         g_assert_not_reached();
2174     }
2176     if (b == sb->first) {
2177         // copy all nodes from b to a, forward 
2178         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2179         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2180             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2181         }
2182     } else if (b == sb->last) {
2183         // copy all nodes from b to a, backward 
2184         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2185         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2186             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2187         }
2188     } else {
2189         g_assert_not_reached();
2190     }
2191     /* and now destroy sb */
2193     sp_nodepath_subpath_destroy(sb);
2195     sp_nodepath_update_handles(sa->nodepath);
2197     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2199     sp_nodepath_update_statusbar(nodepath);
2202 /**
2203  *  Internal function to join two nodes by adding a segment between them.
2204  */
2205 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2207     if (a->subpath == b->subpath) {
2208        Inkscape::NodePath::SubPath *sp = a->subpath;
2210         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2211         sp->closed = TRUE;
2213         sp->first->p.other = sp->last;
2214         sp->last->n.other  = sp->first;
2216         sp_node_handle_mirror_p_to_n(sp->last);
2217         sp_node_handle_mirror_n_to_p(sp->first);
2219         sp->first->code = sp->last->code;
2220         sp->first       = sp->last;
2222         sp_nodepath_update_handles(sp->nodepath);
2224         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2226         return;
2227     }
2229     /* a and b are separate subpaths */
2230     Inkscape::NodePath::SubPath *sa = a->subpath;
2231     Inkscape::NodePath::SubPath *sb = b->subpath;
2233     Inkscape::NodePath::Node *n;
2234     NR::Point p;
2235     NRPathcode code;
2236     if (a == sa->first) {
2237         code = (NRPathcode) sa->first->n.other->code;
2238        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2239         n = sa->last;
2240         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2241         for (n = n->p.other; n != NULL; n = n->p.other) {
2242             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2243         }
2244         sp_nodepath_subpath_destroy(sa);
2245         sa = t;
2246     } else if (a == sa->last) {
2247         code = (NRPathcode)sa->last->code;
2248     } else {
2249         code = NR_END;
2250         g_assert_not_reached();
2251     }
2253     if (b == sb->first) {
2254         n = sb->first;
2255         sp_node_handle_mirror_p_to_n(sa->last);
2256         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2257         sp_node_handle_mirror_n_to_p(sa->last);
2258         for (n = n->n.other; n != NULL; n = n->n.other) {
2259             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2260         }
2261     } else if (b == sb->last) {
2262         n = sb->last;
2263         sp_node_handle_mirror_p_to_n(sa->last);
2264         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2265         sp_node_handle_mirror_n_to_p(sa->last);
2266         for (n = n->p.other; n != NULL; n = n->p.other) {
2267             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2268         }
2269     } else {
2270         g_assert_not_reached();
2271     }
2272     /* and now destroy sb */
2274     sp_nodepath_subpath_destroy(sb);
2276     sp_nodepath_update_handles(sa->nodepath);
2278     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2281 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2283 /**
2284  * Internal function to handle joining two nodes.
2285  */
2286 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2288     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2290     if (g_list_length(nodepath->selected) != 2) {
2291         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2292         return;
2293     }
2295     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2296     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2298     g_assert(a != b);
2299     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2300         // someone tried to join an orphan node (i.e. a single-node subpath).
2301         // this is not worth an error message, just fail silently.
2302         return;
2303     }
2305     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2306         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2307         return;
2308     }
2310     switch(mode) {
2311         case NODE_JOIN_ENDPOINTS:
2312             do_node_selected_join(nodepath, a, b);
2313             break;
2314         case NODE_JOIN_SEGMENT:
2315             do_node_selected_join_segment(nodepath, a, b);
2316             break;
2317     }
2320 /**
2321  *  Join two nodes by merging them into one.
2322  */
2323 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2325     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2328 /**
2329  *  Join two nodes by adding a segment between them.
2330  */
2331 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2333     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2336 /**
2337  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2338  */
2339 void sp_node_delete_preserve(GList *nodes_to_delete)
2341     GSList *nodepaths = NULL;
2343     while (nodes_to_delete) {
2344         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2345         Inkscape::NodePath::SubPath *sp = node->subpath;
2346         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2347         Inkscape::NodePath::Node *sample_cursor = NULL;
2348         Inkscape::NodePath::Node *sample_end = NULL;
2349         Inkscape::NodePath::Node *delete_cursor = node;
2350         bool just_delete = false;
2352         //find the start of this contiguous selection
2353         //move left to the first node that is not selected
2354         //or the start of the non-closed path
2355         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2356             delete_cursor = curr;
2357         }
2359         //just delete at the beginning of an open path
2360         if (!delete_cursor->p.other) {
2361             sample_cursor = delete_cursor;
2362             just_delete = true;
2363         } else {
2364             sample_cursor = delete_cursor->p.other;
2365         }
2367         //calculate points for each segment
2368         int rate = 5;
2369         float period = 1.0 / rate;
2370         std::vector<NR::Point> data;
2371         if (!just_delete) {
2372             data.push_back(sample_cursor->pos);
2373             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2374                 //just delete at the end of an open path
2375                 if (!sp->closed && curr == sp->last) {
2376                     just_delete = true;
2377                     break;
2378                 }
2380                 //sample points on the contiguous selected segment
2381                 NR::Point *bez;
2382                 bez = new NR::Point [4];
2383                 bez[0] = curr->pos;
2384                 bez[1] = curr->n.pos;
2385                 bez[2] = curr->n.other->p.pos;
2386                 bez[3] = curr->n.other->pos;
2387                 for (int i=1; i<rate; i++) {
2388                     gdouble t = i * period;
2389                     NR::Point p = bezier_pt(3, bez, t);
2390                     data.push_back(p);
2391                 }
2392                 data.push_back(curr->n.other->pos);
2394                 sample_end = curr->n.other;
2395                 //break if we've come full circle or hit the end of the selection
2396                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2397                     break;
2398                 }
2399             }
2400         }
2402         if (!just_delete) {
2403             //calculate the best fitting single segment and adjust the endpoints
2404             NR::Point *adata;
2405             adata = new NR::Point [data.size()];
2406             copy(data.begin(), data.end(), adata);
2408             NR::Point *bez;
2409             bez = new NR::Point [4];
2410             //would decreasing error create a better fitting approximation?
2411             gdouble error = 1.0;
2412             gint ret;
2413             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2415             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2416             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2417             //the resulting nodes behave as expected.
2418             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2419                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2420             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2421                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2423             //adjust endpoints
2424             sample_cursor->n.pos = bez[1];
2425             sample_end->p.pos = bez[2];
2426         }
2428         //destroy this contiguous selection
2429         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2430             Inkscape::NodePath::Node *temp = delete_cursor;
2431             if (delete_cursor->n.other == delete_cursor) {
2432                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2433                 delete_cursor = NULL;
2434             } else {
2435                 delete_cursor = delete_cursor->n.other;
2436             }
2437             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2438             sp_nodepath_node_destroy(temp);
2439         }
2441         sp_nodepath_update_handles(nodepath);
2443         if (!g_slist_find(nodepaths, nodepath))
2444             nodepaths = g_slist_prepend (nodepaths, nodepath);
2445     }
2447     for (GSList *i = nodepaths; i; i = i->next) {
2448         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2449         // different nodepaths will give us one undo event per nodepath
2450         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2452         // if the entire nodepath is removed, delete the selected object.
2453         if (nodepath->subpaths == NULL ||
2454             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2455             //at least 2
2456             sp_nodepath_get_node_count(nodepath) < 2) {
2457             SPDocument *document = sp_desktop_document (nodepath->desktop);
2458             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2459             //delete this nodepath's object, not the entire selection! (though at this time, this
2460             //does not matter)
2461             sp_selection_delete();
2462             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2463                               _("Delete nodes"));
2464         } else {
2465             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2466             sp_nodepath_update_statusbar(nodepath);
2467         }
2468     }
2470     g_slist_free (nodepaths);
2473 /**
2474  * Delete one or more selected nodes.
2475  */
2476 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2478     if (!nodepath) return;
2479     if (!nodepath->selected) return;
2481     /** \todo fixme: do it the right way */
2482     while (nodepath->selected) {
2483        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2484         sp_nodepath_node_destroy(node);
2485     }
2488     //clean up the nodepath (such as for trivial subpaths)
2489     sp_nodepath_cleanup(nodepath);
2491     sp_nodepath_update_handles(nodepath);
2493     // if the entire nodepath is removed, delete the selected object.
2494     if (nodepath->subpaths == NULL ||
2495         sp_nodepath_get_node_count(nodepath) < 2) {
2496         SPDocument *document = sp_desktop_document (nodepath->desktop);
2497         sp_selection_delete();
2498         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2499                           _("Delete nodes"));
2500         return;
2501     }
2503     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2505     sp_nodepath_update_statusbar(nodepath);
2508 /**
2509  * Delete one or more segments between two selected nodes.
2510  * This is the code for 'split'.
2511  */
2512 void
2513 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2515    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2516    Inkscape::NodePath::Node *curr, *next;     //Iterators
2518     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2520     if (g_list_length(nodepath->selected) != 2) {
2521         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2522                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2523         return;
2524     }
2526     //Selected nodes, not inclusive
2527    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2528    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2530     if ( ( a==b)                       ||  //same node
2531          (a->subpath  != b->subpath )  ||  //not the same path
2532          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2533          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2534     {
2535         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2536                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2537         return;
2538     }
2540     //###########################################
2541     //# BEGIN EDITS
2542     //###########################################
2543     //##################################
2544     //# CLOSED PATH
2545     //##################################
2546     if (a->subpath->closed) {
2549         gboolean reversed = FALSE;
2551         //Since we can go in a circle, we need to find the shorter distance.
2552         //  a->b or b->a
2553         start = end = NULL;
2554         int distance    = 0;
2555         int minDistance = 0;
2556         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2557             if (curr==b) {
2558                 //printf("a to b:%d\n", distance);
2559                 start = a;//go from a to b
2560                 end   = b;
2561                 minDistance = distance;
2562                 //printf("A to B :\n");
2563                 break;
2564             }
2565             distance++;
2566         }
2568         //try again, the other direction
2569         distance = 0;
2570         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2571             if (curr==a) {
2572                 //printf("b to a:%d\n", distance);
2573                 if (distance < minDistance) {
2574                     start    = b;  //we go from b to a
2575                     end      = a;
2576                     reversed = TRUE;
2577                     //printf("B to A\n");
2578                 }
2579                 break;
2580             }
2581             distance++;
2582         }
2585         //Copy everything from 'end' to 'start' to a new subpath
2586        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2587         for (curr=end ; curr ; curr=curr->n.other) {
2588             NRPathcode code = (NRPathcode) curr->code;
2589             if (curr == end)
2590                 code = NR_MOVETO;
2591             sp_nodepath_node_new(t, NULL,
2592                                  (Inkscape::NodePath::NodeType)curr->type, code,
2593                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2594             if (curr == start)
2595                 break;
2596         }
2597         sp_nodepath_subpath_destroy(a->subpath);
2600     }
2604     //##################################
2605     //# OPEN PATH
2606     //##################################
2607     else {
2609         //We need to get the direction of the list between A and B
2610         //Can we walk from a to b?
2611         start = end = NULL;
2612         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2613             if (curr==b) {
2614                 start = a;  //did it!  we go from a to b
2615                 end   = b;
2616                 //printf("A to B\n");
2617                 break;
2618             }
2619         }
2620         if (!start) {//didn't work?  let's try the other direction
2621             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2622                 if (curr==a) {
2623                     start = b;  //did it!  we go from b to a
2624                     end   = a;
2625                     //printf("B to A\n");
2626                     break;
2627                 }
2628             }
2629         }
2630         if (!start) {
2631             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2632                                                      _("Cannot find path between nodes."));
2633             return;
2634         }
2638         //Copy everything after 'end' to a new subpath
2639        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2640         for (curr=end ; curr ; curr=curr->n.other) {
2641             NRPathcode code = (NRPathcode) curr->code;
2642             if (curr == end)
2643                 code = NR_MOVETO;
2644             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2645                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2646         }
2648         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2649         for (curr = start->n.other ; curr  ; curr=next) {
2650             next = curr->n.other;
2651             sp_nodepath_node_destroy(curr);
2652         }
2654     }
2655     //###########################################
2656     //# END EDITS
2657     //###########################################
2659     //clean up the nodepath (such as for trivial subpaths)
2660     sp_nodepath_cleanup(nodepath);
2662     sp_nodepath_update_handles(nodepath);
2664     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2666     sp_nodepath_update_statusbar(nodepath);
2669 /**
2670  * Call sp_nodepath_set_line() for all selected segments.
2671  */
2672 void
2673 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2675     if (nodepath == NULL) return;
2677     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2678        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2679         g_assert(n->selected);
2680         if (n->p.other && n->p.other->selected) {
2681             sp_nodepath_set_line_type(n, code);
2682         }
2683     }
2685     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2688 /**
2689  * Call sp_nodepath_convert_node_type() for all selected nodes.
2690  */
2691 void
2692 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2694     if (nodepath == NULL) return;
2696     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2698     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2699         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2700     }
2702     sp_nodepath_update_repr(nodepath, _("Change node type"));
2705 /**
2706  * Change select status of node, update its own and neighbour handles.
2707  */
2708 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2710     node->selected = selected;
2712     if (selected) {
2713         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2714         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2715         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2716         sp_knot_update_ctrl(node->knot);
2717     } else {
2718         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2719         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2720         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2721         sp_knot_update_ctrl(node->knot);
2722     }
2724     sp_node_update_handles(node);
2725     if (node->n.other) sp_node_update_handles(node->n.other);
2726     if (node->p.other) sp_node_update_handles(node->p.other);
2729 /**
2730 \brief Select a node
2731 \param node     The node to select
2732 \param incremental   If true, add to selection, otherwise deselect others
2733 \param override   If true, always select this node, otherwise toggle selected status
2734 */
2735 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2737     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2739     if (incremental) {
2740         if (override) {
2741             if (!g_list_find(nodepath->selected, node)) {
2742                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2743             }
2744             sp_node_set_selected(node, TRUE);
2745         } else { // toggle
2746             if (node->selected) {
2747                 g_assert(g_list_find(nodepath->selected, node));
2748                 nodepath->selected = g_list_remove(nodepath->selected, node);
2749             } else {
2750                 g_assert(!g_list_find(nodepath->selected, node));
2751                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2752             }
2753             sp_node_set_selected(node, !node->selected);
2754         }
2755     } else {
2756         sp_nodepath_deselect(nodepath);
2757         nodepath->selected = g_list_prepend(nodepath->selected, node);
2758         sp_node_set_selected(node, TRUE);
2759     }
2761     sp_nodepath_update_statusbar(nodepath);
2765 /**
2766 \brief Deselect all nodes in the nodepath
2767 */
2768 void
2769 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2771     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2773     while (nodepath->selected) {
2774         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2775         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2776     }
2777     sp_nodepath_update_statusbar(nodepath);
2780 /**
2781 \brief Select or invert selection of all nodes in the nodepath
2782 */
2783 void
2784 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2786     if (!nodepath) return;
2788     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2789        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2790         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2791            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2792            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2793         }
2794     }
2797 /**
2798  * If nothing selected, does the same as sp_nodepath_select_all();
2799  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2800  * (i.e., similar to "select all in layer", with the "selected" subpaths
2801  * being treated as "layers" in the path).
2802  */
2803 void
2804 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2806     if (!nodepath) return;
2808     if (g_list_length (nodepath->selected) == 0) {
2809         sp_nodepath_select_all (nodepath, invert);
2810         return;
2811     }
2813     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2814     GSList *subpaths = NULL;
2816     for (GList *l = copy; l != NULL; l = l->next) {
2817         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2818         Inkscape::NodePath::SubPath *subpath = n->subpath;
2819         if (!g_slist_find (subpaths, subpath))
2820             subpaths = g_slist_prepend (subpaths, subpath);
2821     }
2823     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2824         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2825         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2826             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2827             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2828         }
2829     }
2831     g_slist_free (subpaths);
2832     g_list_free (copy);
2835 /**
2836  * \brief Select the node after the last selected; if none is selected,
2837  * select the first within path.
2838  */
2839 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2841     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2843    Inkscape::NodePath::Node *last = NULL;
2844     if (nodepath->selected) {
2845         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2846            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2847             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2848             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2849                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2850                 if (node->selected) {
2851                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2852                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2853                             if (spl->next) { // there's a next subpath
2854                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2855                                 last = subpath_next->first;
2856                             } else if (spl->prev) { // there's a previous subpath
2857                                 last = NULL; // to be set later to the first node of first subpath
2858                             } else {
2859                                 last = node->n.other;
2860                             }
2861                         } else {
2862                             last = node->n.other;
2863                         }
2864                     } else {
2865                         if (node->n.other) {
2866                             last = node->n.other;
2867                         } else {
2868                             if (spl->next) { // there's a next subpath
2869                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2870                                 last = subpath_next->first;
2871                             } else if (spl->prev) { // there's a previous subpath
2872                                 last = NULL; // to be set later to the first node of first subpath
2873                             } else {
2874                                 last = (Inkscape::NodePath::Node *) subpath->first;
2875                             }
2876                         }
2877                     }
2878                 }
2879             }
2880         }
2881         sp_nodepath_deselect(nodepath);
2882     }
2884     if (last) { // there's at least one more node after selected
2885         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2886     } else { // no more nodes, select the first one in first subpath
2887        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2888         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2889     }
2892 /**
2893  * \brief Select the node before the first selected; if none is selected,
2894  * select the last within path
2895  */
2896 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2898     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2900    Inkscape::NodePath::Node *last = NULL;
2901     if (nodepath->selected) {
2902         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2903            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2904             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2905                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2906                 if (node->selected) {
2907                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2908                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2909                             if (spl->prev) { // there's a prev subpath
2910                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2911                                 last = subpath_prev->last;
2912                             } else if (spl->next) { // there's a next subpath
2913                                 last = NULL; // to be set later to the last node of last subpath
2914                             } else {
2915                                 last = node->p.other;
2916                             }
2917                         } else {
2918                             last = node->p.other;
2919                         }
2920                     } else {
2921                         if (node->p.other) {
2922                             last = node->p.other;
2923                         } else {
2924                             if (spl->prev) { // there's a prev subpath
2925                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2926                                 last = subpath_prev->last;
2927                             } else if (spl->next) { // there's a next subpath
2928                                 last = NULL; // to be set later to the last node of last subpath
2929                             } else {
2930                                 last = (Inkscape::NodePath::Node *) subpath->last;
2931                             }
2932                         }
2933                     }
2934                 }
2935             }
2936         }
2937         sp_nodepath_deselect(nodepath);
2938     }
2940     if (last) { // there's at least one more node before selected
2941         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2942     } else { // no more nodes, select the last one in last subpath
2943         GList *spl = g_list_last(nodepath->subpaths);
2944        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2945         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2946     }
2949 /**
2950  * \brief Select all nodes that are within the rectangle.
2951  */
2952 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2954     if (!incremental) {
2955         sp_nodepath_deselect(nodepath);
2956     }
2958     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2959        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2960         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2961            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2963             if (b.contains(node->pos)) {
2964                 sp_nodepath_node_select(node, TRUE, TRUE);
2965             }
2966         }
2967     }
2971 void
2972 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2974     g_assert (n);
2975     g_assert (nodepath);
2976     g_assert (n->subpath->nodepath == nodepath);
2978     if (g_list_length (nodepath->selected) == 0) {
2979         if (grow > 0) {
2980             sp_nodepath_node_select(n, TRUE, TRUE);
2981         }
2982         return;
2983     }
2985     if (g_list_length (nodepath->selected) == 1) {
2986         if (grow < 0) {
2987             sp_nodepath_deselect (nodepath);
2988             return;
2989         }
2990     }
2992         double n_sel_range = 0, p_sel_range = 0;
2993             Inkscape::NodePath::Node *farthest_n_node = n;
2994             Inkscape::NodePath::Node *farthest_p_node = n;
2996         // Calculate ranges
2997         {
2998             double n_range = 0, p_range = 0;
2999             bool n_going = true, p_going = true;
3000             Inkscape::NodePath::Node *n_node = n;
3001             Inkscape::NodePath::Node *p_node = n;
3002             do {
3003                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3004                 if (n_node && n_going)
3005                     n_node = n_node->n.other;
3006                 if (n_node == NULL) {
3007                     n_going = false;
3008                 } else {
3009                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3010                     if (n_node->selected) {
3011                         n_sel_range = n_range;
3012                         farthest_n_node = n_node;
3013                     }
3014                     if (n_node == p_node) {
3015                         n_going = false;
3016                         p_going = false;
3017                     }
3018                 }
3019                 if (p_node && p_going)
3020                     p_node = p_node->p.other;
3021                 if (p_node == NULL) {
3022                     p_going = false;
3023                 } else {
3024                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3025                     if (p_node->selected) {
3026                         p_sel_range = p_range;
3027                         farthest_p_node = p_node;
3028                     }
3029                     if (p_node == n_node) {
3030                         n_going = false;
3031                         p_going = false;
3032                     }
3033                 }
3034             } while (n_going || p_going);
3035         }
3037     if (grow > 0) {
3038         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3039                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3040         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3041                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3042         }
3043     } else {
3044         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3045                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3046         } else if (farthest_p_node && farthest_p_node->selected) {
3047                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3048         }
3049     }
3052 void
3053 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3055     g_assert (n);
3056     g_assert (nodepath);
3057     g_assert (n->subpath->nodepath == nodepath);
3059     if (g_list_length (nodepath->selected) == 0) {
3060         if (grow > 0) {
3061             sp_nodepath_node_select(n, TRUE, TRUE);
3062         }
3063         return;
3064     }
3066     if (g_list_length (nodepath->selected) == 1) {
3067         if (grow < 0) {
3068             sp_nodepath_deselect (nodepath);
3069             return;
3070         }
3071     }
3073     Inkscape::NodePath::Node *farthest_selected = NULL;
3074     double farthest_dist = 0;
3076     Inkscape::NodePath::Node *closest_unselected = NULL;
3077     double closest_dist = NR_HUGE;
3079     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3080        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3081         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3082            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3083            if (node == n)
3084                continue;
3085            if (node->selected) {
3086                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3087                    farthest_dist = NR::L2(node->pos - n->pos);
3088                    farthest_selected = node;
3089                }
3090            } else {
3091                if (NR::L2(node->pos - n->pos) < closest_dist) {
3092                    closest_dist = NR::L2(node->pos - n->pos);
3093                    closest_unselected = node;
3094                }
3095            }
3096         }
3097     }
3099     if (grow > 0) {
3100         if (closest_unselected) {
3101             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3102         }
3103     } else {
3104         if (farthest_selected) {
3105             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3106         }
3107     }
3111 /**
3112 \brief  Saves all nodes' and handles' current positions in their origin members
3113 */
3114 void
3115 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3117     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3118        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3119         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3120            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3121            n->origin = n->pos;
3122            n->p.origin = n->p.pos;
3123            n->n.origin = n->n.pos;
3124         }
3125     }
3128 /**
3129 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3130 */
3131 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3133     if (!nodepath->selected) {
3134         return NULL;
3135     }
3137     GList *r = NULL;
3138     guint i = 0;
3139     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3140        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3141         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3142            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3143             i++;
3144             if (node->selected) {
3145                 r = g_list_append(r, GINT_TO_POINTER(i));
3146             }
3147         }
3148     }
3149     return r;
3152 /**
3153 \brief  Restores selection by selecting nodes whose positions are in the list
3154 */
3155 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3157     sp_nodepath_deselect(nodepath);
3159     guint i = 0;
3160     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3161        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3162         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3163            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3164             i++;
3165             if (g_list_find(r, GINT_TO_POINTER(i))) {
3166                 sp_nodepath_node_select(node, TRUE, TRUE);
3167             }
3168         }
3169     }
3173 /**
3174 \brief Adjusts handle according to node type and line code.
3175 */
3176 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3178     g_assert(node);
3180    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3181    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3183    // nothing to do if we are an end node
3184     if (me->other == NULL) return;
3185     if (other->other == NULL) return;
3187     // nothing to do if we are a cusp node
3188     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3190     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3191     NRPathcode mecode;
3192     if (which_adjust == 1) {
3193         mecode = (NRPathcode)me->other->code;
3194     } else {
3195         mecode = (NRPathcode)node->code;
3196     }
3197     if (mecode == NR_LINETO) return;
3199     if (sp_node_side_is_line(node, other)) {
3200         // other is a line, and we are either smooth or symm
3201        Inkscape::NodePath::Node *othernode = other->other;
3202         double len = NR::L2(me->pos - node->pos);
3203         NR::Point delta = node->pos - othernode->pos;
3204         double linelen = NR::L2(delta);
3205         if (linelen < 1e-18)
3206             return;
3207         me->pos = node->pos + (len / linelen)*delta;
3208         return;
3209     }
3211     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3212         // symmetrize 
3213         me->pos = 2 * node->pos - other->pos;
3214         return;
3215     } else {
3216         // smoothify
3217         double len = NR::L2(me->pos - node->pos);
3218         NR::Point delta = other->pos - node->pos;
3219         double otherlen = NR::L2(delta);
3220         if (otherlen < 1e-18) return;
3221         me->pos = node->pos - (len / otherlen) * delta;
3222     }
3225 /**
3226  \brief Adjusts both handles according to node type and line code
3227  */
3228 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3230     g_assert(node);
3232     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3234     /* we are either smooth or symm */
3236     if (node->p.other == NULL) return;
3237     if (node->n.other == NULL) return;
3239     if (sp_node_side_is_line(node, &node->p)) {
3240         sp_node_adjust_handle(node, 1);
3241         return;
3242     }
3244     if (sp_node_side_is_line(node, &node->n)) {
3245         sp_node_adjust_handle(node, -1);
3246         return;
3247     }
3249     /* both are curves */
3250     NR::Point const delta( node->n.pos - node->p.pos );
3252     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3253         node->p.pos = node->pos - delta / 2;
3254         node->n.pos = node->pos + delta / 2;
3255         return;
3256     }
3258     /* We are smooth */
3259     double plen = NR::L2(node->p.pos - node->pos);
3260     if (plen < 1e-18) return;
3261     double nlen = NR::L2(node->n.pos - node->pos);
3262     if (nlen < 1e-18) return;
3263     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3264     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3267 /**
3268  * Node event callback.
3269  */
3270 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3272     gboolean ret = FALSE;
3273     switch (event->type) {
3274         case GDK_ENTER_NOTIFY:
3275             Inkscape::NodePath::Path::active_node = n;
3276             break;
3277         case GDK_LEAVE_NOTIFY:
3278             Inkscape::NodePath::Path::active_node = NULL;
3279             break;
3280         case GDK_SCROLL:
3281             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3282                 switch (event->scroll.direction) {
3283                     case GDK_SCROLL_UP:
3284                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3285                         break;
3286                     case GDK_SCROLL_DOWN:
3287                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3288                         break;
3289                     default:
3290                         break;
3291                 }
3292                 ret = TRUE;
3293             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3294                 switch (event->scroll.direction) {
3295                     case GDK_SCROLL_UP:
3296                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3297                         break;
3298                     case GDK_SCROLL_DOWN:
3299                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3300                         break;
3301                     default:
3302                         break;
3303                 }
3304                 ret = TRUE;
3305             }
3306             break;
3307         case GDK_KEY_PRESS:
3308             switch (get_group0_keyval (&event->key)) {
3309                 case GDK_space:
3310                     if (event->key.state & GDK_BUTTON1_MASK) {
3311                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3312                         stamp_repr(nodepath);
3313                         ret = TRUE;
3314                     }
3315                     break;
3316                 case GDK_Page_Up:
3317                     if (event->key.state & GDK_CONTROL_MASK) {
3318                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3319                     } else {
3320                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3321                     }
3322                     break;
3323                 case GDK_Page_Down:
3324                     if (event->key.state & GDK_CONTROL_MASK) {
3325                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3326                     } else {
3327                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3328                     }
3329                     break;
3330                 default:
3331                     break;
3332             }
3333             break;
3334         default:
3335             break;
3336     }
3338     return ret;
3341 /**
3342  * Handle keypress on node; directly called.
3343  */
3344 gboolean node_key(GdkEvent *event)
3346     Inkscape::NodePath::Path *np;
3348     // there is no way to verify nodes so set active_node to nil when deleting!!
3349     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3351     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3352         gint ret = FALSE;
3353         switch (get_group0_keyval (&event->key)) {
3354             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3355             case GDK_BackSpace:
3356                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3357                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3358                 sp_nodepath_update_repr(np, _("Delete node"));
3359                 Inkscape::NodePath::Path::active_node = NULL;
3360                 ret = TRUE;
3361                 break;
3362             case GDK_c:
3363                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3364                 ret = TRUE;
3365                 break;
3366             case GDK_s:
3367                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3368                 ret = TRUE;
3369                 break;
3370             case GDK_y:
3371                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3372                 ret = TRUE;
3373                 break;
3374             case GDK_b:
3375                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3376                 ret = TRUE;
3377                 break;
3378         }
3379         return ret;
3380     }
3381     return FALSE;
3384 /**
3385  * Mouseclick on node callback.
3386  */
3387 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3389    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3391     if (state & GDK_CONTROL_MASK) {
3392         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3394         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3395             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3396                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3397             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3398                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3399             } else {
3400                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3401             }
3402             sp_nodepath_update_repr(nodepath, _("Change node type"));
3403             sp_nodepath_update_statusbar(nodepath);
3405         } else { //ctrl+alt+click: delete node
3406             GList *node_to_delete = NULL;
3407             node_to_delete = g_list_append(node_to_delete, n);
3408             sp_node_delete_preserve(node_to_delete);
3409         }
3411     } else {
3412         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3413     }
3416 /**
3417  * Mouse grabbed node callback.
3418  */
3419 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3421    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3423     if (!n->selected) {
3424         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3425     }
3427     n->is_dragging = true;
3428     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3430     sp_nodepath_remember_origins (n->subpath->nodepath);
3433 /**
3434  * Mouse ungrabbed node callback.
3435  */
3436 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3438    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3440    n->dragging_out = NULL;
3441    n->is_dragging = false;
3442    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3444    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3447 /**
3448  * The point on a line, given by its angle, closest to the given point.
3449  * \param p  A point.
3450  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3451  * \param closest  Pointer to the point struct where the result is stored.
3452  * \todo FIXME: use dot product perhaps?
3453  */
3454 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3456     if (a == HUGE_VAL) { // vertical
3457         *closest = NR::Point(0, (*p)[NR::Y]);
3458     } else {
3459         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3460         (*closest)[NR::Y] = a * (*closest)[NR::X];
3461     }
3464 /**
3465  * Distance from the point to a line given by its angle.
3466  * \param p  A point.
3467  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3468  */
3469 static double point_line_distance(NR::Point *p, double a)
3471     NR::Point c;
3472     point_line_closest(p, a, &c);
3473     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]));
3476 /**
3477  * Callback for node "request" signal.
3478  * \todo fixme: This goes to "moved" event? (lauris)
3479  */
3480 static gboolean
3481 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3483     double yn, xn, yp, xp;
3484     double an, ap, na, pa;
3485     double d_an, d_ap, d_na, d_pa;
3486     gboolean collinear = FALSE;
3487     NR::Point c;
3488     NR::Point pr;
3490     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3492     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3494     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3495     if ( (!n->subpath->nodepath->straight_path) &&
3496          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3497            || n->dragging_out ) )
3498     {
3499        NR::Point mouse = (*p);
3501        if (!n->dragging_out) {
3502            // This is the first drag-out event; find out which handle to drag out
3503            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3504            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3506            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3507                return FALSE;
3509            Inkscape::NodePath::NodeSide *opposite;
3510            if (appr_p > appr_n) { // closer to p
3511                n->dragging_out = &n->p;
3512                opposite = &n->n;
3513                n->code = NR_CURVETO;
3514            } else if (appr_p < appr_n) { // closer to n
3515                n->dragging_out = &n->n;
3516                opposite = &n->p;
3517                n->n.other->code = NR_CURVETO;
3518            } else { // p and n nodes are the same
3519                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3520                    n->dragging_out = &n->p;
3521                    opposite = &n->n;
3522                    n->code = NR_CURVETO;
3523                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3524                    n->dragging_out = &n->n;
3525                    opposite = &n->p;
3526                    n->n.other->code = NR_CURVETO;
3527                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3528                    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);
3529                    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);
3530                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3531                        n->dragging_out = &n->n;
3532                        opposite = &n->p;
3533                        n->n.other->code = NR_CURVETO;
3534                    } else { // closer to other's n handle
3535                        n->dragging_out = &n->p;
3536                        opposite = &n->n;
3537                        n->code = NR_CURVETO;
3538                    }
3539                }
3540            }
3542            // if there's another handle, make sure the one we drag out starts parallel to it
3543            if (opposite->pos != n->pos) {
3544                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3545            }
3547            // knots might not be created yet!
3548            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3549            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3550        }
3552        // pass this on to the handle-moved callback
3553        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3554        sp_node_update_handles(n);
3555        return TRUE;
3556    }
3558     if (state & GDK_CONTROL_MASK) { // constrained motion
3560         // calculate relative distances of handles
3561         // n handle:
3562         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3563         xn = n->n.pos[NR::X] - n->pos[NR::X];
3564         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3565         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3566             if (n->n.other) { // if there is the next point
3567                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3568                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3569                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3570             }
3571         }
3572         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3573         if (yn < 0) { xn = -xn; yn = -yn; }
3575         // p handle:
3576         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3577         xp = n->p.pos[NR::X] - n->pos[NR::X];
3578         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3579         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3580             if (n->p.other) {
3581                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3582                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3583                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3584             }
3585         }
3586         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3587         if (yp < 0) { xp = -xp; yp = -yp; }
3589         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3590             // sliding on handles, only if at least one of the handles is non-vertical
3591             // (otherwise it's the same as ctrl+drag anyway)
3593             // calculate angles of the handles
3594             if (xn == 0) {
3595                 if (yn == 0) { // no handle, consider it the continuation of the other one
3596                     an = 0;
3597                     collinear = TRUE;
3598                 }
3599                 else an = 0; // vertical; set the angle to horizontal
3600             } else an = yn/xn;
3602             if (xp == 0) {
3603                 if (yp == 0) { // no handle, consider it the continuation of the other one
3604                     ap = an;
3605                 }
3606                 else ap = 0; // vertical; set the angle to horizontal
3607             } else  ap = yp/xp;
3609             if (collinear) an = ap;
3611             // angles of the perpendiculars; HUGE_VAL means vertical
3612             if (an == 0) na = HUGE_VAL; else na = -1/an;
3613             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3615             // mouse point relative to the node's original pos
3616             pr = (*p) - n->origin;
3618             // distances to the four lines (two handles and two perpendiculars)
3619             d_an = point_line_distance(&pr, an);
3620             d_na = point_line_distance(&pr, na);
3621             d_ap = point_line_distance(&pr, ap);
3622             d_pa = point_line_distance(&pr, pa);
3624             // find out which line is the closest, save its closest point in c
3625             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3626                 point_line_closest(&pr, an, &c);
3627             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3628                 point_line_closest(&pr, ap, &c);
3629             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3630                 point_line_closest(&pr, na, &c);
3631             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3632                 point_line_closest(&pr, pa, &c);
3633             }
3635             // move the node to the closest point
3636             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3637                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3638                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3639                                             true);
3641         } else {  // constraining to hor/vert
3643             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3644                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3645                                                 (*p)[NR::X] - n->pos[NR::X], 
3646                                                 n->origin[NR::Y] - n->pos[NR::Y],
3647                                                 true, 
3648                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3649             } else { // snap to vert
3650                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3651                                                 n->origin[NR::X] - n->pos[NR::X],
3652                                                 (*p)[NR::Y] - n->pos[NR::Y],
3653                                                 true,
3654                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3655             }
3656         }
3657     } else { // move freely
3658         if (n->is_dragging) {
3659             if (state & GDK_MOD1_MASK) { // sculpt
3660                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3661             } else {
3662                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3663                                             (*p)[NR::X] - n->pos[NR::X],
3664                                             (*p)[NR::Y] - n->pos[NR::Y],
3665                                             (state & GDK_SHIFT_MASK) == 0);
3666             }
3667         }
3668     }
3670     n->subpath->nodepath->desktop->scroll_to_point(p);
3672     return TRUE;
3675 /**
3676  * Node handle clicked callback.
3677  */
3678 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3680    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3682     if (state & GDK_CONTROL_MASK) { // "delete" handle
3683         if (n->p.knot == knot) {
3684             n->p.pos = n->pos;
3685         } else if (n->n.knot == knot) {
3686             n->n.pos = n->pos;
3687         }
3688         sp_node_update_handles(n);
3689         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3690         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3691         sp_nodepath_update_statusbar(nodepath);
3693     } else { // just select or add to selection, depending in Shift
3694         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3695     }
3698 /**
3699  * Node handle grabbed callback.
3700  */
3701 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3703    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3705     if (!n->selected) {
3706         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3707     }
3709     // remember the origin point of the handle
3710     if (n->p.knot == knot) {
3711         n->p.origin_radial = n->p.pos - n->pos;
3712     } else if (n->n.knot == knot) {
3713         n->n.origin_radial = n->n.pos - n->pos;
3714     } else {
3715         g_assert_not_reached();
3716     }
3718     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3721 /**
3722  * Node handle ungrabbed callback.
3723  */
3724 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3726    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3728     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3729     if (n->p.knot == knot) {
3730         n->p.origin_radial.a = 0;
3731         sp_knot_set_position(knot, &n->p.pos, state);
3732     } else if (n->n.knot == knot) {
3733         n->n.origin_radial.a = 0;
3734         sp_knot_set_position(knot, &n->n.pos, state);
3735     } else {
3736         g_assert_not_reached();
3737     }
3739     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3742 /**
3743  * Node handle "request" signal callback.
3744  */
3745 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3747     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3749     Inkscape::NodePath::NodeSide *me, *opposite;
3750     gint which;
3751     if (n->p.knot == knot) {
3752         me = &n->p;
3753         opposite = &n->n;
3754         which = -1;
3755     } else if (n->n.knot == knot) {
3756         me = &n->n;
3757         opposite = &n->p;
3758         which = 1;
3759     } else {
3760         me = opposite = NULL;
3761         which = 0;
3762         g_assert_not_reached();
3763     }
3765     SPDesktop *desktop = n->subpath->nodepath->desktop;
3766     SnapManager &m = desktop->namedview->snap_manager;
3767     m.setup(desktop, n->subpath->nodepath->item);
3768     Inkscape::SnappedPoint s ;
3770     Inkscape::NodePath::Node *othernode = opposite->other;
3771     if (othernode) {
3772         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3773             /* We are smooth node adjacent with line */
3774             NR::Point const delta = *p - n->pos;
3775             NR::Coord const len = NR::L2(delta);
3776             Inkscape::NodePath::Node *othernode = opposite->other;
3777             NR::Point const ndelta = n->pos - othernode->pos;
3778             NR::Coord const linelen = NR::L2(ndelta);
3779             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3780                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3781                 (*p) = n->pos + (scal / linelen) * ndelta;
3782             }
3783             s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3784         } else {
3785             s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3786         }
3787     } else {
3788         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3789     }
3790     
3791     s.getPoint(*p);
3792     
3793     sp_node_adjust_handle(n, -which);
3795     return FALSE;
3798 /**
3799  * Node handle moved callback.
3800  */
3801 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3803    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3805    Inkscape::NodePath::NodeSide *me;
3806    Inkscape::NodePath::NodeSide *other;
3807     if (n->p.knot == knot) {
3808         me = &n->p;
3809         other = &n->n;
3810     } else if (n->n.knot == knot) {
3811         me = &n->n;
3812         other = &n->p;
3813     } else {
3814         me = NULL;
3815         other = NULL;
3816         g_assert_not_reached();
3817     }
3819     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3820     Radial rme(me->pos - n->pos);
3821     Radial rother(other->pos - n->pos);
3822     Radial rnew(*p - n->pos);
3824     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3825         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3826         /* 0 interpreted as "no snapping". */
3828         // 1. Snap to the closest PI/snaps angle, starting from zero.
3829         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3831         // 2. Snap to the original angle, its opposite and perpendiculars
3832         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3833             /* The closest PI/2 angle, starting from original angle */
3834             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3836             // Snap to the closest.
3837             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3838                        ? a_snapped
3839                        : a_ortho );
3840         }
3842         // 3. Snap to the angle of the opposite line, if any
3843         Inkscape::NodePath::Node *othernode = other->other;
3844         if (othernode) {
3845             NR::Point other_to_snap(0,0);
3846             if (sp_node_side_is_line(n, other)) {
3847                 other_to_snap = othernode->pos - n->pos;
3848             } else {
3849                 other_to_snap = other->pos - n->pos;
3850             }
3851             if (NR::L2(other_to_snap) > 1e-3) {
3852                 Radial rother_to_snap(other_to_snap);
3853                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3854                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3856                 // Snap to the closest.
3857                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3858                        ? a_snapped
3859                        : a_oppo );
3860             }
3861         }
3863         rnew.a = a_snapped;
3864     }
3866     if (state & GDK_MOD1_MASK) {
3867         // lock handle length
3868         rnew.r = me->origin_radial.r;
3869     }
3871     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3872         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3873         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3874         rother.a += rnew.a - rme.a;
3875         other->pos = NR::Point(rother) + n->pos;
3876         if (other->knot) {
3877             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3878             sp_knot_moveto(other->knot, &other->pos);
3879         }
3880     }
3882     me->pos = NR::Point(rnew) + n->pos;
3883     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3885     // move knot, but without emitting the signal:
3886     // we cannot emit a "moved" signal because we're now processing it
3887     sp_knot_moveto(me->knot, &(me->pos));
3889     update_object(n->subpath->nodepath);
3891     /* status text */
3892     SPDesktop *desktop = n->subpath->nodepath->desktop;
3893     if (!desktop) return;
3894     SPEventContext *ec = desktop->event_context;
3895     if (!ec) return;
3896     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3897     if (!mc) return;
3899     double degrees = 180 / M_PI * rnew.a;
3900     if (degrees > 180) degrees -= 360;
3901     if (degrees < -180) degrees += 360;
3902     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3903         degrees = angle_to_compass (degrees);
3905     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3907     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3908          _("<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);
3910     g_string_free(length, TRUE);
3913 /**
3914  * Node handle event callback.
3915  */
3916 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3918     gboolean ret = FALSE;
3919     switch (event->type) {
3920         case GDK_KEY_PRESS:
3921             switch (get_group0_keyval (&event->key)) {
3922                 case GDK_space:
3923                     if (event->key.state & GDK_BUTTON1_MASK) {
3924                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3925                         stamp_repr(nodepath);
3926                         ret = TRUE;
3927                     }
3928                     break;
3929                 default:
3930                     break;
3931             }
3932             break;
3933         case GDK_ENTER_NOTIFY:
3934             // we use an experimentally determined threshold that seems to work fine
3935             if (NR::L2(n->pos - knot->pos) < 0.75)
3936                 Inkscape::NodePath::Path::active_node = n;
3937             break;
3938         case GDK_LEAVE_NOTIFY:
3939             // we use an experimentally determined threshold that seems to work fine
3940             if (NR::L2(n->pos - knot->pos) < 0.75)
3941                 Inkscape::NodePath::Path::active_node = NULL;
3942             break;
3943         default:
3944             break;
3945     }
3947     return ret;
3950 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3951                                  Radial &rme, Radial &rother, gboolean const both)
3953     rme.a += angle;
3954     if ( both
3955          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3956          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3957     {
3958         rother.a += angle;
3959     }
3962 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3963                                         Radial &rme, Radial &rother, gboolean const both)
3965     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3967     gdouble r;
3968     if ( both
3969          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3970          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3971     {
3972         r = MAX(rme.r, rother.r);
3973     } else {
3974         r = rme.r;
3975     }
3977     gdouble const weird_angle = atan2(norm_angle, r);
3978 /* Bulia says norm_angle is just the visible distance that the
3979  * object's end must travel on the screen.  Left as 'angle' for want of
3980  * a better name.*/
3982     rme.a += weird_angle;
3983     if ( both
3984          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3985          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3986     {
3987         rother.a += weird_angle;
3988     }
3991 /**
3992  * Rotate one node.
3993  */
3994 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3996     Inkscape::NodePath::NodeSide *me, *other;
3997     bool both = false;
3999     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4000     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4002     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4003         me = &(n->p);
4004         other = &(n->n);
4005     } else if (!n->p.other) {
4006         me = &(n->n);
4007         other = &(n->p);
4008     } else {
4009         if (which > 0) { // right handle
4010             if (xn > xp) {
4011                 me = &(n->n);
4012                 other = &(n->p);
4013             } else {
4014                 me = &(n->p);
4015                 other = &(n->n);
4016             }
4017         } else if (which < 0){ // left handle
4018             if (xn <= xp) {
4019                 me = &(n->n);
4020                 other = &(n->p);
4021             } else {
4022                 me = &(n->p);
4023                 other = &(n->n);
4024             }
4025         } else { // both handles
4026             me = &(n->n);
4027             other = &(n->p);
4028             both = true;
4029         }
4030     }
4032     Radial rme(me->pos - n->pos);
4033     Radial rother(other->pos - n->pos);
4035     if (screen) {
4036         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4037     } else {
4038         node_rotate_one_internal (*n, angle, rme, rother, both);
4039     }
4041     me->pos = n->pos + NR::Point(rme);
4043     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4044         other->pos =  n->pos + NR::Point(rother);
4045     }
4047     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4048     // so here we just move all the knots without emitting move signals, for speed
4049     sp_node_update_handles(n, false);
4052 /**
4053  * Rotate selected nodes.
4054  */
4055 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4057     if (!nodepath || !nodepath->selected) return;
4059     if (g_list_length(nodepath->selected) == 1) {
4060        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4061         node_rotate_one (n, angle, which, screen);
4062     } else {
4063        // rotate as an object:
4065         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4066         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4067         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4068             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4069             box.expandTo (n->pos); // contain all selected nodes
4070         }
4072         gdouble rot;
4073         if (screen) {
4074             gdouble const zoom = nodepath->desktop->current_zoom();
4075             gdouble const zmove = angle / zoom;
4076             gdouble const r = NR::L2(box.max() - box.midpoint());
4077             rot = atan2(zmove, r);
4078         } else {
4079             rot = angle;
4080         }
4082         NR::Point rot_center;
4083         if (Inkscape::NodePath::Path::active_node == NULL)
4084             rot_center = box.midpoint();
4085         else
4086             rot_center = Inkscape::NodePath::Path::active_node->pos;
4088         NR::Matrix t =
4089             NR::Matrix (NR::translate(-rot_center)) *
4090             NR::Matrix (NR::rotate(rot)) *
4091             NR::Matrix (NR::translate(rot_center));
4093         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4094             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4095             n->pos *= t;
4096             n->n.pos *= t;
4097             n->p.pos *= t;
4098             sp_node_update_handles(n, false);
4099         }
4100     }
4102     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4105 /**
4106  * Scale one node.
4107  */
4108 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4110     bool both = false;
4111     Inkscape::NodePath::NodeSide *me, *other;
4113     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4114     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4116     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4117         me = &(n->p);
4118         other = &(n->n);
4119         n->code = NR_CURVETO;
4120     } else if (!n->p.other) {
4121         me = &(n->n);
4122         other = &(n->p);
4123         if (n->n.other)
4124             n->n.other->code = NR_CURVETO;
4125     } else {
4126         if (which > 0) { // right handle
4127             if (xn > xp) {
4128                 me = &(n->n);
4129                 other = &(n->p);
4130                 if (n->n.other)
4131                     n->n.other->code = NR_CURVETO;
4132             } else {
4133                 me = &(n->p);
4134                 other = &(n->n);
4135                 n->code = NR_CURVETO;
4136             }
4137         } else if (which < 0){ // left handle
4138             if (xn <= xp) {
4139                 me = &(n->n);
4140                 other = &(n->p);
4141                 if (n->n.other)
4142                     n->n.other->code = NR_CURVETO;
4143             } else {
4144                 me = &(n->p);
4145                 other = &(n->n);
4146                 n->code = NR_CURVETO;
4147             }
4148         } else { // both handles
4149             me = &(n->n);
4150             other = &(n->p);
4151             both = true;
4152             n->code = NR_CURVETO;
4153             if (n->n.other)
4154                 n->n.other->code = NR_CURVETO;
4155         }
4156     }
4158     Radial rme(me->pos - n->pos);
4159     Radial rother(other->pos - n->pos);
4161     rme.r += grow;
4162     if (rme.r < 0) rme.r = 0;
4163     if (rme.a == HUGE_VAL) {
4164         if (me->other) { // if direction is unknown, initialize it towards the next node
4165             Radial rme_next(me->other->pos - n->pos);
4166             rme.a = rme_next.a;
4167         } else { // if there's no next, initialize to 0
4168             rme.a = 0;
4169         }
4170     }
4171     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4172         rother.r += grow;
4173         if (rother.r < 0) rother.r = 0;
4174         if (rother.a == HUGE_VAL) {
4175             rother.a = rme.a + M_PI;
4176         }
4177     }
4179     me->pos = n->pos + NR::Point(rme);
4181     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4182         other->pos = n->pos + NR::Point(rother);
4183     }
4185     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4186     // so here we just move all the knots without emitting move signals, for speed
4187     sp_node_update_handles(n, false);
4190 /**
4191  * Scale selected nodes.
4192  */
4193 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4195     if (!nodepath || !nodepath->selected) return;
4197     if (g_list_length(nodepath->selected) == 1) {
4198         // scale handles of the single selected node
4199         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4200         node_scale_one (n, grow, which);
4201     } else {
4202         // scale nodes as an "object":
4204         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4205         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4206         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4207             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4208             box.expandTo (n->pos); // contain all selected nodes
4209         }
4211         double scale = (box.maxExtent() + grow)/box.maxExtent();
4213         NR::Point scale_center;
4214         if (Inkscape::NodePath::Path::active_node == NULL)
4215             scale_center = box.midpoint();
4216         else
4217             scale_center = Inkscape::NodePath::Path::active_node->pos;
4219         NR::Matrix t =
4220             NR::Matrix (NR::translate(-scale_center)) *
4221             NR::Matrix (NR::scale(scale, scale)) *
4222             NR::Matrix (NR::translate(scale_center));
4224         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4225             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4226             n->pos *= t;
4227             n->n.pos *= t;
4228             n->p.pos *= t;
4229             sp_node_update_handles(n, false);
4230         }
4231     }
4233     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4236 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4238     if (!nodepath) return;
4239     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4242 /**
4243  * Flip selected nodes horizontally/vertically.
4244  */
4245 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4247     if (!nodepath || !nodepath->selected) return;
4249     if (g_list_length(nodepath->selected) == 1 && !center) {
4250         // flip handles of the single selected node
4251         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4252         double temp = n->p.pos[axis];
4253         n->p.pos[axis] = n->n.pos[axis];
4254         n->n.pos[axis] = temp;
4255         sp_node_update_handles(n, false);
4256     } else {
4257         // scale nodes as an "object":
4259         NR::Rect box = sp_node_selected_bbox (nodepath);
4260         if (!center) {
4261             center = box.midpoint();
4262         }
4263         NR::Matrix t =
4264             NR::Matrix (NR::translate(- *center)) *
4265             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4266             NR::Matrix (NR::translate(*center));
4268         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4269             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4270             n->pos *= t;
4271             n->n.pos *= t;
4272             n->p.pos *= t;
4273             sp_node_update_handles(n, false);
4274         }
4275     }
4277     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4280 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4282     g_assert (nodepath->selected);
4284     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4285     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4286     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4287         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4288         box.expandTo (n->pos); // contain all selected nodes
4289     }
4290     return box;
4293 //-----------------------------------------------
4294 /**
4295  * Return new subpath under given nodepath.
4296  */
4297 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4299     g_assert(nodepath);
4300     g_assert(nodepath->desktop);
4302    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4304     s->nodepath = nodepath;
4305     s->closed = FALSE;
4306     s->nodes = NULL;
4307     s->first = NULL;
4308     s->last = NULL;
4310     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4311     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4312     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4314     return s;
4317 /**
4318  * Destroy nodes in subpath, then subpath itself.
4319  */
4320 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4322     g_assert(subpath);
4323     g_assert(subpath->nodepath);
4324     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4326     while (subpath->nodes) {
4327         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4328     }
4330     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4332     g_free(subpath);
4335 /**
4336  * Link head to tail in subpath.
4337  */
4338 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4340     g_assert(!sp->closed);
4341     g_assert(sp->last != sp->first);
4342     g_assert(sp->first->code == NR_MOVETO);
4344     sp->closed = TRUE;
4346     //Link the head to the tail
4347     sp->first->p.other = sp->last;
4348     sp->last->n.other  = sp->first;
4349     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4350     sp->first          = sp->last;
4352     //Remove the extra end node
4353     sp_nodepath_node_destroy(sp->last->n.other);
4356 /**
4357  * Open closed (loopy) subpath at node.
4358  */
4359 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4361     g_assert(sp->closed);
4362     g_assert(n->subpath == sp);
4363     g_assert(sp->first == sp->last);
4365     /* We create new startpoint, current node will become last one */
4367    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4368                                                 &n->pos, &n->pos, &n->n.pos);
4371     sp->closed        = FALSE;
4373     //Unlink to make a head and tail
4374     sp->first         = new_path;
4375     sp->last          = n;
4376     n->n.other        = NULL;
4377     new_path->p.other = NULL;
4380 /**
4381  * Return new node in subpath with given properties.
4382  * \param pos Position of node.
4383  * \param ppos Handle position in previous direction
4384  * \param npos Handle position in previous direction
4385  */
4386 Inkscape::NodePath::Node *
4387 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)
4389     g_assert(sp);
4390     g_assert(sp->nodepath);
4391     g_assert(sp->nodepath->desktop);
4393     if (nodechunk == NULL)
4394         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4396     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4398     n->subpath  = sp;
4400     if (type != Inkscape::NodePath::NODE_NONE) {
4401         // use the type from sodipodi:nodetypes
4402         n->type = type;
4403     } else {
4404         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4405             // points are (almost) collinear
4406             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4407                 // endnode, or a node with a retracted handle
4408                 n->type = Inkscape::NodePath::NODE_CUSP;
4409             } else {
4410                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4411             }
4412         } else {
4413             n->type = Inkscape::NodePath::NODE_CUSP;
4414         }
4415     }
4417     n->code     = code;
4418     n->selected = FALSE;
4419     n->pos      = *pos;
4420     n->p.pos    = *ppos;
4421     n->n.pos    = *npos;
4423     n->dragging_out = NULL;
4425     Inkscape::NodePath::Node *prev;
4426     if (next) {
4427         //g_assert(g_list_find(sp->nodes, next));
4428         prev = next->p.other;
4429     } else {
4430         prev = sp->last;
4431     }
4433     if (prev)
4434         prev->n.other = n;
4435     else
4436         sp->first = n;
4438     if (next)
4439         next->p.other = n;
4440     else
4441         sp->last = n;
4443     n->p.other = prev;
4444     n->n.other = next;
4446     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"));
4447     sp_knot_set_position(n->knot, pos, 0);
4449     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4450     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4451     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4452     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4453     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4454     sp_knot_update_ctrl(n->knot);
4456     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4457     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4458     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4459     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4460     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4461     sp_knot_show(n->knot);
4463     // We only create handle knots and lines on demand
4464     n->p.knot = NULL;
4465     n->p.line = NULL;
4466     n->n.knot = NULL;
4467     n->n.line = NULL;
4469     sp->nodes = g_list_prepend(sp->nodes, n);
4471     return n;
4474 /**
4475  * Destroy node and its knots, link neighbors in subpath.
4476  */
4477 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4479     g_assert(node);
4480     g_assert(node->subpath);
4481     g_assert(SP_IS_KNOT(node->knot));
4483    Inkscape::NodePath::SubPath *sp = node->subpath;
4485     if (node->selected) { // first, deselect
4486         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4487         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4488     }
4490     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4492     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4493     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4494     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4495     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4496     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4497     g_object_unref(G_OBJECT(node->knot));
4499     if (node->p.knot) {
4500         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4501         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4502         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4503         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4504         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4505         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4506         g_object_unref(G_OBJECT(node->p.knot));
4507         node->p.knot = NULL;
4508     }
4510     if (node->n.knot) {
4511         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4512         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4513         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4514         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4515         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4516         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4517         g_object_unref(G_OBJECT(node->n.knot));
4518         node->n.knot = NULL;
4519     }
4521     if (node->p.line)
4522         gtk_object_destroy(GTK_OBJECT(node->p.line));
4523     if (node->n.line)
4524         gtk_object_destroy(GTK_OBJECT(node->n.line));
4526     if (sp->nodes) { // there are others nodes on the subpath
4527         if (sp->closed) {
4528             if (sp->first == node) {
4529                 g_assert(sp->last == node);
4530                 sp->first = node->n.other;
4531                 sp->last = sp->first;
4532             }
4533             node->p.other->n.other = node->n.other;
4534             node->n.other->p.other = node->p.other;
4535         } else {
4536             if (sp->first == node) {
4537                 sp->first = node->n.other;
4538                 sp->first->code = NR_MOVETO;
4539             }
4540             if (sp->last == node) sp->last = node->p.other;
4541             if (node->p.other) node->p.other->n.other = node->n.other;
4542             if (node->n.other) node->n.other->p.other = node->p.other;
4543         }
4544     } else { // this was the last node on subpath
4545         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4546     }
4548     g_mem_chunk_free(nodechunk, node);
4551 /**
4552  * Returns one of the node's two sides.
4553  * \param which Indicates which side.
4554  * \return Pointer to previous node side if which==-1, next if which==1.
4555  */
4556 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4558     g_assert(node);
4560     switch (which) {
4561         case -1:
4562             return &node->p;
4563         case 1:
4564             return &node->n;
4565         default:
4566             break;
4567     }
4569     g_assert_not_reached();
4571     return NULL;
4574 /**
4575  * Return the other side of the node, given one of its sides.
4576  */
4577 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4579     g_assert(node);
4581     if (me == &node->p) return &node->n;
4582     if (me == &node->n) return &node->p;
4584     g_assert_not_reached();
4586     return NULL;
4589 /**
4590  * Return NRPathcode on the given side of the node.
4591  */
4592 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4594     g_assert(node);
4596     if (me == &node->p) {
4597         if (node->p.other) return (NRPathcode)node->code;
4598         return NR_MOVETO;
4599     }
4601     if (me == &node->n) {
4602         if (node->n.other) return (NRPathcode)node->n.other->code;
4603         return NR_MOVETO;
4604     }
4606     g_assert_not_reached();
4608     return NR_END;
4611 /**
4612  * Return node with the given index
4613  */
4614 Inkscape::NodePath::Node *
4615 sp_nodepath_get_node_by_index(int index)
4617     Inkscape::NodePath::Node *e = NULL;
4619     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4620     if (!nodepath) {
4621         return e;
4622     }
4624     //find segment
4625     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4627         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4628         int n = g_list_length(sp->nodes);
4629         if (sp->closed) {
4630             n++;
4631         }
4633         //if the piece belongs to this subpath grab it
4634         //otherwise move onto the next subpath
4635         if (index < n) {
4636             e = sp->first;
4637             for (int i = 0; i < index; ++i) {
4638                 e = e->n.other;
4639             }
4640             break;
4641         } else {
4642             if (sp->closed) {
4643                 index -= (n+1);
4644             } else {
4645                 index -= n;
4646             }
4647         }
4648     }
4650     return e;
4653 /**
4654  * Returns plain text meaning of node type.
4655  */
4656 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4658     unsigned retracted = 0;
4659     bool endnode = false;
4661     for (int which = -1; which <= 1; which += 2) {
4662         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4663         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4664             retracted ++;
4665         if (!side->other)
4666             endnode = true;
4667     }
4669     if (retracted == 0) {
4670         if (endnode) {
4671                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4672                 return _("end node");
4673         } else {
4674             switch (node->type) {
4675                 case Inkscape::NodePath::NODE_CUSP:
4676                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4677                     return _("cusp");
4678                 case Inkscape::NodePath::NODE_SMOOTH:
4679                     // TRANSLATORS: "smooth" is an adjective here
4680                     return _("smooth");
4681                 case Inkscape::NodePath::NODE_SYMM:
4682                     return _("symmetric");
4683             }
4684         }
4685     } else if (retracted == 1) {
4686         if (endnode) {
4687             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4688             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4689         } else {
4690             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4691         }
4692     } else {
4693         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4694     }
4696     return NULL;
4699 /**
4700  * Handles content of statusbar as long as node tool is active.
4701  */
4702 void
4703 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4705     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");
4706     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4708     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4709     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4710     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4711     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4713     SPDesktop *desktop = NULL;
4714     if (nodepath) {
4715         desktop = nodepath->desktop;
4716     } else {
4717         desktop = SP_ACTIVE_DESKTOP;
4718     }
4720     SPEventContext *ec = desktop->event_context;
4721     if (!ec) return;
4722     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4723     if (!mc) return;
4725     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4727     if (selected_nodes == 0) {
4728         Inkscape::Selection *sel = desktop->selection;
4729         if (!sel || sel->isEmpty()) {
4730             mc->setF(Inkscape::NORMAL_MESSAGE,
4731                      _("Select a single object to edit its nodes or handles."));
4732         } else {
4733             if (nodepath) {
4734             mc->setF(Inkscape::NORMAL_MESSAGE,
4735                      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.",
4736                               "<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.",
4737                               total_nodes),
4738                      total_nodes);
4739             } else {
4740                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4741                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4742                 } else {
4743                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4744                 }
4745             }
4746         }
4747     } else if (nodepath && selected_nodes == 1) {
4748         mc->setF(Inkscape::NORMAL_MESSAGE,
4749                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4750                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4751                           total_nodes),
4752                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4753     } else {
4754         if (selected_subpaths > 1) {
4755             mc->setF(Inkscape::NORMAL_MESSAGE,
4756                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4757                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4758                               total_nodes),
4759                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4760         } else {
4761             mc->setF(Inkscape::NORMAL_MESSAGE,
4762                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4763                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4764                               total_nodes),
4765                      selected_nodes, total_nodes, when_selected);
4766         }
4767     }
4770 /*
4771  * returns a *copy* of the curve of that object.
4772  */
4773 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4774     if (!object)
4775         return NULL;
4777     SPCurve *curve = NULL;
4778     if (SP_IS_PATH(object)) {
4779         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4780         curve = curve_new->copy();
4781     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4782         const gchar *svgd = object->repr->attribute(key);
4783         if (svgd) {
4784             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4785             SPCurve *curve_new = new SPCurve(pv);
4786             if (curve_new) {
4787                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4788             }
4789         }
4790     }
4792     return curve;
4795 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4796     if (!np || !np->object || !curve)
4797         return;
4799     if (SP_IS_PATH(np->object)) {
4800         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4801             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4802         } else {
4803             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4804         }
4805     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4806         // FIXME: this writing to string and then reading from string is bound to be slow.
4807         // create a method to convert from curve directly to 2geom...
4808         gchar *svgpath = sp_svg_write_path( np->curve->get_pathvector() );
4809         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4810         g_free(svgpath);
4812         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4813     }
4816 SPCanvasItem *
4817 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4818     SPCurve *flash_curve = curve->copy();
4819     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4820     flash_curve->transform(i2d);
4821     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4822     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4823     // unless we also flash the nodes...
4824     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4825     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4826     sp_canvas_item_show(canvasitem);
4827     flash_curve->unref();
4828     return canvasitem;
4831 SPCanvasItem *
4832 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4833     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4834                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4837 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4838     np->show_helperpath = show;
4840     if (show) {
4841         SPCurve *helper_curve = np->curve->copy();
4842         helper_curve->transform(np->i2d );
4843         if (!np->helper_path) {
4844             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4845             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);
4846             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4847             sp_canvas_item_move_to_z(np->helper_path, 0);
4848             sp_canvas_item_show(np->helper_path);
4849         } else {
4850             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4851         }
4852         helper_curve->unref();
4853     } else {
4854         if (np->helper_path) {
4855             GtkObject *temp = np->helper_path;
4856             np->helper_path = NULL;
4857             gtk_object_destroy(temp);
4858         }
4859     }
4862 /* sp_nodepath_make_straight_path:
4863  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4864  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4865  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4866  */
4867 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4868     np->straight_path = true;
4869     np->show_handles = false;
4870     g_message("add code to make the path straight.");
4871     // do sp_nodepath_convert_node_type on all nodes?
4872     // coding tip: search for this text : "Make selected segments lines"
4876 /*
4877   Local Variables:
4878   mode:c++
4879   c-file-style:"stroustrup"
4880   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4881   indent-tabs-mode:nil
4882   fill-column:99
4883   End:
4884 */
4885 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :