Code

2geom update (rev. 1578); fixes node editing of some degenerate paths
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include <2geom/pathvector.h>
25 #include <2geom/sbasis-to-bezier.h>
26 #include <2geom/bezier-curve.h>
27 #include <2geom/hvlinesegment.h>
28 #include "helper/units.h"
29 #include "helper/geom.h"
30 #include "knot.h"
31 #include "inkscape.h"
32 #include "document.h"
33 #include "sp-namedview.h"
34 #include "desktop.h"
35 #include "desktop-handles.h"
36 #include "snap.h"
37 #include "message-stack.h"
38 #include "message-context.h"
39 #include "node-context.h"
40 #include "lpe-tool-context.h"
41 #include "shape-editor.h"
42 #include "selection-chemistry.h"
43 #include "selection.h"
44 #include "xml/repr.h"
45 #include "prefs-utils.h"
46 #include "sp-metrics.h"
47 #include "sp-path.h"
48 #include "libnr/nr-matrix-ops.h"
49 #include "svg/svg.h"
50 #include "verbs.h"
51 #include "display/bezier-utils.h"
52 #include <vector>
53 #include <algorithm>
54 #include <cstring>
55 #include <cmath>
56 #include <string>
57 #include "live_effects/lpeobject.h"
58 #include "live_effects/lpeobject-reference.h"
59 #include "live_effects/effect.h"
60 #include "live_effects/parameter/parameter.h"
61 #include "live_effects/parameter/path.h"
62 #include "util/mathfns.h"
63 #include "display/snap-indicator.h"
64 #include "snapped-point.h"
66 class NR::Matrix;
68 /// \todo
69 /// evil evil evil. FIXME: conflict of two different Path classes!
70 /// There is a conflict in the namespace between two classes named Path.
71 /// #include "sp-flowtext.h"
72 /// #include "sp-flowregion.h"
74 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
75 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
76 GType sp_flowregion_get_type (void);
77 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
78 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
79 GType sp_flowtext_get_type (void);
80 // end evil workaround
82 #include "helper/stlport.h"
85 /// \todo fixme: Implement these via preferences */
87 #define NODE_FILL          0xbfbfbf00
88 #define NODE_STROKE        0x000000ff
89 #define NODE_FILL_HI       0xff000000
90 #define NODE_STROKE_HI     0x000000ff
91 #define NODE_FILL_SEL      0x0000ffff
92 #define NODE_STROKE_SEL    0x000000ff
93 #define NODE_FILL_SEL_HI   0xff000000
94 #define NODE_STROKE_SEL_HI 0x000000ff
95 #define KNOT_FILL          0xffffffff
96 #define KNOT_STROKE        0x000000ff
97 #define KNOT_FILL_HI       0xff000000
98 #define KNOT_STROKE_HI     0x000000ff
100 static GMemChunk *nodechunk = NULL;
102 /* Creation from object */
104 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
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 SPCanvasItem *
162 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
163     SPCurve *helper_curve = curve->copy();
164     helper_curve->transform(np->i2d);
165     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
166     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
167     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
168     sp_canvas_item_move_to_z(helper_path, 0);
169     if (show) {
170         sp_canvas_item_show(helper_path);
171     }
172     helper_curve->unref();
173     return helper_path;
176 static SPCanvasItem *
177 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
178     SPCurve *helper_curve = new SPCurve(pathv);
179     return sp_nodepath_make_helper_item(np, helper_curve, show);
182 static void
183 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
184     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
185     if (!SP_IS_LPE_ITEM(np->item)) {
186         g_print ("Only LPEItems can have helperpaths!\n");
187         return;
188     }
190     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
191     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
192     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
193         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
194         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
195         // create new canvas items from the effect's helper paths
196         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
197         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
198             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
199         }
200     }
203 void
204 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
205     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
206     if (!SP_IS_LPE_ITEM(np->item)) {
207         g_print ("Only LPEItems can have helperpaths!\n");
208         return;
209     }
211     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
212     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
213     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
214         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
215         /* update canvas items from the effect's helper paths; note that this code relies on the
216          * fact that getHelperPaths() will always return the same number of helperpaths in the same
217          * order as during their creation in sp_nodepath_create_helperpaths
218          */
219         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
220         for (unsigned int j = 0; j < hpaths.size(); ++j) {
221             SPCurve *curve = new SPCurve(hpaths[j]);
222             curve->transform(np->i2d);
223             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
224             curve = curve->unref();
225         }
226     }
229 static void
230 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
231     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
232         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
233             GtkObject *temp = *j;
234             *j = NULL;
235             gtk_object_destroy(temp);
236         }
237     }
241 /**
242  * \brief Creates new nodepath from item
243  */
244 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
246     Inkscape::XML::Node *repr = object->repr;
248     /** \todo
249      * FIXME: remove this. We don't want to edit paths inside flowtext.
250      * Instead we will build our flowtext with cloned paths, so that the
251      * real paths are outside the flowtext and thus editable as usual.
252      */
253     if (SP_IS_FLOWTEXT(object)) {
254         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
255             if SP_IS_FLOWREGION(child) {
256                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
257                 if (grandchild && SP_IS_PATH(grandchild)) {
258                     object = SP_ITEM(grandchild);
259                     break;
260                 }
261             }
262         }
263     }
265     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
267     if (curve == NULL) {
268         return NULL;
269     }
271     if (curve->get_segment_count() < 1) {
272         curve->unref();
273         return NULL; // prevent crash for one-node paths
274     }
276     //Create new nodepath
277     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
278     if (!np) {
279         curve->unref();
280         return NULL;
281     }
283     // Set defaults
284     np->desktop     = desktop;
285     np->object      = object;
286     np->subpaths    = NULL;
287     np->selected    = NULL;
288     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
289     np->local_change = 0;
290     np->show_handles = show_handles;
291     np->helper_path = NULL;
292     np->helper_path_vec = new HelperPathList;
293     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
294     np->helperpath_width = 1.0;
295     np->curve = curve->copy();
296     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
297     if (SP_IS_LPE_ITEM(object)) {
298         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
299         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
300             np->show_helperpath = true;
301         }            
302     }
303     np->straight_path = false;
304     if (IS_LIVEPATHEFFECT(object) && item) {
305         np->item = item;
306     } else {
307         np->item = SP_ITEM(object);
308     }
310     // we need to update item's transform from the repr here,
311     // because they may be out of sync when we respond
312     // to a change in repr by regenerating nodepath     --bb
313     sp_object_read_attr(SP_OBJECT(np->item), "transform");
315     np->i2d  = sp_item_i2d_affine(np->item);
316     np->d2i  = np->i2d.inverse();
318     np->repr = repr;
319     if (repr_key_in) { // apparantly the object is an LPEObject
320         np->repr_key = g_strdup(repr_key_in);
321         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
322         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
323         if (lpeparam) {
324             lpeparam->param_setup_nodepath(np);
325         }
326     } else {
327         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
328         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
329             np->repr_key = g_strdup("inkscape:original-d");
331             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
332             if (lpe) {
333                 lpe->setup_nodepath(np);
334             }
335         } else {
336             np->repr_key = g_strdup("d");
337         }
338     }
340     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
341      * So for example a closed rectangle has a nodetypestring of length 5.
342      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
343     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
344     np->curve->set_pathvector(pathv_sanitized);
345     guint length = np->curve->get_segment_count();
346     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
347         length += pit->empty() ? 0 : 1;
348     }
350     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
351     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
353     // create the subpath(s) from the bpath
354     subpaths_from_pathvector(np, pathv_sanitized, typestr);
356     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
357     np->subpaths = g_list_reverse(np->subpaths);
359     delete[] typestr;
360     curve->unref();
362     // Draw helper curve
363     if (np->show_helperpath) {
364         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
365     }
367     sp_nodepath_create_helperpaths(np);
369     return np;
372 /**
373  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
374  */
375 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
377     if (!np) {  //soft fail, like delete
378         return;
379     }
381     while (np->subpaths) {
382         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
383     }
385     //Inform the ShapeEditor that made me, if any, that I am gone.
386     if (np->shape_editor)
387         np->shape_editor->nodepath_destroyed();
389     g_assert(!np->selected);
391     if (np->helper_path) {
392         GtkObject *temp = np->helper_path;
393         np->helper_path = NULL;
394         gtk_object_destroy(temp);
395     }
396     if (np->curve) {
397         np->curve->unref();
398         np->curve = NULL;
399     }
401     if (np->repr_key) {
402         g_free(np->repr_key);
403         np->repr_key = NULL;
404     }
405     if (np->repr_nodetypes_key) {
406         g_free(np->repr_nodetypes_key);
407         np->repr_nodetypes_key = NULL;
408     }
410     sp_nodepath_destroy_helperpaths(np);
411     delete np->helper_path_vec;
412     np->helper_path_vec = NULL;
414     np->desktop = NULL;
416     g_free(np);
419 /**
420  *  Return the node count of a given NodeSubPath.
421  */
422 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
424     int nodeCount = 0;
426     if (subpath) {
427         nodeCount = g_list_length(subpath->nodes);
428     }
430     return nodeCount;
433 /**
434  *  Return the node count of a given NodePath.
435  */
436 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
438     gint nodeCount = 0;
439     if (np) {
440         for (GList *item = np->subpaths ; item ; item=item->next) {
441             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
442             nodeCount += g_list_length(subpath->nodes);
443         }
444     }
445     return nodeCount;
448 /**
449  *  Return the subpath count of a given NodePath.
450  */
451 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
453     gint nodeCount = 0;
454     if (np) {
455         nodeCount = g_list_length(np->subpaths);
456     }
457     return nodeCount;
460 /**
461  *  Return the selected node count of a given NodePath.
462  */
463 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
465     gint nodeCount = 0;
466     if (np) {
467         nodeCount = g_list_length(np->selected);
468     }
469     return nodeCount;
472 /**
473  *  Return the number of subpaths where nodes are selected in a given NodePath.
474  */
475 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
477     gint nodeCount = 0;
478     if (np && np->selected) {
479         if (!np->selected->next) {
480             nodeCount = 1;
481         } else {
482             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
483                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
484                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
485                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
486                     if (node->selected) {
487                         nodeCount++;
488                         break;
489                     }
490                 }
491             }
492         }
493     }
494     return nodeCount;
497 /**
498  * Clean up a nodepath after editing.
499  *
500  * Currently we are deleting trivial subpaths.
501  */
502 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
504     GList *badSubPaths = NULL;
506     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
507     for (GList *l = nodepath->subpaths; l ; l=l->next) {
508        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
509        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
510             badSubPaths = g_list_append(badSubPaths, sp);
511     }
513     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
514     //also removes the subpath from nodepath->subpaths
515     for (GList *l = badSubPaths; l ; l=l->next) {
516        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
517         sp_nodepath_subpath_destroy(sp);
518     }
520     g_list_free(badSubPaths);
523 /**
524  * Create new nodepaths from pathvector, make it subpaths of np.
525  * \param t The node type array.
526  */
527 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
529     guint i = 0;  // index into node type array
530     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
531         if (pit->empty())
532             continue;  // don't add single knot paths
534         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
536         NR::Point ppos = pit->initialPoint() * (Geom::Matrix)np->i2d;
537         NRPathcode pcode = NR_MOVETO;
539         /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/
540         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
541             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
542                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
543                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
544             {
545                 NR::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
546                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
548                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
549                 pcode = NR_LINETO;
550             }
551             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
552                 std::vector<Geom::Point> points = cubic_bezier->points();
553                 NR::Point pos = points[0] * (Geom::Matrix)np->i2d;
554                 NR::Point npos = points[1] * (Geom::Matrix)np->i2d;
555                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
557                 ppos = points[2] * (Geom::Matrix)np->i2d;
558                 pcode = NR_CURVETO;
559             }
560         }
562         if (pit->closed()) {
563             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
564             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
565              * If the length is zero, don't add it to the nodepath. */
566             Geom::Curve const &closing_seg = pit->back_closed();
567             if ( ! closing_seg.isDegenerate() ) {
568                 NR::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
569                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
570             }
572             sp_nodepath_subpath_close(sp);
573         }
574     }
577 /**
578  * Convert from sodipodi:nodetypes to new style type array.
579  */
580 static
581 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
583     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
585     guint pos = 0;
587     if (types) {
588         for (guint i = 0; types[i] && ( i < length ); i++) {
589             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
590             if (types[i] != '\0') {
591                 switch (types[i]) {
592                     case 's':
593                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
594                         break;
595                     case 'z':
596                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
597                         break;
598                     case 'c':
599                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
600                         break;
601                     default:
602                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
603                         break;
604                 }
605             }
606         }
607     }
609     while (pos < length) {
610         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
611     }
613     return typestr;
616 /**
617  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
618  * updated but repr is not (for speed). Used during curve and node drag.
619  */
620 static void update_object(Inkscape::NodePath::Path *np)
622     g_assert(np);
624     np->curve->unref();
625     np->curve = create_curve(np);
627     sp_nodepath_set_curve(np, np->curve);
629     if (np->show_helperpath) {
630         SPCurve * helper_curve = np->curve->copy();
631         helper_curve->transform(np->i2d);
632         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
633         helper_curve->unref();
634     }
636     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
637     //sp_nodepath_update_helperpaths(np);
639     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
640     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
641     np->shape_editor->update_knotholder();
644 /**
645  * Update XML path node with data from path object.
646  */
647 static void update_repr_internal(Inkscape::NodePath::Path *np)
649     g_assert(np);
651     Inkscape::XML::Node *repr = np->object->repr;
653     np->curve->unref();
654     np->curve = create_curve(np);
656     gchar *typestr = create_typestr(np);
657     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
659     // determine if path has an effect applied and write to correct "d" attribute.
660     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
661         np->local_change++;
662         repr->setAttribute(np->repr_key, svgpath);
663     }
665     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
666         np->local_change++;
667         repr->setAttribute(np->repr_nodetypes_key, typestr);
668     }
670     g_free(svgpath);
671     g_free(typestr);
673     if (np->show_helperpath) {
674         SPCurve * helper_curve = np->curve->copy();
675         helper_curve->transform(np->i2d);
676         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
677         helper_curve->unref();
678     }
680     // TODO: do we need this call here? after all, update_object() should have been called just before
681     //sp_nodepath_update_helperpaths(np);
684 /**
685  * Update XML path node with data from path object, commit changes forever.
686  */
687 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
689     //fixme: np can be NULL, so check before proceeding
690     g_return_if_fail(np != NULL);
692     update_repr_internal(np);
693     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
695     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
696                      annotation);
699 /**
700  * Update XML path node with data from path object, commit changes with undo.
701  */
702 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
704     update_repr_internal(np);
705     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
706                            annotation);
709 /**
710  * Make duplicate of path, replace corresponding XML node in tree, commit.
711  */
712 static void stamp_repr(Inkscape::NodePath::Path *np)
714     g_assert(np);
716     Inkscape::XML::Node *old_repr = np->object->repr;
717     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
719     // remember the position of the item
720     gint pos = old_repr->position();
721     // remember parent
722     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
724     SPCurve *curve = create_curve(np);
725     gchar *typestr = create_typestr(np);
727     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
729     new_repr->setAttribute(np->repr_key, svgpath);
730     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
732     // add the new repr to the parent
733     parent->appendChild(new_repr);
734     // move to the saved position
735     new_repr->setPosition(pos > 0 ? pos : 0);
737     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
738                      _("Stamp"));
740     Inkscape::GC::release(new_repr);
741     g_free(svgpath);
742     g_free(typestr);
743     curve->unref();
746 /**
747  * Create curve from path.
748  */
749 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
751     SPCurve *curve = new SPCurve();
753     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
754        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
755         curve->moveto(sp->first->pos * np->d2i);
756        Inkscape::NodePath::Node *n = sp->first->n.other;
757         while (n) {
758             NR::Point const end_pt = n->pos * np->d2i;
759             switch (n->code) {
760                 case NR_LINETO:
761                     curve->lineto(end_pt);
762                     break;
763                 case NR_CURVETO:
764                     curve->curveto(n->p.other->n.pos * np->d2i,
765                                      n->p.pos * np->d2i,
766                                      end_pt);
767                     break;
768                 default:
769                     g_assert_not_reached();
770                     break;
771             }
772             if (n != sp->last) {
773                 n = n->n.other;
774             } else {
775                 n = NULL;
776             }
777         }
778         if (sp->closed) {
779             curve->closepath();
780         }
781     }
783     return curve;
786 /**
787  * Convert path type string to sodipodi:nodetypes style.
788  */
789 static gchar *create_typestr(Inkscape::NodePath::Path *np)
791     gchar *typestr = g_new(gchar, 32);
792     gint len = 32;
793     gint pos = 0;
795     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
796        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
798         if (pos >= len) {
799             typestr = g_renew(gchar, typestr, len + 32);
800             len += 32;
801         }
803         typestr[pos++] = 'c';
805        Inkscape::NodePath::Node *n;
806         n = sp->first->n.other;
807         while (n) {
808             gchar code;
810             switch (n->type) {
811                 case Inkscape::NodePath::NODE_CUSP:
812                     code = 'c';
813                     break;
814                 case Inkscape::NodePath::NODE_SMOOTH:
815                     code = 's';
816                     break;
817                 case Inkscape::NodePath::NODE_SYMM:
818                     code = 'z';
819                     break;
820                 default:
821                     g_assert_not_reached();
822                     code = '\0';
823                     break;
824             }
826             if (pos >= len) {
827                 typestr = g_renew(gchar, typestr, len + 32);
828                 len += 32;
829             }
831             typestr[pos++] = code;
833             if (n != sp->last) {
834                 n = n->n.other;
835             } else {
836                 n = NULL;
837             }
838         }
839     }
841     if (pos >= len) {
842         typestr = g_renew(gchar, typestr, len + 1);
843         len += 1;
844     }
846     typestr[pos++] = '\0';
848     return typestr;
851 // Returns different message contexts depending on the current context. This function should only
852 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
853 // other cases.
854 static Inkscape::MessageContext *
855 get_message_context(SPEventContext *ec)
857     Inkscape::MessageContext *mc = 0;
859     if (SP_IS_NODE_CONTEXT(ec)) {
860         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
861     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
862         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
863     } else {
864         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
865     }
867     return mc;
870 /**
871  \brief Fills node and handle positions for three nodes, splitting line
872   marked by end at distance t.
873  */
874 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
876     g_assert(new_path != NULL);
877     g_assert(end      != NULL);
879     g_assert(end->p.other == new_path);
880    Inkscape::NodePath::Node *start = new_path->p.other;
881     g_assert(start);
883     if (end->code == NR_LINETO) {
884         new_path->type =Inkscape::NodePath::NODE_CUSP;
885         new_path->code = NR_LINETO;
886         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
887     } else {
888         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
889         new_path->code = NR_CURVETO;
890         gdouble s      = 1 - t;
891         for (int dim = 0; dim < 2; dim++) {
892             NR::Coord const f000 = start->pos[dim];
893             NR::Coord const f001 = start->n.pos[dim];
894             NR::Coord const f011 = end->p.pos[dim];
895             NR::Coord const f111 = end->pos[dim];
896             NR::Coord const f00t = s * f000 + t * f001;
897             NR::Coord const f01t = s * f001 + t * f011;
898             NR::Coord const f11t = s * f011 + t * f111;
899             NR::Coord const f0tt = s * f00t + t * f01t;
900             NR::Coord const f1tt = s * f01t + t * f11t;
901             NR::Coord const fttt = s * f0tt + t * f1tt;
902             start->n.pos[dim]    = f00t;
903             new_path->p.pos[dim] = f0tt;
904             new_path->pos[dim]   = fttt;
905             new_path->n.pos[dim] = f1tt;
906             end->p.pos[dim]      = f11t;
907         }
908     }
911 /**
912  * Adds new node on direct line between two nodes, activates handles of all
913  * three nodes.
914  */
915 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
917     g_assert(end);
918     g_assert(end->subpath);
919     g_assert(g_list_find(end->subpath->nodes, end));
921    Inkscape::NodePath::Node *start = end->p.other;
922     g_assert( start->n.other == end );
923    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
924                                                end,
925                                                (NRPathcode)end->code == NR_LINETO?
926                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
927                                                (NRPathcode)end->code,
928                                                &start->pos, &start->pos, &start->n.pos);
929     sp_nodepath_line_midpoint(newnode, end, t);
931     sp_node_adjust_handles(start);
932     sp_node_update_handles(start);
933     sp_node_update_handles(newnode);
934     sp_node_adjust_handles(end);
935     sp_node_update_handles(end);
937     return newnode;
940 /**
941 \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
942 */
943 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
945     g_assert(node);
946     g_assert(node->subpath);
947     g_assert(g_list_find(node->subpath->nodes, node));
949     Inkscape::NodePath::Node* result = 0;
950     Inkscape::NodePath::SubPath *sp = node->subpath;
951     Inkscape::NodePath::Path *np    = sp->nodepath;
953     if (sp->closed) {
954         sp_nodepath_subpath_open(sp, node);
955         result = sp->first;
956     } else if ( (node == sp->first) || (node == sp->last ) ){
957         // no break for end nodes
958         result = 0;
959     } else {
960         // create a new subpath
961         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
963         // duplicate the break node as start of the new subpath
964         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
965                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
966                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
968         // attach rest of curve to new node
969         g_assert(node->n.other);
970         newnode->n.other = node->n.other; node->n.other = NULL;
971         newnode->n.other->p.other = newnode;
972         newsubpath->last = sp->last;
973         sp->last = node;
974         node = newnode;
975         while (node->n.other) {
976             node = node->n.other;
977             node->subpath = newsubpath;
978             sp->nodes = g_list_remove(sp->nodes, node);
979             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
980         }
983         result = newnode;
984     }
985     return result;
988 /**
989  * Duplicate node and connect to neighbours.
990  */
991 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
993     g_assert(node);
994     g_assert(node->subpath);
995     g_assert(g_list_find(node->subpath->nodes, node));
997    Inkscape::NodePath::SubPath *sp = node->subpath;
999     NRPathcode code = (NRPathcode) node->code;
1000     if (code == NR_MOVETO) { // if node is the endnode,
1001         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1002     }
1004     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1006     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1007         return node;
1008     } else {
1009         return newnode; // otherwise select the newly created node
1010     }
1013 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1015     node->p.pos = (node->pos + (node->pos - node->n.pos));
1018 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1020     node->n.pos = (node->pos + (node->pos - node->p.pos));
1023 /**
1024  * Change line type at node, with side effects on neighbours.
1025  */
1026 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1028     g_assert(end);
1029     g_assert(end->subpath);
1030     g_assert(end->p.other);
1032     if (end->code != static_cast<guint>(code) ) {
1033         Inkscape::NodePath::Node *start = end->p.other;
1035         end->code = code;
1037         if (code == NR_LINETO) {
1038             if (start->code == NR_LINETO) {
1039                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1040             }
1041             if (end->n.other) {
1042                 if (end->n.other->code == NR_LINETO) {
1043                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1044                 }
1045             }
1046         } else {
1047             NR::Point delta = end->pos - start->pos;
1048             start->n.pos = start->pos + delta / 3;
1049             end->p.pos = end->pos - delta / 3;
1050             sp_node_adjust_handle(start, 1);
1051             sp_node_adjust_handle(end, -1);
1052         }
1054         sp_node_update_handles(start);
1055         sp_node_update_handles(end);
1056     }
1059 /**
1060  * Change node type, and its handles accordingly.
1061  */
1062 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1064     g_assert(node);
1065     g_assert(node->subpath);
1067     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1068         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1069             type =Inkscape::NodePath::NODE_CUSP;
1070         }
1071     }
1073     node->type = type;
1075     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1076         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1077         node->knot->setSize (node->selected? 11 : 9);
1078         sp_knot_update_ctrl(node->knot);
1079     } else {
1080         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1081         node->knot->setSize (node->selected? 9 : 7);
1082         sp_knot_update_ctrl(node->knot);
1083     }
1085     // if one of handles is mouseovered, preserve its position
1086     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1087         sp_node_adjust_handle(node, 1);
1088     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1089         sp_node_adjust_handle(node, -1);
1090     } else {
1091         sp_node_adjust_handles(node);
1092     }
1094     sp_node_update_handles(node);
1096     sp_nodepath_update_statusbar(node->subpath->nodepath);
1098     return node;
1101 bool
1102 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1104 // TODO clean up multiple returns
1105         Inkscape::NodePath::Node *othernode = side->other;
1106         if (!othernode)
1107             return false;
1108         NRPathcode const code = sp_node_path_code_from_side(node, side);
1109         if (code == NR_LINETO)
1110             return true;
1111         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1112         if (&node->p == side) {
1113             other_to_me = &othernode->n;
1114         } else if (&node->n == side) {
1115             other_to_me = &othernode->p;
1116         } 
1117         if (!other_to_me)
1118             return false;
1119         bool is_line = 
1120              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1121               NR::L2(node->pos - side->pos) < 1e-6);
1122         return is_line;
1125 /**
1126  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1127  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1128  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1129  * If already cusp and set to cusp, retracts handles.
1130 */
1131 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1133     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1135 /* 
1136   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1137  
1138         if (two_handles) {
1139             // do nothing, adjust_handles called via set_node_type will line them up
1140         } else if (one_handle) {
1141             if (opposite_to_handle_is_line) {
1142                 if (lined_up) {
1143                     // already half-smooth; pull opposite handle too making it fully smooth
1144                 } else {
1145                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1146                 }
1147             } else {
1148                 // pull opposite handle in line with the existing one
1149             }
1150         } else if (no_handles) {
1151             if (both_segments_are_lines OR both_segments_are_curves) {
1152                 //pull both handles
1153             } else {
1154                 // pull the handle opposite to line segment, making node half-smooth
1155             }
1156         }
1157 */
1158         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1159         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1160         bool p_is_line = sp_node_side_is_line(node, &node->p);
1161         bool n_is_line = sp_node_side_is_line(node, &node->n);
1163         if (p_has_handle && n_has_handle) {
1164             // do nothing, adjust_handles will line them up
1165         } else if (p_has_handle || n_has_handle) {
1166             if (p_has_handle && n_is_line) {
1167                 Radial line (node->n.other->pos - node->pos);
1168                 Radial handle (node->pos - node->p.pos);
1169                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1170                     // already half-smooth; pull opposite handle too making it fully smooth
1171                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1172                 } else {
1173                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1174                 }
1175             } else if (n_has_handle && p_is_line) {
1176                 Radial line (node->p.other->pos - node->pos);
1177                 Radial handle (node->pos - node->n.pos);
1178                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1179                     // already half-smooth; pull opposite handle too making it fully smooth
1180                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1181                 } else {
1182                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1183                 }
1184             } else if (p_has_handle && node->n.other) {
1185                 // pull n handle
1186                 node->n.other->code = NR_CURVETO;
1187                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1188                     NR::L2(node->p.pos - node->pos) :
1189                     NR::L2(node->n.other->pos - node->pos) / 3;
1190                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1191             } else if (n_has_handle && node->p.other) {
1192                 // pull p handle
1193                 node->code = NR_CURVETO;
1194                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1195                     NR::L2(node->n.pos - node->pos) :
1196                     NR::L2(node->p.other->pos - node->pos) / 3;
1197                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1198             }
1199         } else if (!p_has_handle && !n_has_handle) {
1200             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1201                 // no handles, but both segments are either lnes or curves:
1202                 //pull both handles
1204                 // convert both to curves:
1205                 node->code = NR_CURVETO;
1206                 node->n.other->code = NR_CURVETO;
1208                 NR::Point leg_prev = node->pos - node->p.other->pos;
1209                 NR::Point leg_next = node->pos - node->n.other->pos;
1211                 double norm_leg_prev = L2(leg_prev);
1212                 double norm_leg_next = L2(leg_next);
1214                 NR::Point delta;
1215                 if (norm_leg_next > 0.0) {
1216                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1217                     (&delta)->normalize();
1218                 }
1220                 if (type == Inkscape::NodePath::NODE_SYMM) {
1221                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1222                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1223                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1224                 } else {
1225                     // length of handle is proportional to distance to adjacent node
1226                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1227                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1228                 }
1230             } else {
1231                 // pull the handle opposite to line segment, making it half-smooth
1232                 if (p_is_line && node->n.other) {
1233                     if (type != Inkscape::NodePath::NODE_SYMM) {
1234                         // pull n handle
1235                         node->n.other->code = NR_CURVETO;
1236                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1237                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1238                     }
1239                 } else if (n_is_line && node->p.other) {
1240                     if (type != Inkscape::NodePath::NODE_SYMM) {
1241                         // pull p handle
1242                         node->code = NR_CURVETO;
1243                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1244                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1245                     }
1246                 }
1247             }
1248         }
1249     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1250         // cusping a cusp: retract nodes
1251         node->p.pos = node->pos;
1252         node->n.pos = node->pos;
1253     }
1255     sp_nodepath_set_node_type (node, type);
1258 /**
1259  * Move node to point, and adjust its and neighbouring handles.
1260  */
1261 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1263     NR::Point delta = p - node->pos;
1264     node->pos = p;
1266     node->p.pos += delta;
1267     node->n.pos += delta;
1269     Inkscape::NodePath::Node *node_p = NULL;
1270     Inkscape::NodePath::Node *node_n = NULL;
1272     if (node->p.other) {
1273         if (node->code == NR_LINETO) {
1274             sp_node_adjust_handle(node, 1);
1275             sp_node_adjust_handle(node->p.other, -1);
1276             node_p = node->p.other;
1277         }
1278     }
1279     if (node->n.other) {
1280         if (node->n.other->code == NR_LINETO) {
1281             sp_node_adjust_handle(node, -1);
1282             sp_node_adjust_handle(node->n.other, 1);
1283             node_n = node->n.other;
1284         }
1285     }
1287     // this function is only called from batch movers that will update display at the end
1288     // themselves, so here we just move all the knots without emitting move signals, for speed
1289     sp_node_update_handles(node, false);
1290     if (node_n) {
1291         sp_node_update_handles(node_n, false);
1292     }
1293     if (node_p) {
1294         sp_node_update_handles(node_p, false);
1295     }
1298 /**
1299  * Call sp_node_moveto() for node selection and handle possible snapping.
1300  */
1301 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1302                                             bool const snap, bool constrained = false, 
1303                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1305     NR::Coord best = NR_HUGE;
1306     NR::Point delta(dx, dy);
1307     NR::Point best_pt = delta;
1308     Inkscape::SnappedPoint best_abs;
1309     
1310     if (snap) {    
1311         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1312          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1313          * must provide that information. */
1314           
1315         // Build a list of the unselected nodes to which the snapper should snap 
1316         std::vector<Geom::Point> unselected_nodes;
1317         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1318             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1319             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1320                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1321                 if (!node->selected) {
1322                     unselected_nodes.push_back(to_2geom(node->pos));
1323                 }    
1324             }
1325         }        
1326         
1327         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1328         
1329         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1330             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1331             m.setup(nodepath->desktop, false, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1332             Inkscape::SnappedPoint s;
1333             if (constrained) {
1334                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1335                 dedicated_constraint.setPoint(n->pos);
1336                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1337             } else {
1338                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1339             }            
1340             if (s.getSnapped() && (s.getDistance() < best)) {
1341                 best = s.getDistance();
1342                 best_abs = s;
1343                 best_pt = from_2geom(s.getPoint()) - n->pos;
1344             }
1345         }
1346                         
1347         if (best_abs.getSnapped()) {
1348             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1349         } else {
1350             nodepath->desktop->snapindicator->remove_snappoint();    
1351         }
1352     }
1354     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1355         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1356         sp_node_moveto(n, n->pos + best_pt);
1357     }
1359     // do not update repr here so that node dragging is acceptably fast
1360     update_object(nodepath);
1363 /**
1364 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1365 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1366 near x = 0.
1367  */
1368 double
1369 sculpt_profile (double x, double alpha, guint profile)
1371     double result = 1;
1373     if (x >= 1) {
1374         result = 0;
1375     } else if (x <= 0) {
1376         result = 1;
1377     } else {
1378         switch (profile) {
1379             case SCULPT_PROFILE_LINEAR:
1380                 result = 1 - x;
1381                 break;
1382             case SCULPT_PROFILE_BELL:
1383                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1384                 break;
1385             case SCULPT_PROFILE_ELLIPTIC:
1386                 result = sqrt(1 - x*x);
1387                 break;
1388             default:
1389                 g_assert_not_reached();
1390         }
1391     }
1393     return result;
1396 double
1397 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1399     // extremely primitive for now, don't have time to look for the real one
1400     double lower = NR::L2(b - a);
1401     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1402     return (lower + upper)/2;
1405 void
1406 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1408     n->pos = n->origin + delta;
1409     n->n.pos = n->n.origin + delta_n;
1410     n->p.pos = n->p.origin + delta_p;
1411     sp_node_adjust_handles(n);
1412     sp_node_update_handles(n, false);
1415 /**
1416  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1417  * on how far they are from the dragged node n.
1418  */
1419 static void
1420 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1422     g_assert (n);
1423     g_assert (nodepath);
1424     g_assert (n->subpath->nodepath == nodepath);
1426     double pressure = n->knot->pressure;
1427     if (pressure == 0)
1428         pressure = 0.5; // default
1429     pressure = CLAMP (pressure, 0.2, 0.8);
1431     // map pressure to alpha = 1/5 ... 5
1432     double alpha = 1 - 2 * fabs(pressure - 0.5);
1433     if (pressure > 0.5)
1434         alpha = 1/alpha;
1436     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1438     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1439         // Only one subpath has selected nodes:
1440         // use linear mode, where the distance from n to node being dragged is calculated along the path
1442         double n_sel_range = 0, p_sel_range = 0;
1443         guint n_nodes = 0, p_nodes = 0;
1444         guint n_sel_nodes = 0, p_sel_nodes = 0;
1446         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1447         {
1448             double n_range = 0, p_range = 0;
1449             bool n_going = true, p_going = true;
1450             Inkscape::NodePath::Node *n_node = n;
1451             Inkscape::NodePath::Node *p_node = n;
1452             do {
1453                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1454                 if (n_node && n_going)
1455                     n_node = n_node->n.other;
1456                 if (n_node == NULL) {
1457                     n_going = false;
1458                 } else {
1459                     n_nodes ++;
1460                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1461                     if (n_node->selected) {
1462                         n_sel_nodes ++;
1463                         n_sel_range = n_range;
1464                     }
1465                     if (n_node == p_node) {
1466                         n_going = false;
1467                         p_going = false;
1468                     }
1469                 }
1470                 if (p_node && p_going)
1471                     p_node = p_node->p.other;
1472                 if (p_node == NULL) {
1473                     p_going = false;
1474                 } else {
1475                     p_nodes ++;
1476                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1477                     if (p_node->selected) {
1478                         p_sel_nodes ++;
1479                         p_sel_range = p_range;
1480                     }
1481                     if (p_node == n_node) {
1482                         n_going = false;
1483                         p_going = false;
1484                     }
1485                 }
1486             } while (n_going || p_going);
1487         }
1489         // Second pass: actually move nodes in this subpath
1490         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1491         {
1492             double n_range = 0, p_range = 0;
1493             bool n_going = true, p_going = true;
1494             Inkscape::NodePath::Node *n_node = n;
1495             Inkscape::NodePath::Node *p_node = n;
1496             do {
1497                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1498                 if (n_node && n_going)
1499                     n_node = n_node->n.other;
1500                 if (n_node == NULL) {
1501                     n_going = false;
1502                 } else {
1503                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1504                     if (n_node->selected) {
1505                         sp_nodepath_move_node_and_handles (n_node,
1506                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1507                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1508                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1509                     }
1510                     if (n_node == p_node) {
1511                         n_going = false;
1512                         p_going = false;
1513                     }
1514                 }
1515                 if (p_node && p_going)
1516                     p_node = p_node->p.other;
1517                 if (p_node == NULL) {
1518                     p_going = false;
1519                 } else {
1520                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1521                     if (p_node->selected) {
1522                         sp_nodepath_move_node_and_handles (p_node,
1523                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1524                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1525                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1526                     }
1527                     if (p_node == n_node) {
1528                         n_going = false;
1529                         p_going = false;
1530                     }
1531                 }
1532             } while (n_going || p_going);
1533         }
1535     } else {
1536         // Multiple subpaths have selected nodes:
1537         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1538         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1539         // fix the pear-like shape when sculpting e.g. a ring
1541         // First pass: calculate range
1542         gdouble direct_range = 0;
1543         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1544             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1545             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1546                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1547                 if (node->selected) {
1548                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1549                 }
1550             }
1551         }
1553         // Second pass: actually move nodes
1554         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1555             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1556             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1557                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1558                 if (node->selected) {
1559                     if (direct_range > 1e-6) {
1560                         sp_nodepath_move_node_and_handles (node,
1561                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1562                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1563                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1564                     } else {
1565                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1566                     }
1568                 }
1569             }
1570         }
1571     }
1573     // do not update repr here so that node dragging is acceptably fast
1574     update_object(nodepath);
1578 /**
1579  * Move node selection to point, adjust its and neighbouring handles,
1580  * handle possible snapping, and commit the change with possible undo.
1581  */
1582 void
1583 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1585     if (!nodepath) return;
1587     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1589     if (dx == 0) {
1590         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1591     } else if (dy == 0) {
1592         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1593     } else {
1594         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1595     }
1598 /**
1599  * Move node selection off screen and commit the change.
1600  */
1601 void
1602 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1604     // borrowed from sp_selection_move_screen in selection-chemistry.c
1605     // we find out the current zoom factor and divide deltas by it
1607     gdouble zoom = desktop->current_zoom();
1608     gdouble zdx = dx / zoom;
1609     gdouble zdy = dy / zoom;
1611     if (!nodepath) return;
1613     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1615     if (dx == 0) {
1616         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1617     } else if (dy == 0) {
1618         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1619     } else {
1620         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1621     }
1624 /**
1625  * Move selected nodes to the absolute position given
1626  */
1627 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1629     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1630         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1631         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1632         sp_node_moveto(n, npos);
1633     }
1635     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1638 /**
1639  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1640  */
1641 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1643     boost::optional<Geom::Coord> no_coord;
1644     g_return_val_if_fail(nodepath->selected, no_coord);
1646     // determine coordinate of first selected node
1647     GList *nsel = nodepath->selected;
1648     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1649     NR::Coord coord = n->pos[axis];
1650     bool coincide = true;
1652     // compare it to the coordinates of all the other selected nodes
1653     for (GList *l = nsel->next; l != NULL; l = l->next) {
1654         n = (Inkscape::NodePath::Node *) l->data;
1655         if (n->pos[axis] != coord) {
1656             coincide = false;
1657         }
1658     }
1659     if (coincide) {
1660         return coord;
1661     } else {
1662         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1663         // currently we return the coordinate of the bounding box midpoint because I don't know how
1664         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1665         return bbox.midpoint()[axis];
1666     }
1669 /** If they don't yet exist, creates knot and line for the given side of the node */
1670 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1672     if (!side->knot) {
1673         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"));
1675         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1676         side->knot->setSize (7);
1677         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1678         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1679         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1680         sp_knot_update_ctrl(side->knot);
1682         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1683         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1684         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1685         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1686         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1687         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1688     }
1690     if (!side->line) {
1691         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1692                                         SP_TYPE_CTRLLINE, NULL);
1693     }
1696 /**
1697  * Ensure the given handle of the node is visible/invisible, update its screen position
1698  */
1699 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1701     g_assert(node != NULL);
1703    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1704     NRPathcode code = sp_node_path_code_from_side(node, side);
1706     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1708     if (show_handle) {
1709         if (!side->knot) { // No handle knot at all
1710             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1711             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1712             side->knot->pos = side->pos;
1713             if (side->knot->item)
1714                 SP_CTRL(side->knot->item)->moveto(side->pos);
1715             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1716             sp_knot_show(side->knot);
1717         } else {
1718             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1719                 if (fire_move_signals) {
1720                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1721                 } else {
1722                     sp_knot_moveto(side->knot, side->pos);
1723                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1724                 }
1725             }
1726             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1727                 sp_knot_show(side->knot);
1728             }
1729         }
1730         sp_canvas_item_show(side->line);
1731     } else {
1732         if (side->knot) {
1733             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1734                 sp_knot_hide(side->knot);
1735             }
1736         }
1737         if (side->line) {
1738             sp_canvas_item_hide(side->line);
1739         }
1740     }
1743 /**
1744  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1745  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1746  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1747  * updated; otherwise, just move the knots silently (used in batch moves).
1748  */
1749 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1751     g_assert(node != NULL);
1753     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1754         sp_knot_show(node->knot);
1755     }
1757     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1758         if (fire_move_signals)
1759             sp_knot_set_position(node->knot, node->pos, 0);
1760         else
1761             sp_knot_moveto(node->knot, node->pos);
1762     }
1764     gboolean show_handles = node->selected;
1765     if (node->p.other != NULL) {
1766         if (node->p.other->selected) show_handles = TRUE;
1767     }
1768     if (node->n.other != NULL) {
1769         if (node->n.other->selected) show_handles = TRUE;
1770     }
1772     if (node->subpath->nodepath->show_handles == false)
1773         show_handles = FALSE;
1775     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1776     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1779 /**
1780  * Call sp_node_update_handles() for all nodes on subpath.
1781  */
1782 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1784     g_assert(subpath != NULL);
1786     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1787         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1788     }
1791 /**
1792  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1793  */
1794 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1796     g_assert(nodepath != NULL);
1798     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1799         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1800     }
1803 void
1804 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1806     if (nodepath) {
1807         nodepath->show_handles = show;
1808         sp_nodepath_update_handles(nodepath);
1809     }
1812 /**
1813  * Adds all selected nodes in nodepath to list.
1814  */
1815 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1817     StlConv<Node *>::list(l, selected);
1818 /// \todo this adds a copying, rework when the selection becomes a stl list
1821 /**
1822  * Align selected nodes on the specified axis.
1823  */
1824 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1826     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1827         return;
1828     }
1830     if ( !nodepath->selected->next ) { // only one node selected
1831         return;
1832     }
1833    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1834     NR::Point dest(pNode->pos);
1835     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1836         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1837         if (pNode) {
1838             dest[axis] = pNode->pos[axis];
1839             sp_node_moveto(pNode, dest);
1840         }
1841     }
1843     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1846 /// Helper struct.
1847 struct NodeSort
1849    Inkscape::NodePath::Node *_node;
1850     NR::Coord _coord;
1851     /// \todo use vectorof pointers instead of calling copy ctor
1852     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1853         _node(node), _coord(node->pos[axis])
1854     {}
1856 };
1858 static bool operator<(NodeSort const &a, NodeSort const &b)
1860     return (a._coord < b._coord);
1863 /**
1864  * Distribute selected nodes on the specified axis.
1865  */
1866 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1868     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1869         return;
1870     }
1872     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1873         return;
1874     }
1876    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1877     std::vector<NodeSort> sorted;
1878     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1879         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1880         if (pNode) {
1881             NodeSort n(pNode, axis);
1882             sorted.push_back(n);
1883             //dest[axis] = pNode->pos[axis];
1884             //sp_node_moveto(pNode, dest);
1885         }
1886     }
1887     std::sort(sorted.begin(), sorted.end());
1888     unsigned int len = sorted.size();
1889     //overall bboxes span
1890     float dist = (sorted.back()._coord -
1891                   sorted.front()._coord);
1892     //new distance between each bbox
1893     float step = (dist) / (len - 1);
1894     float pos = sorted.front()._coord;
1895     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1896           it < sorted.end();
1897           it ++ )
1898     {
1899         NR::Point dest((*it)._node->pos);
1900         dest[axis] = pos;
1901         sp_node_moveto((*it)._node, dest);
1902         pos += step;
1903     }
1905     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1909 /**
1910  * Call sp_nodepath_line_add_node() for all selected segments.
1911  */
1912 void
1913 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1915     if (!nodepath) {
1916         return;
1917     }
1919     GList *nl = NULL;
1921     int n_added = 0;
1923     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1924        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1925         g_assert(t->selected);
1926         if (t->p.other && t->p.other->selected) {
1927             nl = g_list_prepend(nl, t);
1928         }
1929     }
1931     while (nl) {
1932        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1933        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1934        sp_nodepath_node_select(n, TRUE, FALSE);
1935        n_added ++;
1936        nl = g_list_remove(nl, t);
1937     }
1939     /** \todo fixme: adjust ? */
1940     sp_nodepath_update_handles(nodepath);
1942     if (n_added > 1) {
1943         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1944     } else if (n_added > 0) {
1945         sp_nodepath_update_repr(nodepath, _("Add node"));
1946     }
1948     sp_nodepath_update_statusbar(nodepath);
1951 /**
1952  * Select segment nearest to point
1953  */
1954 void
1955 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1957     if (!nodepath) {
1958         return;
1959     }
1961     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1962     Geom::PathVector const &pathv = curve->get_pathvector();
1963     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
1964     if (!pvpos) {
1965         g_print ("Possible error?\n");
1966         return;
1967     }
1969     // calculate index for nodepath's representation.
1970     unsigned int segment_index = floor(pvpos->t) + 1;
1971     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
1972         segment_index += pathv[i].size() + 1;
1973         if (pathv[i].closed()) {
1974             segment_index += 1;
1975         }
1976     }
1978     curve->unref();
1980     //find segment to segment
1981     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
1983     //fixme: this can return NULL, so check before proceeding.
1984     g_return_if_fail(e != NULL);
1986     gboolean force = FALSE;
1987     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1988         force = TRUE;
1989     }
1990     sp_nodepath_node_select(e, (gboolean) toggle, force);
1991     if (e->p.other)
1992         sp_nodepath_node_select(e->p.other, TRUE, force);
1994     sp_nodepath_update_handles(nodepath);
1996     sp_nodepath_update_statusbar(nodepath);
1999 /**
2000  * Add a node nearest to point
2001  */
2002 void
2003 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
2005     if (!nodepath) {
2006         return;
2007     }
2009     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2010     Geom::PathVector const &pathv = curve->get_pathvector();
2011     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2012     if (!pvpos) {
2013         g_print ("Possible error?\n");
2014         return;
2015     }
2017     // calculate index for nodepath's representation.
2018     double int_part;
2019     double t = std::modf(pvpos->t, &int_part);
2020     unsigned int segment_index = (unsigned int)int_part + 1;
2021     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2022         segment_index += pathv[i].size() + 1;
2023         if (pathv[i].closed()) {
2024             segment_index += 1;
2025         }
2026     }
2028     curve->unref();
2030     //find segment to split
2031     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2033     //don't know why but t seems to flip for lines
2034     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2035         t = 1.0 - t;
2036     }
2038     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2039     sp_nodepath_node_select(n, FALSE, TRUE);
2041     /* fixme: adjust ? */
2042     sp_nodepath_update_handles(nodepath);
2044     sp_nodepath_update_repr(nodepath, _("Add node"));
2046     sp_nodepath_update_statusbar(nodepath);
2049 /*
2050  * Adjusts a segment so that t moves by a certain delta for dragging
2051  * converts lines to curves
2052  *
2053  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2054  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2055  */
2056 void
2057 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, NR::Point delta)
2059     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2061     //fixme: e and e->p can be NULL, so check for those before proceeding
2062     g_return_if_fail(e != NULL);
2063     g_return_if_fail(&e->p != NULL);
2065     /* feel good is an arbitrary parameter that distributes the delta between handles
2066      * if t of the drag point is less than 1/6 distance form the endpoint only
2067      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2068      */
2069     double feel_good;
2070     if (t <= 1.0 / 6.0)
2071         feel_good = 0;
2072     else if (t <= 0.5)
2073         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2074     else if (t <= 5.0 / 6.0)
2075         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2076     else
2077         feel_good = 1;
2079     //if we're dragging a line convert it to a curve
2080     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2081         sp_nodepath_set_line_type(e, NR_CURVETO);
2082     }
2084     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2085     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2086     e->p.other->n.pos += offsetcoord0;
2087     e->p.pos += offsetcoord1;
2089     // adjust handles of adjacent nodes where necessary
2090     sp_node_adjust_handle(e,1);
2091     sp_node_adjust_handle(e->p.other,-1);
2093     sp_nodepath_update_handles(e->subpath->nodepath);
2095     update_object(e->subpath->nodepath);
2097     sp_nodepath_update_statusbar(e->subpath->nodepath);
2101 /**
2102  * Call sp_nodepath_break() for all selected segments.
2103  */
2104 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2106     if (!nodepath) return;
2108     GList *tempin = g_list_copy(nodepath->selected);
2109     GList *temp = NULL;
2110     for (GList *l = tempin; l != NULL; l = l->next) {
2111        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2112        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2113         if (nn == NULL) continue; // no break, no new node
2114         temp = g_list_prepend(temp, nn);
2115     }
2116     g_list_free(tempin);
2118     if (temp) {
2119         sp_nodepath_deselect(nodepath);
2120     }
2121     for (GList *l = temp; l != NULL; l = l->next) {
2122         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2123     }
2125     sp_nodepath_update_handles(nodepath);
2127     sp_nodepath_update_repr(nodepath, _("Break path"));
2130 /**
2131  * Duplicate the selected node(s).
2132  */
2133 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2135     if (!nodepath) {
2136         return;
2137     }
2139     GList *temp = NULL;
2140     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2141        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2142        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2143         if (nn == NULL) continue; // could not duplicate
2144         temp = g_list_prepend(temp, nn);
2145     }
2147     if (temp) {
2148         sp_nodepath_deselect(nodepath);
2149     }
2150     for (GList *l = temp; l != NULL; l = l->next) {
2151         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2152     }
2154     sp_nodepath_update_handles(nodepath);
2156     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2159 /**
2160  *  Internal function to join two nodes by merging them into one.
2161  */
2162 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2164     /* a and b are endpoints */
2166     // if one of the two nodes is mouseovered, fix its position
2167     NR::Point c;
2168     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2169         c = a->pos;
2170     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2171         c = b->pos;
2172     } else {
2173         // otherwise, move joined node to the midpoint
2174         c = (a->pos + b->pos) / 2;
2175     }
2177     if (a->subpath == b->subpath) {
2178        Inkscape::NodePath::SubPath *sp = a->subpath;
2179         sp_nodepath_subpath_close(sp);
2180         sp_node_moveto (sp->first, c);
2182         sp_nodepath_update_handles(sp->nodepath);
2183         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2184         return;
2185     }
2187     /* a and b are separate subpaths */
2188     Inkscape::NodePath::SubPath *sa = a->subpath;
2189     Inkscape::NodePath::SubPath *sb = b->subpath;
2190     NR::Point p;
2191     Inkscape::NodePath::Node *n;
2192     NRPathcode code;
2193     if (a == sa->first) {
2194         // we will now reverse sa, so that a is its last node, not first, and drop that node
2195         p = sa->first->n.pos;
2196         code = (NRPathcode)sa->first->n.other->code;
2197         // create new subpath
2198        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2199        // create a first moveto node on it
2200         n = sa->last;
2201         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2202         n = n->p.other;
2203         if (n == sa->first) n = NULL;
2204         while (n) {
2205             // copy the rest of the nodes from sa to t, going backwards
2206             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2207             n = n->p.other;
2208             if (n == sa->first) n = NULL;
2209         }
2210         // replace sa with t
2211         sp_nodepath_subpath_destroy(sa);
2212         sa = t;
2213     } else if (a == sa->last) {
2214         // a is already last, just drop it
2215         p = sa->last->p.pos;
2216         code = (NRPathcode)sa->last->code;
2217         sp_nodepath_node_destroy(sa->last);
2218     } else {
2219         code = NR_END;
2220         g_assert_not_reached();
2221     }
2223     if (b == sb->first) {
2224         // copy all nodes from b to a, forward 
2225         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2226         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2227             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2228         }
2229     } else if (b == sb->last) {
2230         // copy all nodes from b to a, backward 
2231         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2232         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2233             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2234         }
2235     } else {
2236         g_assert_not_reached();
2237     }
2238     /* and now destroy sb */
2240     sp_nodepath_subpath_destroy(sb);
2242     sp_nodepath_update_handles(sa->nodepath);
2244     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2246     sp_nodepath_update_statusbar(nodepath);
2249 /**
2250  *  Internal function to join two nodes by adding a segment between them.
2251  */
2252 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2254     if (a->subpath == b->subpath) {
2255        Inkscape::NodePath::SubPath *sp = a->subpath;
2257         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2258         sp->closed = TRUE;
2260         sp->first->p.other = sp->last;
2261         sp->last->n.other  = sp->first;
2263         sp_node_handle_mirror_p_to_n(sp->last);
2264         sp_node_handle_mirror_n_to_p(sp->first);
2266         sp->first->code = sp->last->code;
2267         sp->first       = sp->last;
2269         sp_nodepath_update_handles(sp->nodepath);
2271         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2273         return;
2274     }
2276     /* a and b are separate subpaths */
2277     Inkscape::NodePath::SubPath *sa = a->subpath;
2278     Inkscape::NodePath::SubPath *sb = b->subpath;
2280     Inkscape::NodePath::Node *n;
2281     NR::Point p;
2282     NRPathcode code;
2283     if (a == sa->first) {
2284         code = (NRPathcode) sa->first->n.other->code;
2285        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2286         n = sa->last;
2287         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2288         for (n = n->p.other; n != NULL; n = n->p.other) {
2289             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2290         }
2291         sp_nodepath_subpath_destroy(sa);
2292         sa = t;
2293     } else if (a == sa->last) {
2294         code = (NRPathcode)sa->last->code;
2295     } else {
2296         code = NR_END;
2297         g_assert_not_reached();
2298     }
2300     if (b == sb->first) {
2301         n = sb->first;
2302         sp_node_handle_mirror_p_to_n(sa->last);
2303         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2304         sp_node_handle_mirror_n_to_p(sa->last);
2305         for (n = n->n.other; n != NULL; n = n->n.other) {
2306             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2307         }
2308     } else if (b == sb->last) {
2309         n = sb->last;
2310         sp_node_handle_mirror_p_to_n(sa->last);
2311         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2312         sp_node_handle_mirror_n_to_p(sa->last);
2313         for (n = n->p.other; n != NULL; n = n->p.other) {
2314             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2315         }
2316     } else {
2317         g_assert_not_reached();
2318     }
2319     /* and now destroy sb */
2321     sp_nodepath_subpath_destroy(sb);
2323     sp_nodepath_update_handles(sa->nodepath);
2325     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2328 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2330 /**
2331  * Internal function to handle joining two nodes.
2332  */
2333 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2335     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2337     if (g_list_length(nodepath->selected) != 2) {
2338         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2339         return;
2340     }
2342     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2343     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2345     g_assert(a != b);
2346     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2347         // someone tried to join an orphan node (i.e. a single-node subpath).
2348         // this is not worth an error message, just fail silently.
2349         return;
2350     }
2352     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2353         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2354         return;
2355     }
2357     switch(mode) {
2358         case NODE_JOIN_ENDPOINTS:
2359             do_node_selected_join(nodepath, a, b);
2360             break;
2361         case NODE_JOIN_SEGMENT:
2362             do_node_selected_join_segment(nodepath, a, b);
2363             break;
2364     }
2367 /**
2368  *  Join two nodes by merging them into one.
2369  */
2370 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2372     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2375 /**
2376  *  Join two nodes by adding a segment between them.
2377  */
2378 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2380     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2383 /**
2384  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2385  */
2386 void sp_node_delete_preserve(GList *nodes_to_delete)
2388     GSList *nodepaths = NULL;
2390     while (nodes_to_delete) {
2391         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2392         Inkscape::NodePath::SubPath *sp = node->subpath;
2393         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2394         Inkscape::NodePath::Node *sample_cursor = NULL;
2395         Inkscape::NodePath::Node *sample_end = NULL;
2396         Inkscape::NodePath::Node *delete_cursor = node;
2397         bool just_delete = false;
2399         //find the start of this contiguous selection
2400         //move left to the first node that is not selected
2401         //or the start of the non-closed path
2402         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2403             delete_cursor = curr;
2404         }
2406         //just delete at the beginning of an open path
2407         if (!delete_cursor->p.other) {
2408             sample_cursor = delete_cursor;
2409             just_delete = true;
2410         } else {
2411             sample_cursor = delete_cursor->p.other;
2412         }
2414         //calculate points for each segment
2415         int rate = 5;
2416         float period = 1.0 / rate;
2417         std::vector<NR::Point> data;
2418         if (!just_delete) {
2419             data.push_back(sample_cursor->pos);
2420             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2421                 //just delete at the end of an open path
2422                 if (!sp->closed && curr == sp->last) {
2423                     just_delete = true;
2424                     break;
2425                 }
2427                 //sample points on the contiguous selected segment
2428                 NR::Point *bez;
2429                 bez = new NR::Point [4];
2430                 bez[0] = curr->pos;
2431                 bez[1] = curr->n.pos;
2432                 bez[2] = curr->n.other->p.pos;
2433                 bez[3] = curr->n.other->pos;
2434                 for (int i=1; i<rate; i++) {
2435                     gdouble t = i * period;
2436                     NR::Point p = bezier_pt(3, bez, t);
2437                     data.push_back(p);
2438                 }
2439                 data.push_back(curr->n.other->pos);
2441                 sample_end = curr->n.other;
2442                 //break if we've come full circle or hit the end of the selection
2443                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2444                     break;
2445                 }
2446             }
2447         }
2449         if (!just_delete) {
2450             //calculate the best fitting single segment and adjust the endpoints
2451             NR::Point *adata;
2452             adata = new NR::Point [data.size()];
2453             copy(data.begin(), data.end(), adata);
2455             NR::Point *bez;
2456             bez = new NR::Point [4];
2457             //would decreasing error create a better fitting approximation?
2458             gdouble error = 1.0;
2459             gint ret;
2460             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2462             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2463             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2464             //the resulting nodes behave as expected.
2465             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2466                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2467             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2468                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2470             //adjust endpoints
2471             sample_cursor->n.pos = bez[1];
2472             sample_end->p.pos = bez[2];
2473         }
2475         //destroy this contiguous selection
2476         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2477             Inkscape::NodePath::Node *temp = delete_cursor;
2478             if (delete_cursor->n.other == delete_cursor) {
2479                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2480                 delete_cursor = NULL;
2481             } else {
2482                 delete_cursor = delete_cursor->n.other;
2483             }
2484             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2485             sp_nodepath_node_destroy(temp);
2486         }
2488         sp_nodepath_update_handles(nodepath);
2490         if (!g_slist_find(nodepaths, nodepath))
2491             nodepaths = g_slist_prepend (nodepaths, nodepath);
2492     }
2494     for (GSList *i = nodepaths; i; i = i->next) {
2495         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2496         // different nodepaths will give us one undo event per nodepath
2497         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2499         // if the entire nodepath is removed, delete the selected object.
2500         if (nodepath->subpaths == NULL ||
2501             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2502             //at least 2
2503             sp_nodepath_get_node_count(nodepath) < 2) {
2504             SPDocument *document = sp_desktop_document (nodepath->desktop);
2505             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2506             //delete this nodepath's object, not the entire selection! (though at this time, this
2507             //does not matter)
2508             sp_selection_delete(nodepath->desktop);
2509             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2510                               _("Delete nodes"));
2511         } else {
2512             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2513             sp_nodepath_update_statusbar(nodepath);
2514         }
2515     }
2517     g_slist_free (nodepaths);
2520 /**
2521  * Delete one or more selected nodes.
2522  */
2523 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2525     if (!nodepath) return;
2526     if (!nodepath->selected) return;
2528     /** \todo fixme: do it the right way */
2529     while (nodepath->selected) {
2530        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2531         sp_nodepath_node_destroy(node);
2532     }
2535     //clean up the nodepath (such as for trivial subpaths)
2536     sp_nodepath_cleanup(nodepath);
2538     sp_nodepath_update_handles(nodepath);
2540     // if the entire nodepath is removed, delete the selected object.
2541     if (nodepath->subpaths == NULL ||
2542         sp_nodepath_get_node_count(nodepath) < 2) {
2543         SPDocument *document = sp_desktop_document (nodepath->desktop);
2544         sp_selection_delete(nodepath->desktop);
2545         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2546                           _("Delete nodes"));
2547         return;
2548     }
2550     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2552     sp_nodepath_update_statusbar(nodepath);
2555 /**
2556  * Delete one or more segments between two selected nodes.
2557  * This is the code for 'split'.
2558  */
2559 void
2560 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2562    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2563    Inkscape::NodePath::Node *curr, *next;     //Iterators
2565     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2567     if (g_list_length(nodepath->selected) != 2) {
2568         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2569                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2570         return;
2571     }
2573     //Selected nodes, not inclusive
2574    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2575    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2577     if ( ( a==b)                       ||  //same node
2578          (a->subpath  != b->subpath )  ||  //not the same path
2579          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2580          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2581     {
2582         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2583                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2584         return;
2585     }
2587     //###########################################
2588     //# BEGIN EDITS
2589     //###########################################
2590     //##################################
2591     //# CLOSED PATH
2592     //##################################
2593     if (a->subpath->closed) {
2596         gboolean reversed = FALSE;
2598         //Since we can go in a circle, we need to find the shorter distance.
2599         //  a->b or b->a
2600         start = end = NULL;
2601         int distance    = 0;
2602         int minDistance = 0;
2603         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2604             if (curr==b) {
2605                 //printf("a to b:%d\n", distance);
2606                 start = a;//go from a to b
2607                 end   = b;
2608                 minDistance = distance;
2609                 //printf("A to B :\n");
2610                 break;
2611             }
2612             distance++;
2613         }
2615         //try again, the other direction
2616         distance = 0;
2617         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2618             if (curr==a) {
2619                 //printf("b to a:%d\n", distance);
2620                 if (distance < minDistance) {
2621                     start    = b;  //we go from b to a
2622                     end      = a;
2623                     reversed = TRUE;
2624                     //printf("B to A\n");
2625                 }
2626                 break;
2627             }
2628             distance++;
2629         }
2632         //Copy everything from 'end' to 'start' to a new subpath
2633        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2634         for (curr=end ; curr ; curr=curr->n.other) {
2635             NRPathcode code = (NRPathcode) curr->code;
2636             if (curr == end)
2637                 code = NR_MOVETO;
2638             sp_nodepath_node_new(t, NULL,
2639                                  (Inkscape::NodePath::NodeType)curr->type, code,
2640                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2641             if (curr == start)
2642                 break;
2643         }
2644         sp_nodepath_subpath_destroy(a->subpath);
2647     }
2651     //##################################
2652     //# OPEN PATH
2653     //##################################
2654     else {
2656         //We need to get the direction of the list between A and B
2657         //Can we walk from a to b?
2658         start = end = NULL;
2659         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2660             if (curr==b) {
2661                 start = a;  //did it!  we go from a to b
2662                 end   = b;
2663                 //printf("A to B\n");
2664                 break;
2665             }
2666         }
2667         if (!start) {//didn't work?  let's try the other direction
2668             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2669                 if (curr==a) {
2670                     start = b;  //did it!  we go from b to a
2671                     end   = a;
2672                     //printf("B to A\n");
2673                     break;
2674                 }
2675             }
2676         }
2677         if (!start) {
2678             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2679                                                      _("Cannot find path between nodes."));
2680             return;
2681         }
2685         //Copy everything after 'end' to a new subpath
2686        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2687         for (curr=end ; curr ; curr=curr->n.other) {
2688             NRPathcode code = (NRPathcode) curr->code;
2689             if (curr == end)
2690                 code = NR_MOVETO;
2691             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2692                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2693         }
2695         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2696         for (curr = start->n.other ; curr  ; curr=next) {
2697             next = curr->n.other;
2698             sp_nodepath_node_destroy(curr);
2699         }
2701     }
2702     //###########################################
2703     //# END EDITS
2704     //###########################################
2706     //clean up the nodepath (such as for trivial subpaths)
2707     sp_nodepath_cleanup(nodepath);
2709     sp_nodepath_update_handles(nodepath);
2711     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2713     sp_nodepath_update_statusbar(nodepath);
2716 /**
2717  * Call sp_nodepath_set_line() for all selected segments.
2718  */
2719 void
2720 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2722     if (nodepath == NULL) return;
2724     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2725        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2726         g_assert(n->selected);
2727         if (n->p.other && n->p.other->selected) {
2728             sp_nodepath_set_line_type(n, code);
2729         }
2730     }
2732     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2735 /**
2736  * Call sp_nodepath_convert_node_type() for all selected nodes.
2737  */
2738 void
2739 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2741     if (nodepath == NULL) return;
2743     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2745     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2746         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2747     }
2749     sp_nodepath_update_repr(nodepath, _("Change node type"));
2752 /**
2753  * Change select status of node, update its own and neighbour handles.
2754  */
2755 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2757     node->selected = selected;
2759     if (selected) {
2760         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2761         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2762         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2763         sp_knot_update_ctrl(node->knot);
2764     } else {
2765         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2766         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2767         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2768         sp_knot_update_ctrl(node->knot);
2769     }
2771     sp_node_update_handles(node);
2772     if (node->n.other) sp_node_update_handles(node->n.other);
2773     if (node->p.other) sp_node_update_handles(node->p.other);
2776 /**
2777 \brief Select a node
2778 \param node     The node to select
2779 \param incremental   If true, add to selection, otherwise deselect others
2780 \param override   If true, always select this node, otherwise toggle selected status
2781 */
2782 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2784     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2786     if (incremental) {
2787         if (override) {
2788             if (!g_list_find(nodepath->selected, node)) {
2789                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2790             }
2791             sp_node_set_selected(node, TRUE);
2792         } else { // toggle
2793             if (node->selected) {
2794                 g_assert(g_list_find(nodepath->selected, node));
2795                 nodepath->selected = g_list_remove(nodepath->selected, node);
2796             } else {
2797                 g_assert(!g_list_find(nodepath->selected, node));
2798                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2799             }
2800             sp_node_set_selected(node, !node->selected);
2801         }
2802     } else {
2803         sp_nodepath_deselect(nodepath);
2804         nodepath->selected = g_list_prepend(nodepath->selected, node);
2805         sp_node_set_selected(node, TRUE);
2806     }
2808     sp_nodepath_update_statusbar(nodepath);
2812 /**
2813 \brief Deselect all nodes in the nodepath
2814 */
2815 void
2816 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2818     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2820     while (nodepath->selected) {
2821         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2822         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2823     }
2824     sp_nodepath_update_statusbar(nodepath);
2827 /**
2828 \brief Select or invert selection of all nodes in the nodepath
2829 */
2830 void
2831 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2833     if (!nodepath) return;
2835     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2836        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2837         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2838            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2839            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2840         }
2841     }
2844 /**
2845  * If nothing selected, does the same as sp_nodepath_select_all();
2846  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2847  * (i.e., similar to "select all in layer", with the "selected" subpaths
2848  * being treated as "layers" in the path).
2849  */
2850 void
2851 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2853     if (!nodepath) return;
2855     if (g_list_length (nodepath->selected) == 0) {
2856         sp_nodepath_select_all (nodepath, invert);
2857         return;
2858     }
2860     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2861     GSList *subpaths = NULL;
2863     for (GList *l = copy; l != NULL; l = l->next) {
2864         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2865         Inkscape::NodePath::SubPath *subpath = n->subpath;
2866         if (!g_slist_find (subpaths, subpath))
2867             subpaths = g_slist_prepend (subpaths, subpath);
2868     }
2870     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2871         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2872         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2873             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2874             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2875         }
2876     }
2878     g_slist_free (subpaths);
2879     g_list_free (copy);
2882 /**
2883  * \brief Select the node after the last selected; if none is selected,
2884  * select the first within path.
2885  */
2886 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2888     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2890    Inkscape::NodePath::Node *last = NULL;
2891     if (nodepath->selected) {
2892         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2893            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2894             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2895             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2896                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2897                 if (node->selected) {
2898                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2899                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2900                             if (spl->next) { // there's a next subpath
2901                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2902                                 last = subpath_next->first;
2903                             } else if (spl->prev) { // there's a previous subpath
2904                                 last = NULL; // to be set later to the first node of first subpath
2905                             } else {
2906                                 last = node->n.other;
2907                             }
2908                         } else {
2909                             last = node->n.other;
2910                         }
2911                     } else {
2912                         if (node->n.other) {
2913                             last = node->n.other;
2914                         } else {
2915                             if (spl->next) { // there's a next subpath
2916                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2917                                 last = subpath_next->first;
2918                             } else if (spl->prev) { // there's a previous subpath
2919                                 last = NULL; // to be set later to the first node of first subpath
2920                             } else {
2921                                 last = (Inkscape::NodePath::Node *) subpath->first;
2922                             }
2923                         }
2924                     }
2925                 }
2926             }
2927         }
2928         sp_nodepath_deselect(nodepath);
2929     }
2931     if (last) { // there's at least one more node after selected
2932         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2933     } else { // no more nodes, select the first one in first subpath
2934        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2935         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2936     }
2939 /**
2940  * \brief Select the node before the first selected; if none is selected,
2941  * select the last within path
2942  */
2943 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2945     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2947    Inkscape::NodePath::Node *last = NULL;
2948     if (nodepath->selected) {
2949         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2950            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2951             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2952                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2953                 if (node->selected) {
2954                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2955                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2956                             if (spl->prev) { // there's a prev subpath
2957                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2958                                 last = subpath_prev->last;
2959                             } else if (spl->next) { // there's a next subpath
2960                                 last = NULL; // to be set later to the last node of last subpath
2961                             } else {
2962                                 last = node->p.other;
2963                             }
2964                         } else {
2965                             last = node->p.other;
2966                         }
2967                     } else {
2968                         if (node->p.other) {
2969                             last = node->p.other;
2970                         } else {
2971                             if (spl->prev) { // there's a prev subpath
2972                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2973                                 last = subpath_prev->last;
2974                             } else if (spl->next) { // there's a next subpath
2975                                 last = NULL; // to be set later to the last node of last subpath
2976                             } else {
2977                                 last = (Inkscape::NodePath::Node *) subpath->last;
2978                             }
2979                         }
2980                     }
2981                 }
2982             }
2983         }
2984         sp_nodepath_deselect(nodepath);
2985     }
2987     if (last) { // there's at least one more node before selected
2988         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2989     } else { // no more nodes, select the last one in last subpath
2990         GList *spl = g_list_last(nodepath->subpaths);
2991        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2992         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2993     }
2996 /**
2997  * \brief Select all nodes that are within the rectangle.
2998  */
2999 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
3001     if (!incremental) {
3002         sp_nodepath_deselect(nodepath);
3003     }
3005     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3006        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3007         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3008            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3010             if (b.contains(node->pos)) {
3011                 sp_nodepath_node_select(node, TRUE, TRUE);
3012             }
3013         }
3014     }
3018 void
3019 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3021     g_assert (n);
3022     g_assert (nodepath);
3023     g_assert (n->subpath->nodepath == nodepath);
3025     if (g_list_length (nodepath->selected) == 0) {
3026         if (grow > 0) {
3027             sp_nodepath_node_select(n, TRUE, TRUE);
3028         }
3029         return;
3030     }
3032     if (g_list_length (nodepath->selected) == 1) {
3033         if (grow < 0) {
3034             sp_nodepath_deselect (nodepath);
3035             return;
3036         }
3037     }
3039         double n_sel_range = 0, p_sel_range = 0;
3040             Inkscape::NodePath::Node *farthest_n_node = n;
3041             Inkscape::NodePath::Node *farthest_p_node = n;
3043         // Calculate ranges
3044         {
3045             double n_range = 0, p_range = 0;
3046             bool n_going = true, p_going = true;
3047             Inkscape::NodePath::Node *n_node = n;
3048             Inkscape::NodePath::Node *p_node = n;
3049             do {
3050                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3051                 if (n_node && n_going)
3052                     n_node = n_node->n.other;
3053                 if (n_node == NULL) {
3054                     n_going = false;
3055                 } else {
3056                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3057                     if (n_node->selected) {
3058                         n_sel_range = n_range;
3059                         farthest_n_node = n_node;
3060                     }
3061                     if (n_node == p_node) {
3062                         n_going = false;
3063                         p_going = false;
3064                     }
3065                 }
3066                 if (p_node && p_going)
3067                     p_node = p_node->p.other;
3068                 if (p_node == NULL) {
3069                     p_going = false;
3070                 } else {
3071                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3072                     if (p_node->selected) {
3073                         p_sel_range = p_range;
3074                         farthest_p_node = p_node;
3075                     }
3076                     if (p_node == n_node) {
3077                         n_going = false;
3078                         p_going = false;
3079                     }
3080                 }
3081             } while (n_going || p_going);
3082         }
3084     if (grow > 0) {
3085         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3086                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3087         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3088                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3089         }
3090     } else {
3091         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3092                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3093         } else if (farthest_p_node && farthest_p_node->selected) {
3094                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3095         }
3096     }
3099 void
3100 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3102     g_assert (n);
3103     g_assert (nodepath);
3104     g_assert (n->subpath->nodepath == nodepath);
3106     if (g_list_length (nodepath->selected) == 0) {
3107         if (grow > 0) {
3108             sp_nodepath_node_select(n, TRUE, TRUE);
3109         }
3110         return;
3111     }
3113     if (g_list_length (nodepath->selected) == 1) {
3114         if (grow < 0) {
3115             sp_nodepath_deselect (nodepath);
3116             return;
3117         }
3118     }
3120     Inkscape::NodePath::Node *farthest_selected = NULL;
3121     double farthest_dist = 0;
3123     Inkscape::NodePath::Node *closest_unselected = NULL;
3124     double closest_dist = NR_HUGE;
3126     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3127        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3128         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3129            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3130            if (node == n)
3131                continue;
3132            if (node->selected) {
3133                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3134                    farthest_dist = NR::L2(node->pos - n->pos);
3135                    farthest_selected = node;
3136                }
3137            } else {
3138                if (NR::L2(node->pos - n->pos) < closest_dist) {
3139                    closest_dist = NR::L2(node->pos - n->pos);
3140                    closest_unselected = node;
3141                }
3142            }
3143         }
3144     }
3146     if (grow > 0) {
3147         if (closest_unselected) {
3148             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3149         }
3150     } else {
3151         if (farthest_selected) {
3152             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3153         }
3154     }
3158 /**
3159 \brief  Saves all nodes' and handles' current positions in their origin members
3160 */
3161 void
3162 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3164     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3165        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3166         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3167            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3168            n->origin = n->pos;
3169            n->p.origin = n->p.pos;
3170            n->n.origin = n->n.pos;
3171         }
3172     }
3175 /**
3176 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3177 */
3178 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3180     GList *r = NULL;
3181     if (nodepath->selected) {
3182         guint i = 0;
3183         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3184             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3185             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3186                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3187                 i++;
3188                 if (node->selected) {
3189                     r = g_list_append(r, GINT_TO_POINTER(i));
3190                 }
3191             }
3192         }
3193     }
3194     return r;
3197 /**
3198 \brief  Restores selection by selecting nodes whose positions are in the list
3199 */
3200 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3202     sp_nodepath_deselect(nodepath);
3204     guint i = 0;
3205     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3206        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3207         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3208            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3209             i++;
3210             if (g_list_find(r, GINT_TO_POINTER(i))) {
3211                 sp_nodepath_node_select(node, TRUE, TRUE);
3212             }
3213         }
3214     }
3218 /**
3219 \brief Adjusts handle according to node type and line code.
3220 */
3221 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3223     g_assert(node);
3225    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3226    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3228    // nothing to do if we are an end node
3229     if (me->other == NULL) return;
3230     if (other->other == NULL) return;
3232     // nothing to do if we are a cusp node
3233     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3235     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3236     NRPathcode mecode;
3237     if (which_adjust == 1) {
3238         mecode = (NRPathcode)me->other->code;
3239     } else {
3240         mecode = (NRPathcode)node->code;
3241     }
3242     if (mecode == NR_LINETO) return;
3244     if (sp_node_side_is_line(node, other)) {
3245         // other is a line, and we are either smooth or symm
3246        Inkscape::NodePath::Node *othernode = other->other;
3247         double len = NR::L2(me->pos - node->pos);
3248         NR::Point delta = node->pos - othernode->pos;
3249         double linelen = NR::L2(delta);
3250         if (linelen < 1e-18)
3251             return;
3252         me->pos = node->pos + (len / linelen)*delta;
3253         return;
3254     }
3256     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3257         // symmetrize 
3258         me->pos = 2 * node->pos - other->pos;
3259         return;
3260     } else {
3261         // smoothify
3262         double len = NR::L2(me->pos - node->pos);
3263         NR::Point delta = other->pos - node->pos;
3264         double otherlen = NR::L2(delta);
3265         if (otherlen < 1e-18) return;
3266         me->pos = node->pos - (len / otherlen) * delta;
3267     }
3270 /**
3271  \brief Adjusts both handles according to node type and line code
3272  */
3273 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3275     g_assert(node);
3277     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3279     /* we are either smooth or symm */
3281     if (node->p.other == NULL) return;
3282     if (node->n.other == NULL) return;
3284     if (sp_node_side_is_line(node, &node->p)) {
3285         sp_node_adjust_handle(node, 1);
3286         return;
3287     }
3289     if (sp_node_side_is_line(node, &node->n)) {
3290         sp_node_adjust_handle(node, -1);
3291         return;
3292     }
3294     /* both are curves */
3295     NR::Point const delta( node->n.pos - node->p.pos );
3297     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3298         node->p.pos = node->pos - delta / 2;
3299         node->n.pos = node->pos + delta / 2;
3300         return;
3301     }
3303     /* We are smooth */
3304     double plen = NR::L2(node->p.pos - node->pos);
3305     if (plen < 1e-18) return;
3306     double nlen = NR::L2(node->n.pos - node->pos);
3307     if (nlen < 1e-18) return;
3308     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3309     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3312 /**
3313  * Node event callback.
3314  */
3315 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3317     gboolean ret = FALSE;
3318     switch (event->type) {
3319         case GDK_ENTER_NOTIFY:
3320             Inkscape::NodePath::Path::active_node = n;
3321             break;
3322         case GDK_LEAVE_NOTIFY:
3323             Inkscape::NodePath::Path::active_node = NULL;
3324             break;
3325         case GDK_SCROLL:
3326             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3327                 switch (event->scroll.direction) {
3328                     case GDK_SCROLL_UP:
3329                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3330                         break;
3331                     case GDK_SCROLL_DOWN:
3332                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3333                         break;
3334                     default:
3335                         break;
3336                 }
3337                 ret = TRUE;
3338             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3339                 switch (event->scroll.direction) {
3340                     case GDK_SCROLL_UP:
3341                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3342                         break;
3343                     case GDK_SCROLL_DOWN:
3344                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3345                         break;
3346                     default:
3347                         break;
3348                 }
3349                 ret = TRUE;
3350             }
3351             break;
3352         case GDK_KEY_PRESS:
3353             switch (get_group0_keyval (&event->key)) {
3354                 case GDK_space:
3355                     if (event->key.state & GDK_BUTTON1_MASK) {
3356                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3357                         stamp_repr(nodepath);
3358                         ret = TRUE;
3359                     }
3360                     break;
3361                 case GDK_Page_Up:
3362                     if (event->key.state & GDK_CONTROL_MASK) {
3363                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3364                     } else {
3365                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3366                     }
3367                     break;
3368                 case GDK_Page_Down:
3369                     if (event->key.state & GDK_CONTROL_MASK) {
3370                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3371                     } else {
3372                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3373                     }
3374                     break;
3375                 default:
3376                     break;
3377             }
3378             break;
3379         default:
3380             break;
3381     }
3383     return ret;
3386 /**
3387  * Handle keypress on node; directly called.
3388  */
3389 gboolean node_key(GdkEvent *event)
3391     Inkscape::NodePath::Path *np;
3393     // there is no way to verify nodes so set active_node to nil when deleting!!
3394     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3396     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3397         gint ret = FALSE;
3398         switch (get_group0_keyval (&event->key)) {
3399             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3400             case GDK_BackSpace:
3401                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3402                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3403                 sp_nodepath_update_repr(np, _("Delete node"));
3404                 Inkscape::NodePath::Path::active_node = NULL;
3405                 ret = TRUE;
3406                 break;
3407             case GDK_c:
3408                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3409                 ret = TRUE;
3410                 break;
3411             case GDK_s:
3412                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3413                 ret = TRUE;
3414                 break;
3415             case GDK_y:
3416                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3417                 ret = TRUE;
3418                 break;
3419             case GDK_b:
3420                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3421                 ret = TRUE;
3422                 break;
3423         }
3424         return ret;
3425     }
3426     return FALSE;
3429 /**
3430  * Mouseclick on node callback.
3431  */
3432 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3434    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3436     if (state & GDK_CONTROL_MASK) {
3437         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3439         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3440             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3441                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3442             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3443                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3444             } else {
3445                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3446             }
3447             sp_nodepath_update_repr(nodepath, _("Change node type"));
3448             sp_nodepath_update_statusbar(nodepath);
3450         } else { //ctrl+alt+click: delete node
3451             GList *node_to_delete = NULL;
3452             node_to_delete = g_list_append(node_to_delete, n);
3453             sp_node_delete_preserve(node_to_delete);
3454         }
3456     } else {
3457         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3458     }
3461 /**
3462  * Mouse grabbed node callback.
3463  */
3464 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3466    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3468     if (!n->selected) {
3469         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3470     }
3472     n->is_dragging = true;
3473     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3475     sp_nodepath_remember_origins (n->subpath->nodepath);
3478 /**
3479  * Mouse ungrabbed node callback.
3480  */
3481 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3483    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3485    n->dragging_out = NULL;
3486    n->is_dragging = false;
3487    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3489    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3492 /**
3493  * The point on a line, given by its angle, closest to the given point.
3494  * \param p  A point.
3495  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3496  * \param closest  Pointer to the point struct where the result is stored.
3497  * \todo FIXME: use dot product perhaps?
3498  */
3499 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3501     if (a == HUGE_VAL) { // vertical
3502         *closest = NR::Point(0, (*p)[NR::Y]);
3503     } else {
3504         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3505         (*closest)[NR::Y] = a * (*closest)[NR::X];
3506     }
3509 /**
3510  * Distance from the point to a line given by its angle.
3511  * \param p  A point.
3512  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3513  */
3514 static double point_line_distance(NR::Point *p, double a)
3516     NR::Point c;
3517     point_line_closest(p, a, &c);
3518     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]));
3521 /**
3522  * Callback for node "request" signal.
3523  * \todo fixme: This goes to "moved" event? (lauris)
3524  */
3525 static gboolean
3526 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3528     double yn, xn, yp, xp;
3529     double an, ap, na, pa;
3530     double d_an, d_ap, d_na, d_pa;
3531     gboolean collinear = FALSE;
3532     NR::Point c;
3533     NR::Point pr;
3535     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3537     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3539     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3540     if ( (!n->subpath->nodepath->straight_path) &&
3541          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3542            || n->dragging_out ) )
3543     {
3544        NR::Point mouse = (*p);
3546        if (!n->dragging_out) {
3547            // This is the first drag-out event; find out which handle to drag out
3548            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3549            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3551            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3552                return FALSE;
3554            Inkscape::NodePath::NodeSide *opposite;
3555            if (appr_p > appr_n) { // closer to p
3556                n->dragging_out = &n->p;
3557                opposite = &n->n;
3558                n->code = NR_CURVETO;
3559            } else if (appr_p < appr_n) { // closer to n
3560                n->dragging_out = &n->n;
3561                opposite = &n->p;
3562                n->n.other->code = NR_CURVETO;
3563            } else { // p and n nodes are the same
3564                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3565                    n->dragging_out = &n->p;
3566                    opposite = &n->n;
3567                    n->code = NR_CURVETO;
3568                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3569                    n->dragging_out = &n->n;
3570                    opposite = &n->p;
3571                    n->n.other->code = NR_CURVETO;
3572                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3573                    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);
3574                    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);
3575                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3576                        n->dragging_out = &n->n;
3577                        opposite = &n->p;
3578                        n->n.other->code = NR_CURVETO;
3579                    } else { // closer to other's n handle
3580                        n->dragging_out = &n->p;
3581                        opposite = &n->n;
3582                        n->code = NR_CURVETO;
3583                    }
3584                }
3585            }
3587            // if there's another handle, make sure the one we drag out starts parallel to it
3588            if (opposite->pos != n->pos) {
3589                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3590            }
3592            // knots might not be created yet!
3593            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3594            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3595        }
3597        // pass this on to the handle-moved callback
3598        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3599        sp_node_update_handles(n);
3600        return TRUE;
3601    }
3603     if (state & GDK_CONTROL_MASK) { // constrained motion
3605         // calculate relative distances of handles
3606         // n handle:
3607         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3608         xn = n->n.pos[NR::X] - n->pos[NR::X];
3609         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3610         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3611             if (n->n.other) { // if there is the next point
3612                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3613                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3614                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3615             }
3616         }
3617         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3618         if (yn < 0) { xn = -xn; yn = -yn; }
3620         // p handle:
3621         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3622         xp = n->p.pos[NR::X] - n->pos[NR::X];
3623         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3624         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3625             if (n->p.other) {
3626                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3627                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3628                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3629             }
3630         }
3631         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3632         if (yp < 0) { xp = -xp; yp = -yp; }
3634         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3635             // sliding on handles, only if at least one of the handles is non-vertical
3636             // (otherwise it's the same as ctrl+drag anyway)
3638             // calculate angles of the handles
3639             if (xn == 0) {
3640                 if (yn == 0) { // no handle, consider it the continuation of the other one
3641                     an = 0;
3642                     collinear = TRUE;
3643                 }
3644                 else an = 0; // vertical; set the angle to horizontal
3645             } else an = yn/xn;
3647             if (xp == 0) {
3648                 if (yp == 0) { // no handle, consider it the continuation of the other one
3649                     ap = an;
3650                 }
3651                 else ap = 0; // vertical; set the angle to horizontal
3652             } else  ap = yp/xp;
3654             if (collinear) an = ap;
3656             // angles of the perpendiculars; HUGE_VAL means vertical
3657             if (an == 0) na = HUGE_VAL; else na = -1/an;
3658             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3660             // mouse point relative to the node's original pos
3661             pr = (*p) - n->origin;
3663             // distances to the four lines (two handles and two perpendiculars)
3664             d_an = point_line_distance(&pr, an);
3665             d_na = point_line_distance(&pr, na);
3666             d_ap = point_line_distance(&pr, ap);
3667             d_pa = point_line_distance(&pr, pa);
3669             // find out which line is the closest, save its closest point in c
3670             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3671                 point_line_closest(&pr, an, &c);
3672             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3673                 point_line_closest(&pr, ap, &c);
3674             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3675                 point_line_closest(&pr, na, &c);
3676             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3677                 point_line_closest(&pr, pa, &c);
3678             }
3680             // move the node to the closest point
3681             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3682                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3683                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3684                                             true);
3686         } else {  // constraining to hor/vert
3688             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3689                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3690                                                 (*p)[NR::X] - n->pos[NR::X], 
3691                                                 n->origin[NR::Y] - n->pos[NR::Y],
3692                                                 true, 
3693                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3694             } else { // snap to vert
3695                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3696                                                 n->origin[NR::X] - n->pos[NR::X],
3697                                                 (*p)[NR::Y] - n->pos[NR::Y],
3698                                                 true,
3699                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3700             }
3701         }
3702     } else { // move freely
3703         if (n->is_dragging) {
3704             if (state & GDK_MOD1_MASK) { // sculpt
3705                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3706             } else {
3707                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3708                                             (*p)[NR::X] - n->pos[NR::X],
3709                                             (*p)[NR::Y] - n->pos[NR::Y],
3710                                             (state & GDK_SHIFT_MASK) == 0);
3711             }
3712         }
3713     }
3715     n->subpath->nodepath->desktop->scroll_to_point(p);
3717     return TRUE;
3720 /**
3721  * Node handle clicked callback.
3722  */
3723 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3725    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3727     if (state & GDK_CONTROL_MASK) { // "delete" handle
3728         if (n->p.knot == knot) {
3729             n->p.pos = n->pos;
3730         } else if (n->n.knot == knot) {
3731             n->n.pos = n->pos;
3732         }
3733         sp_node_update_handles(n);
3734         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3735         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3736         sp_nodepath_update_statusbar(nodepath);
3738     } else { // just select or add to selection, depending in Shift
3739         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3740     }
3743 /**
3744  * Node handle grabbed callback.
3745  */
3746 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3748    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3750     if (!n->selected) {
3751         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3752     }
3754     // remember the origin point of the handle
3755     if (n->p.knot == knot) {
3756         n->p.origin_radial = n->p.pos - n->pos;
3757     } else if (n->n.knot == knot) {
3758         n->n.origin_radial = n->n.pos - n->pos;
3759     } else {
3760         g_assert_not_reached();
3761     }
3763     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3766 /**
3767  * Node handle ungrabbed callback.
3768  */
3769 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3771    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3773     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3774     if (n->p.knot == knot) {
3775         n->p.origin_radial.a = 0;
3776         sp_knot_set_position(knot, n->p.pos, state);
3777     } else if (n->n.knot == knot) {
3778         n->n.origin_radial.a = 0;
3779         sp_knot_set_position(knot, n->n.pos, state);
3780     } else {
3781         g_assert_not_reached();
3782     }
3784     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3787 /**
3788  * Node handle "request" signal callback.
3789  */
3790 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3792     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3794     Inkscape::NodePath::NodeSide *me, *opposite;
3795     gint which;
3796     if (n->p.knot == knot) {
3797         me = &n->p;
3798         opposite = &n->n;
3799         which = -1;
3800     } else if (n->n.knot == knot) {
3801         me = &n->n;
3802         opposite = &n->p;
3803         which = 1;
3804     } else {
3805         me = opposite = NULL;
3806         which = 0;
3807         g_assert_not_reached();
3808     }
3810     SPDesktop *desktop = n->subpath->nodepath->desktop;
3811     SnapManager &m = desktop->namedview->snap_manager;
3812     m.setup(desktop, true, n->subpath->nodepath->item);
3813     Inkscape::SnappedPoint s;
3814     
3815     if ((state & GDK_SHIFT_MASK) != 0) {
3816         // We will not try to snap when the shift-key is pressed
3817         // so remove the old snap indicator and don't wait for it to time-out  
3818         desktop->snapindicator->remove_snappoint();     
3819     }
3821     Inkscape::NodePath::Node *othernode = opposite->other;
3822     if (othernode) {
3823         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3824             /* We are smooth node adjacent with line */
3825             NR::Point const delta = *p - n->pos;
3826             NR::Coord const len = NR::L2(delta);
3827             Inkscape::NodePath::Node *othernode = opposite->other;
3828             NR::Point const ndelta = n->pos - othernode->pos;
3829             NR::Coord const linelen = NR::L2(ndelta);
3830             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3831                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3832                 (*p) = n->pos + (scal / linelen) * ndelta;
3833             }
3834             if ((state & GDK_SHIFT_MASK) == 0) {
3835                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p), Inkscape::Snapper::ConstraintLine(*p, ndelta));
3836             }
3837         } else {
3838                 if ((state & GDK_SHIFT_MASK) == 0) {
3839                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3840                 }
3841         }
3842     } else {
3843         if ((state & GDK_SHIFT_MASK) == 0) {
3844                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3845         }
3846     }
3847     
3848     Geom::Point pt2g = *p;
3849     s.getPoint(pt2g);
3850     *p = pt2g;
3851     
3852     sp_node_adjust_handle(n, -which);
3854     return FALSE;
3857 /**
3858  * Node handle moved callback.
3859  */
3860 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3862    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3864    Inkscape::NodePath::NodeSide *me;
3865    Inkscape::NodePath::NodeSide *other;
3866     if (n->p.knot == knot) {
3867         me = &n->p;
3868         other = &n->n;
3869     } else if (n->n.knot == knot) {
3870         me = &n->n;
3871         other = &n->p;
3872     } else {
3873         me = NULL;
3874         other = NULL;
3875         g_assert_not_reached();
3876     }
3878     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3879     Radial rme(me->pos - n->pos);
3880     Radial rother(other->pos - n->pos);
3881     Radial rnew(*p - n->pos);
3883     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3884         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3885         /* 0 interpreted as "no snapping". */
3887         // 1. Snap to the closest PI/snaps angle, starting from zero.
3888         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3890         // 2. Snap to the original angle, its opposite and perpendiculars
3891         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3892             /* The closest PI/2 angle, starting from original angle */
3893             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3895             // Snap to the closest.
3896             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3897                        ? a_snapped
3898                        : a_ortho );
3899         }
3901         // 3. Snap to the angle of the opposite line, if any
3902         Inkscape::NodePath::Node *othernode = other->other;
3903         if (othernode) {
3904             NR::Point other_to_snap(0,0);
3905             if (sp_node_side_is_line(n, other)) {
3906                 other_to_snap = othernode->pos - n->pos;
3907             } else {
3908                 other_to_snap = other->pos - n->pos;
3909             }
3910             if (NR::L2(other_to_snap) > 1e-3) {
3911                 Radial rother_to_snap(other_to_snap);
3912                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3913                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3915                 // Snap to the closest.
3916                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3917                        ? a_snapped
3918                        : a_oppo );
3919             }
3920         }
3922         rnew.a = a_snapped;
3923     }
3925     if (state & GDK_MOD1_MASK) {
3926         // lock handle length
3927         rnew.r = me->origin_radial.r;
3928     }
3930     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3931         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3932         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3933         rother.a += rnew.a - rme.a;
3934         other->pos = NR::Point(rother) + n->pos;
3935         if (other->knot) {
3936             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3937             sp_knot_moveto(other->knot, other->pos);
3938         }
3939     }
3941     me->pos = NR::Point(rnew) + n->pos;
3942     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3944     // move knot, but without emitting the signal:
3945     // we cannot emit a "moved" signal because we're now processing it
3946     sp_knot_moveto(me->knot, me->pos);
3948     update_object(n->subpath->nodepath);
3950     /* status text */
3951     SPDesktop *desktop = n->subpath->nodepath->desktop;
3952     if (!desktop) return;
3953     SPEventContext *ec = desktop->event_context;
3954     if (!ec) return;
3956     Inkscape::MessageContext *mc = get_message_context(ec);
3958     if (!mc) return;
3960     double degrees = 180 / M_PI * rnew.a;
3961     if (degrees > 180) degrees -= 360;
3962     if (degrees < -180) degrees += 360;
3963     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3964         degrees = angle_to_compass (degrees);
3966     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3968     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3969          _("<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);
3971     g_string_free(length, TRUE);
3974 /**
3975  * Node handle event callback.
3976  */
3977 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3979     gboolean ret = FALSE;
3980     switch (event->type) {
3981         case GDK_KEY_PRESS:
3982             switch (get_group0_keyval (&event->key)) {
3983                 case GDK_space:
3984                     if (event->key.state & GDK_BUTTON1_MASK) {
3985                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3986                         stamp_repr(nodepath);
3987                         ret = TRUE;
3988                     }
3989                     break;
3990                 default:
3991                     break;
3992             }
3993             break;
3994         case GDK_ENTER_NOTIFY:
3995             // we use an experimentally determined threshold that seems to work fine
3996             if (NR::L2(n->pos - knot->pos) < 0.75)
3997                 Inkscape::NodePath::Path::active_node = n;
3998             break;
3999         case GDK_LEAVE_NOTIFY:
4000             // we use an experimentally determined threshold that seems to work fine
4001             if (NR::L2(n->pos - knot->pos) < 0.75)
4002                 Inkscape::NodePath::Path::active_node = NULL;
4003             break;
4004         default:
4005             break;
4006     }
4008     return ret;
4011 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4012                                  Radial &rme, Radial &rother, gboolean const both)
4014     rme.a += angle;
4015     if ( both
4016          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4017          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4018     {
4019         rother.a += angle;
4020     }
4023 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4024                                         Radial &rme, Radial &rother, gboolean const both)
4026     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4028     gdouble r;
4029     if ( both
4030          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4031          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4032     {
4033         r = MAX(rme.r, rother.r);
4034     } else {
4035         r = rme.r;
4036     }
4038     gdouble const weird_angle = atan2(norm_angle, r);
4039 /* Bulia says norm_angle is just the visible distance that the
4040  * object's end must travel on the screen.  Left as 'angle' for want of
4041  * a better name.*/
4043     rme.a += weird_angle;
4044     if ( both
4045          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4046          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4047     {
4048         rother.a += weird_angle;
4049     }
4052 /**
4053  * Rotate one node.
4054  */
4055 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4057     Inkscape::NodePath::NodeSide *me, *other;
4058     bool both = false;
4060     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4061     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4063     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4064         me = &(n->p);
4065         other = &(n->n);
4066     } else if (!n->p.other) {
4067         me = &(n->n);
4068         other = &(n->p);
4069     } else {
4070         if (which > 0) { // right handle
4071             if (xn > xp) {
4072                 me = &(n->n);
4073                 other = &(n->p);
4074             } else {
4075                 me = &(n->p);
4076                 other = &(n->n);
4077             }
4078         } else if (which < 0){ // left handle
4079             if (xn <= xp) {
4080                 me = &(n->n);
4081                 other = &(n->p);
4082             } else {
4083                 me = &(n->p);
4084                 other = &(n->n);
4085             }
4086         } else { // both handles
4087             me = &(n->n);
4088             other = &(n->p);
4089             both = true;
4090         }
4091     }
4093     Radial rme(me->pos - n->pos);
4094     Radial rother(other->pos - n->pos);
4096     if (screen) {
4097         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4098     } else {
4099         node_rotate_one_internal (*n, angle, rme, rother, both);
4100     }
4102     me->pos = n->pos + NR::Point(rme);
4104     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4105         other->pos =  n->pos + NR::Point(rother);
4106     }
4108     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4109     // so here we just move all the knots without emitting move signals, for speed
4110     sp_node_update_handles(n, false);
4113 /**
4114  * Rotate selected nodes.
4115  */
4116 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4118     if (!nodepath || !nodepath->selected) return;
4120     if (g_list_length(nodepath->selected) == 1) {
4121        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4122         node_rotate_one (n, angle, which, screen);
4123     } else {
4124        // rotate as an object:
4126         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4127         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4128         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4129             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4130             box.expandTo (n->pos); // contain all selected nodes
4131         }
4133         gdouble rot;
4134         if (screen) {
4135             gdouble const zoom = nodepath->desktop->current_zoom();
4136             gdouble const zmove = angle / zoom;
4137             gdouble const r = NR::L2(box.max() - box.midpoint());
4138             rot = atan2(zmove, r);
4139         } else {
4140             rot = angle;
4141         }
4143         NR::Point rot_center;
4144         if (Inkscape::NodePath::Path::active_node == NULL)
4145             rot_center = box.midpoint();
4146         else
4147             rot_center = Inkscape::NodePath::Path::active_node->pos;
4149         NR::Matrix t =
4150             NR::Matrix (NR::translate(-rot_center)) *
4151             NR::Matrix (NR::rotate(rot)) *
4152             NR::Matrix (NR::translate(rot_center));
4154         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4155             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4156             n->pos *= t;
4157             n->n.pos *= t;
4158             n->p.pos *= t;
4159             sp_node_update_handles(n, false);
4160         }
4161     }
4163     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4166 /**
4167  * Scale one node.
4168  */
4169 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4171     bool both = false;
4172     Inkscape::NodePath::NodeSide *me, *other;
4174     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4175     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4177     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4178         me = &(n->p);
4179         other = &(n->n);
4180         n->code = NR_CURVETO;
4181     } else if (!n->p.other) {
4182         me = &(n->n);
4183         other = &(n->p);
4184         if (n->n.other)
4185             n->n.other->code = NR_CURVETO;
4186     } else {
4187         if (which > 0) { // right handle
4188             if (xn > xp) {
4189                 me = &(n->n);
4190                 other = &(n->p);
4191                 if (n->n.other)
4192                     n->n.other->code = NR_CURVETO;
4193             } else {
4194                 me = &(n->p);
4195                 other = &(n->n);
4196                 n->code = NR_CURVETO;
4197             }
4198         } else if (which < 0){ // left handle
4199             if (xn <= xp) {
4200                 me = &(n->n);
4201                 other = &(n->p);
4202                 if (n->n.other)
4203                     n->n.other->code = NR_CURVETO;
4204             } else {
4205                 me = &(n->p);
4206                 other = &(n->n);
4207                 n->code = NR_CURVETO;
4208             }
4209         } else { // both handles
4210             me = &(n->n);
4211             other = &(n->p);
4212             both = true;
4213             n->code = NR_CURVETO;
4214             if (n->n.other)
4215                 n->n.other->code = NR_CURVETO;
4216         }
4217     }
4219     Radial rme(me->pos - n->pos);
4220     Radial rother(other->pos - n->pos);
4222     rme.r += grow;
4223     if (rme.r < 0) rme.r = 0;
4224     if (rme.a == HUGE_VAL) {
4225         if (me->other) { // if direction is unknown, initialize it towards the next node
4226             Radial rme_next(me->other->pos - n->pos);
4227             rme.a = rme_next.a;
4228         } else { // if there's no next, initialize to 0
4229             rme.a = 0;
4230         }
4231     }
4232     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4233         rother.r += grow;
4234         if (rother.r < 0) rother.r = 0;
4235         if (rother.a == HUGE_VAL) {
4236             rother.a = rme.a + M_PI;
4237         }
4238     }
4240     me->pos = n->pos + NR::Point(rme);
4242     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4243         other->pos = n->pos + NR::Point(rother);
4244     }
4246     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4247     // so here we just move all the knots without emitting move signals, for speed
4248     sp_node_update_handles(n, false);
4251 /**
4252  * Scale selected nodes.
4253  */
4254 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4256     if (!nodepath || !nodepath->selected) return;
4258     if (g_list_length(nodepath->selected) == 1) {
4259         // scale handles of the single selected node
4260         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4261         node_scale_one (n, grow, which);
4262     } else {
4263         // scale nodes as an "object":
4265         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4266         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4267         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4268             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4269             box.expandTo (n->pos); // contain all selected nodes
4270         }
4272         double scale = (box.maxExtent() + grow)/box.maxExtent();
4274         NR::Point scale_center;
4275         if (Inkscape::NodePath::Path::active_node == NULL)
4276             scale_center = box.midpoint();
4277         else
4278             scale_center = Inkscape::NodePath::Path::active_node->pos;
4280         NR::Matrix t =
4281             NR::Matrix (NR::translate(-scale_center)) *
4282             NR::Matrix (NR::scale(scale, scale)) *
4283             NR::Matrix (NR::translate(scale_center));
4285         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4286             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4287             n->pos *= t;
4288             n->n.pos *= t;
4289             n->p.pos *= t;
4290             sp_node_update_handles(n, false);
4291         }
4292     }
4294     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4297 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4299     if (!nodepath) return;
4300     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4303 /**
4304  * Flip selected nodes horizontally/vertically.
4305  */
4306 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, boost::optional<NR::Point> center)
4308     if (!nodepath || !nodepath->selected) return;
4310     if (g_list_length(nodepath->selected) == 1 && !center) {
4311         // flip handles of the single selected node
4312         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4313         double temp = n->p.pos[axis];
4314         n->p.pos[axis] = n->n.pos[axis];
4315         n->n.pos[axis] = temp;
4316         sp_node_update_handles(n, false);
4317     } else {
4318         // scale nodes as an "object":
4320         Geom::Rect box = sp_node_selected_bbox (nodepath);
4321         if (!center) {
4322             center = box.midpoint();
4323         }
4324         NR::Matrix t =
4325             NR::Matrix (NR::translate(- *center)) *
4326             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4327             NR::Matrix (NR::translate(*center));
4329         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4330             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4331             n->pos *= t;
4332             n->n.pos *= t;
4333             n->p.pos *= t;
4334             sp_node_update_handles(n, false);
4335         }
4336     }
4338     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4341 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4343     g_assert (nodepath->selected);
4345     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4346     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4347     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4348         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4349         box.expandTo (n->pos); // contain all selected nodes
4350     }
4351     return box;
4354 //-----------------------------------------------
4355 /**
4356  * Return new subpath under given nodepath.
4357  */
4358 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4360     g_assert(nodepath);
4361     g_assert(nodepath->desktop);
4363    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4365     s->nodepath = nodepath;
4366     s->closed = FALSE;
4367     s->nodes = NULL;
4368     s->first = NULL;
4369     s->last = NULL;
4371     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4372     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4373     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4375     return s;
4378 /**
4379  * Destroy nodes in subpath, then subpath itself.
4380  */
4381 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4383     g_assert(subpath);
4384     g_assert(subpath->nodepath);
4385     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4387     while (subpath->nodes) {
4388         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4389     }
4391     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4393     g_free(subpath);
4396 /**
4397  * Link head to tail in subpath.
4398  */
4399 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4401     g_assert(!sp->closed);
4402     g_assert(sp->last != sp->first);
4403     g_assert(sp->first->code == NR_MOVETO);
4405     sp->closed = TRUE;
4407     //Link the head to the tail
4408     sp->first->p.other = sp->last;
4409     sp->last->n.other  = sp->first;
4410     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4411     sp->first          = sp->last;
4413     //Remove the extra end node
4414     sp_nodepath_node_destroy(sp->last->n.other);
4417 /**
4418  * Open closed (loopy) subpath at node.
4419  */
4420 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4422     g_assert(sp->closed);
4423     g_assert(n->subpath == sp);
4424     g_assert(sp->first == sp->last);
4426     /* We create new startpoint, current node will become last one */
4428    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4429                                                 &n->pos, &n->pos, &n->n.pos);
4432     sp->closed        = FALSE;
4434     //Unlink to make a head and tail
4435     sp->first         = new_path;
4436     sp->last          = n;
4437     n->n.other        = NULL;
4438     new_path->p.other = NULL;
4441 /**
4442  * Return new node in subpath with given properties.
4443  * \param pos Position of node.
4444  * \param ppos Handle position in previous direction
4445  * \param npos Handle position in previous direction
4446  */
4447 Inkscape::NodePath::Node *
4448 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)
4450     g_assert(sp);
4451     g_assert(sp->nodepath);
4452     g_assert(sp->nodepath->desktop);
4454     if (nodechunk == NULL)
4455         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4457     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4459     n->subpath  = sp;
4461     if (type != Inkscape::NodePath::NODE_NONE) {
4462         // use the type from sodipodi:nodetypes
4463         n->type = type;
4464     } else {
4465         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4466             // points are (almost) collinear
4467             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4468                 // endnode, or a node with a retracted handle
4469                 n->type = Inkscape::NodePath::NODE_CUSP;
4470             } else {
4471                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4472             }
4473         } else {
4474             n->type = Inkscape::NodePath::NODE_CUSP;
4475         }
4476     }
4478     n->code     = code;
4479     n->selected = FALSE;
4480     n->pos      = *pos;
4481     n->p.pos    = *ppos;
4482     n->n.pos    = *npos;
4484     n->dragging_out = NULL;
4486     Inkscape::NodePath::Node *prev;
4487     if (next) {
4488         //g_assert(g_list_find(sp->nodes, next));
4489         prev = next->p.other;
4490     } else {
4491         prev = sp->last;
4492     }
4494     if (prev)
4495         prev->n.other = n;
4496     else
4497         sp->first = n;
4499     if (next)
4500         next->p.other = n;
4501     else
4502         sp->last = n;
4504     n->p.other = prev;
4505     n->n.other = next;
4507     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"));
4508     sp_knot_set_position(n->knot, *pos, 0);
4510     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4511     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4512     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4513     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4514     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4515     sp_knot_update_ctrl(n->knot);
4517     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4518     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4519     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4520     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4521     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4522     sp_knot_show(n->knot);
4524     // We only create handle knots and lines on demand
4525     n->p.knot = NULL;
4526     n->p.line = NULL;
4527     n->n.knot = NULL;
4528     n->n.line = NULL;
4530     sp->nodes = g_list_prepend(sp->nodes, n);
4532     return n;
4535 /**
4536  * Destroy node and its knots, link neighbors in subpath.
4537  */
4538 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4540     g_assert(node);
4541     g_assert(node->subpath);
4542     g_assert(SP_IS_KNOT(node->knot));
4544    Inkscape::NodePath::SubPath *sp = node->subpath;
4546     if (node->selected) { // first, deselect
4547         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4548         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4549     }
4551     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4553     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4554     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4555     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4556     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4557     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4558     g_object_unref(G_OBJECT(node->knot));
4560     if (node->p.knot) {
4561         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4562         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4563         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4564         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4565         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4566         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4567         g_object_unref(G_OBJECT(node->p.knot));
4568         node->p.knot = NULL;
4569     }
4571     if (node->n.knot) {
4572         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4573         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4574         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4575         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4576         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4577         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4578         g_object_unref(G_OBJECT(node->n.knot));
4579         node->n.knot = NULL;
4580     }
4582     if (node->p.line)
4583         gtk_object_destroy(GTK_OBJECT(node->p.line));
4584     if (node->n.line)
4585         gtk_object_destroy(GTK_OBJECT(node->n.line));
4587     if (sp->nodes) { // there are others nodes on the subpath
4588         if (sp->closed) {
4589             if (sp->first == node) {
4590                 g_assert(sp->last == node);
4591                 sp->first = node->n.other;
4592                 sp->last = sp->first;
4593             }
4594             node->p.other->n.other = node->n.other;
4595             node->n.other->p.other = node->p.other;
4596         } else {
4597             if (sp->first == node) {
4598                 sp->first = node->n.other;
4599                 sp->first->code = NR_MOVETO;
4600             }
4601             if (sp->last == node) sp->last = node->p.other;
4602             if (node->p.other) node->p.other->n.other = node->n.other;
4603             if (node->n.other) node->n.other->p.other = node->p.other;
4604         }
4605     } else { // this was the last node on subpath
4606         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4607     }
4609     g_mem_chunk_free(nodechunk, node);
4612 /**
4613  * Returns one of the node's two sides.
4614  * \param which Indicates which side.
4615  * \return Pointer to previous node side if which==-1, next if which==1.
4616  */
4617 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4619     g_assert(node);
4620     Inkscape::NodePath::NodeSide * result = 0;
4621     switch (which) {
4622         case -1:
4623             result = &node->p;
4624             break;
4625         case 1:
4626             result = &node->n;
4627             break;
4628         default:
4629             g_assert_not_reached();
4630     }
4632     return result;
4635 /**
4636  * Return the other side of the node, given one of its sides.
4637  */
4638 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4640     g_assert(node);
4641     Inkscape::NodePath::NodeSide *result = 0;
4643     if (me == &node->p) {
4644         result = &node->n;
4645     } else if (me == &node->n) {
4646         result = &node->p;
4647     } else {
4648         g_assert_not_reached();
4649     }
4651     return result;
4654 /**
4655  * Return NRPathcode on the given side of the node.
4656  */
4657 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4659     g_assert(node);
4661     NRPathcode result = NR_END;
4662     if (me == &node->p) {
4663         if (node->p.other) {
4664             result = (NRPathcode)node->code;
4665         } else {
4666             result = NR_MOVETO;
4667         }
4668     } else if (me == &node->n) {
4669         if (node->n.other) {
4670             result = (NRPathcode)node->n.other->code;
4671         } else {
4672             result = NR_MOVETO;
4673         }
4674     } else {
4675         g_assert_not_reached();
4676     }
4678     return result;
4681 /**
4682  * Return node with the given index
4683  */
4684 Inkscape::NodePath::Node *
4685 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4687     Inkscape::NodePath::Node *e = NULL;
4689     if (!nodepath) {
4690         return e;
4691     }
4693     //find segment
4694     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4696         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4697         int n = g_list_length(sp->nodes);
4698         if (sp->closed) {
4699             n++;
4700         }
4702         //if the piece belongs to this subpath grab it
4703         //otherwise move onto the next subpath
4704         if (index < n) {
4705             e = sp->first;
4706             for (int i = 0; i < index; ++i) {
4707                 e = e->n.other;
4708             }
4709             break;
4710         } else {
4711             if (sp->closed) {
4712                 index -= (n+1);
4713             } else {
4714                 index -= n;
4715             }
4716         }
4717     }
4719     return e;
4722 /**
4723  * Returns plain text meaning of node type.
4724  */
4725 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4727     unsigned retracted = 0;
4728     bool endnode = false;
4730     for (int which = -1; which <= 1; which += 2) {
4731         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4732         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4733             retracted ++;
4734         if (!side->other)
4735             endnode = true;
4736     }
4738     if (retracted == 0) {
4739         if (endnode) {
4740                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4741                 return _("end node");
4742         } else {
4743             switch (node->type) {
4744                 case Inkscape::NodePath::NODE_CUSP:
4745                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4746                     return _("cusp");
4747                 case Inkscape::NodePath::NODE_SMOOTH:
4748                     // TRANSLATORS: "smooth" is an adjective here
4749                     return _("smooth");
4750                 case Inkscape::NodePath::NODE_SYMM:
4751                     return _("symmetric");
4752             }
4753         }
4754     } else if (retracted == 1) {
4755         if (endnode) {
4756             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4757             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4758         } else {
4759             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4760         }
4761     } else {
4762         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4763     }
4765     return NULL;
4768 /**
4769  * Handles content of statusbar as long as node tool is active.
4770  */
4771 void
4772 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4774     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");
4775     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4777     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4778     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4779     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4780     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4782     SPDesktop *desktop = NULL;
4783     if (nodepath) {
4784         desktop = nodepath->desktop;
4785     } else {
4786         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4787     }
4789     SPEventContext *ec = desktop->event_context;
4790     if (!ec) return;
4792     Inkscape::MessageContext *mc = get_message_context(ec);
4793     if (!mc) return;
4795     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4797     if (selected_nodes == 0) {
4798         Inkscape::Selection *sel = desktop->selection;
4799         if (!sel || sel->isEmpty()) {
4800             mc->setF(Inkscape::NORMAL_MESSAGE,
4801                      _("Select a single object to edit its nodes or handles."));
4802         } else {
4803             if (nodepath) {
4804             mc->setF(Inkscape::NORMAL_MESSAGE,
4805                      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.",
4806                               "<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.",
4807                               total_nodes),
4808                      total_nodes);
4809             } else {
4810                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4811                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4812                 } else {
4813                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4814                 }
4815             }
4816         }
4817     } else if (nodepath && selected_nodes == 1) {
4818         mc->setF(Inkscape::NORMAL_MESSAGE,
4819                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4820                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4821                           total_nodes),
4822                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4823     } else {
4824         if (selected_subpaths > 1) {
4825             mc->setF(Inkscape::NORMAL_MESSAGE,
4826                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4827                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4828                               total_nodes),
4829                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4830         } else {
4831             mc->setF(Inkscape::NORMAL_MESSAGE,
4832                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4833                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4834                               total_nodes),
4835                      selected_nodes, total_nodes, when_selected);
4836         }
4837     }
4840 /*
4841  * returns a *copy* of the curve of that object.
4842  */
4843 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4844     if (!object)
4845         return NULL;
4847     SPCurve *curve = NULL;
4848     if (SP_IS_PATH(object)) {
4849         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4850         curve = curve_new->copy();
4851     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4852         const gchar *svgd = object->repr->attribute(key);
4853         if (svgd) {
4854             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4855             SPCurve *curve_new = new SPCurve(pv);
4856             if (curve_new) {
4857                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4858             }
4859         }
4860     }
4862     return curve;
4865 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4866     if (!np || !np->object || !curve)
4867         return;
4869     if (SP_IS_PATH(np->object)) {
4870         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4871             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4872         } else {
4873             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4874         }
4875     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4876         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4877         if (pathparam) {
4878             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4879             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4880         }
4881     }
4884 /**
4885 SPCanvasItem *
4886 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4887     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4889 **/
4891 /**
4892 SPCanvasItem *
4893 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4894     SPCurve *flash_curve = curve->copy();
4895     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4896     flash_curve->transform(i2d);
4897     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4898     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4899     // unless we also flash the nodes...
4900     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4901     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4902     sp_canvas_item_show(canvasitem);
4903     flash_curve->unref();
4904     return canvasitem;
4907 SPCanvasItem *
4908 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4909     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4910                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4912 **/
4914 SPCanvasItem *
4915 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4916     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4917     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4918     flash_curve->transform(i2d);
4919     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4920     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4921     // unless we also flash the nodes...
4922     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4923     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4924     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4925     sp_canvas_item_show(canvasitem);
4926     flash_curve->unref();
4927     return canvasitem;
4930 // TODO: Merge this with sp_nodepath_make_helper_item()!
4931 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4932     np->show_helperpath = show;
4934     if (show) {
4935         SPCurve *helper_curve = np->curve->copy();
4936         helper_curve->transform(np->i2d);
4937         if (!np->helper_path) {
4938             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4940             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4941             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);
4942             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4943             sp_canvas_item_move_to_z(np->helper_path, 0);
4944             sp_canvas_item_show(np->helper_path);
4945         } else {
4946             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4947         }
4948         helper_curve->unref();
4949     } else {
4950         if (np->helper_path) {
4951             GtkObject *temp = np->helper_path;
4952             np->helper_path = NULL;
4953             gtk_object_destroy(temp);
4954         }
4955     }
4958 /* sp_nodepath_make_straight_path:
4959  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4960  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4961  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4962  */
4963 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4964     np->straight_path = true;
4965     np->show_handles = false;
4966     g_message("add code to make the path straight.");
4967     // do sp_nodepath_convert_node_type on all nodes?
4968     // coding tip: search for this text : "Make selected segments lines"
4971 /*
4972   Local Variables:
4973   mode:c++
4974   c-file-style:"stroustrup"
4975   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4976   indent-tabs-mode:nil
4977   fill-column:99
4978   End:
4979 */
4980 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :