Code

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